Skip to content

Commit 1ec8d39

Browse files
committed
Add DefaultCacheStore and DefaultCrypto Services
1 parent dd1d91f commit 1ec8d39

File tree

6 files changed

+173
-6
lines changed

6 files changed

+173
-6
lines changed

packages/javascript/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@
5454
},
5555
"dependencies": {
5656
"@asgardeo/i18n": "workspace:*",
57-
"tslib": "2.8.1"
57+
"tslib": "2.8.1",
58+
"base64url": "^3.0.1",
59+
"jose": "^5.2.0"
5860
},
5961
"publishConfig": {
6062
"access": "public"

packages/javascript/src/AsgardeoJavaScriptClient.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import {Storage} from './models/store';
3333
import {TokenExchangeRequestConfig, TokenResponse} from './models/token';
3434
import {User, UserProfile} from './models/user';
3535
import initializeEmbeddedSignInFlow from './api/initializeEmbeddedSignInFlow';
36+
import {DefaultCacheStore} from './DefaultCacheStore';
37+
import {DefaultCrypto} from './DefaultCrypto';
3638

3739
interface AgentConfig {
3840
agentID: string;
@@ -61,8 +63,8 @@ class AsgardeoJavaScriptClient<T = Config> implements AsgardeoClient<T> {
6163
void: void;
6264

6365
constructor(config?: AuthClientConfig<T>, cacheStore?: Storage, cryptoUtils?: Crypto) {
64-
this.cacheStore = cacheStore;
65-
this.cryptoUtils = cryptoUtils;
66+
this.cacheStore = cacheStore ?? new DefaultCacheStore();
67+
this.cryptoUtils = cryptoUtils ?? new DefaultCrypto();
6668
this.auth = new AsgardeoAuthClient();
6769
this.auth.initialize(config, this.cacheStore, this.cryptoUtils);
6870
this.storageManager = this.auth.getStorageManager();
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
export class DefaultCacheStore implements Storage {
20+
21+
private cache: Map<string, string>;
22+
23+
constructor() {
24+
this.cache = new Map<string, string>();
25+
}
26+
27+
public get length(): number {
28+
return this.cache.size;
29+
}
30+
31+
public getItem(key: string): string | null {
32+
return this.cache.get(key) ?? null;
33+
}
34+
35+
public setItem(key: string, value: string): void {
36+
this.cache.set(key, value);
37+
}
38+
39+
public removeItem(key: string): void {
40+
this.cache.delete(key);
41+
}
42+
43+
public clear(): void {
44+
this.cache.clear();
45+
}
46+
47+
public key(index: number): string | null {
48+
const keys = Array.from(this.cache.keys());
49+
return keys[index] ?? null;
50+
}
51+
52+
public async setData(key: string, value: string): Promise<void> {
53+
this.cache.set(key, value);
54+
}
55+
56+
public async getData(key: string): Promise<string> {
57+
return this.cache.get(key) ?? "{}";
58+
}
59+
60+
public async removeData(key: string): Promise<void> {
61+
this.cache.delete(key);
62+
}
63+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Copyright (c) 2026, WSO2 LLC. (https://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
import * as jose from "jose";
20+
import { JWKInterface } from "./models/crypto";
21+
22+
interface CryptoInterface<T> {
23+
base64URLEncode(value: T): string;
24+
base64URLDecode(value: string): string;
25+
hashSha256(data: string): Promise<Uint8Array>; // Changed to Promise to match implementation
26+
generateRandomBytes(length: number): Uint8Array;
27+
verifyJwt(
28+
idToken: string,
29+
jwk: Partial<any>,
30+
algorithms: string[],
31+
clientId: string,
32+
issuer: string,
33+
subject: string,
34+
clockTolerance?: number,
35+
): Promise<boolean>;
36+
}
37+
38+
export class DefaultCrypto implements CryptoInterface<Uint8Array | string> {
39+
40+
constructor() {}
41+
42+
/**
43+
* Cross-platform Base64URL encoding using 'jose' utilities
44+
*/
45+
public base64URLEncode(value: Uint8Array | string): string {
46+
const uint8Array = typeof value === "string"
47+
? new TextEncoder().encode(value)
48+
: value;
49+
50+
return jose.base64url.encode(uint8Array);
51+
}
52+
53+
/**
54+
* Cross-platform Base64URL decoding
55+
*/
56+
public base64URLDecode(value: string): string {
57+
const decodedArray = jose.base64url.decode(value);
58+
return new TextDecoder().decode(decodedArray);
59+
}
60+
61+
public async hashSha256(data: string): Promise<Uint8Array> {
62+
const encoder = new TextEncoder();
63+
const dataBuffer = encoder.encode(data);
64+
65+
// Using native web crypto (available in modern Node and Browsers)
66+
const hashBuffer = await crypto.subtle.digest("SHA-256", dataBuffer);
67+
return new Uint8Array(hashBuffer);
68+
}
69+
70+
public generateRandomBytes(length: number): Uint8Array {
71+
// globalThis.crypto works in Browsers and Node.js 19+
72+
const array = new Uint8Array(length);
73+
crypto.getRandomValues(array);
74+
return array;
75+
}
76+
77+
public async verifyJwt(
78+
idToken: string,
79+
jwk: Partial<JWKInterface>,
80+
algorithms: string[],
81+
clientId: string,
82+
issuer: string,
83+
subject: string,
84+
clockTolerance?: number,
85+
): Promise<boolean> {
86+
87+
const key = await jose.importJWK(jwk as any);
88+
89+
await jose.jwtVerify(idToken, key, {
90+
algorithms,
91+
audience: clientId,
92+
issuer,
93+
subject,
94+
clockTolerance,
95+
});
96+
97+
return true;
98+
}
99+
}

packages/javascript/src/IsomorphicCrypto.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,9 @@ export class IsomorphicCrypto<T = any> {
4343
*
4444
* @returns - code challenge.
4545
*/
46-
public getCodeChallenge(verifier: string): string {
47-
return this.cryptoUtils.base64URLEncode(this.cryptoUtils.hashSha256(verifier));
46+
public async getCodeChallenge(verifier: string): Promise<string> {
47+
const hashed = await this.cryptoUtils.hashSha256(verifier);
48+
return this.cryptoUtils.base64URLEncode(hashed);
4849
}
4950

5051
/**

packages/javascript/src/__legacy__/client.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export class AsgardeoAuthClient<T> {
242242

243243
if (configData.enablePKCE) {
244244
codeVerifier = this.cryptoHelper?.getCodeVerifier();
245-
codeChallenge = this.cryptoHelper?.getCodeChallenge(codeVerifier);
245+
codeChallenge = await this.cryptoHelper?.getCodeChallenge(codeVerifier);
246246
await this.storageManager.setTemporaryDataParameter(pkceKey, codeVerifier, userId);
247247
}
248248

0 commit comments

Comments
 (0)