@logto/browser 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/index.d.ts +5 -3
- package/lib/index.js +49 -27
- package/package.json +8 -3
package/lib/index.d.ts
CHANGED
|
@@ -31,23 +31,25 @@ export default class LogtoClient {
|
|
|
31
31
|
protected logtoStorageKey: string;
|
|
32
32
|
protected requester: Requester;
|
|
33
33
|
protected accessTokenMap: Map<string, AccessToken>;
|
|
34
|
+
private readonly getAccessTokenPromiseMap;
|
|
34
35
|
private _refreshToken;
|
|
35
36
|
private _idToken;
|
|
36
37
|
constructor(logtoConfig: LogtoConfig, requester?: <T>(input: RequestInfo, init?: RequestInit | undefined) => Promise<T>);
|
|
37
38
|
get isAuthenticated(): boolean;
|
|
38
39
|
protected get signInSession(): Nullable<LogtoSignInSessionItem>;
|
|
39
40
|
protected set signInSession(logtoSignInSessionItem: Nullable<LogtoSignInSessionItem>);
|
|
40
|
-
|
|
41
|
+
get refreshToken(): Nullable<string>;
|
|
41
42
|
private set refreshToken(value);
|
|
42
|
-
|
|
43
|
+
get idToken(): Nullable<string>;
|
|
43
44
|
private set idToken(value);
|
|
44
|
-
getAccessToken(resource?: string): Promise<
|
|
45
|
+
getAccessToken(resource?: string): Promise<string>;
|
|
45
46
|
getIdTokenClaims(): IdTokenClaims;
|
|
46
47
|
fetchUserInfo(): Promise<UserInfoResponse>;
|
|
47
48
|
signIn(redirectUri: string): Promise<void>;
|
|
48
49
|
isSignInRedirected(url: string): boolean;
|
|
49
50
|
handleSignInCallback(callbackUri: string): Promise<void>;
|
|
50
51
|
signOut(postLogoutRedirectUri?: string): Promise<void>;
|
|
52
|
+
private getAccessTokenByRefreshToken;
|
|
51
53
|
private getOidcConfig;
|
|
52
54
|
private verifyIdToken;
|
|
53
55
|
private saveCodeToken;
|
package/lib/index.js
CHANGED
|
@@ -25,6 +25,7 @@ exports.LogtoSignInSessionItemSchema = (0, superstruct_1.type)({
|
|
|
25
25
|
class LogtoClient {
|
|
26
26
|
constructor(logtoConfig, requester = (0, js_1.createRequester)()) {
|
|
27
27
|
this.accessTokenMap = new Map();
|
|
28
|
+
this.getAccessTokenPromiseMap = new Map();
|
|
28
29
|
this.logtoConfig = logtoConfig;
|
|
29
30
|
this.logtoStorageKey = (0, utils_1.buildLogtoKey)(logtoConfig.clientId);
|
|
30
31
|
this.requester = requester;
|
|
@@ -89,33 +90,24 @@ class LogtoClient {
|
|
|
89
90
|
if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
|
|
90
91
|
return accessToken.token;
|
|
91
92
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
if (idToken) {
|
|
111
|
-
await this.verifyIdToken(idToken);
|
|
112
|
-
this.idToken = idToken;
|
|
113
|
-
}
|
|
114
|
-
return accessToken;
|
|
115
|
-
}
|
|
116
|
-
catch (error) {
|
|
117
|
-
throw new errors_1.LogtoClientError('get_access_token_by_refresh_token_failed', error);
|
|
118
|
-
}
|
|
93
|
+
/**
|
|
94
|
+
* Token has expired, need to fetch a new one using refresh token.
|
|
95
|
+
* Reuse the cached promise if exists.
|
|
96
|
+
*/
|
|
97
|
+
const cachedPromise = this.getAccessTokenPromiseMap.get(accessTokenKey);
|
|
98
|
+
if (cachedPromise) {
|
|
99
|
+
return cachedPromise;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Create a new promise and cache in map to avoid race condition.
|
|
103
|
+
* Since we enable "refresh token rotation" by default,
|
|
104
|
+
* it will be problematic when calling multiple `getAccessToken()` closely.
|
|
105
|
+
*/
|
|
106
|
+
const promise = this.getAccessTokenByRefreshToken(resource);
|
|
107
|
+
this.getAccessTokenPromiseMap.set(accessTokenKey, promise);
|
|
108
|
+
const token = await promise;
|
|
109
|
+
this.getAccessTokenPromiseMap.delete(accessTokenKey);
|
|
110
|
+
return token;
|
|
119
111
|
}
|
|
120
112
|
getIdTokenClaims() {
|
|
121
113
|
if (!this.idToken) {
|
|
@@ -202,6 +194,36 @@ class LogtoClient {
|
|
|
202
194
|
this.idToken = null;
|
|
203
195
|
window.location.assign(url);
|
|
204
196
|
}
|
|
197
|
+
async getAccessTokenByRefreshToken(resource) {
|
|
198
|
+
const accessTokenKey = (0, utils_1.buildAccessTokenKey)(resource);
|
|
199
|
+
// Token expired, remove it from the map
|
|
200
|
+
if (this.accessTokenMap.has(accessTokenKey)) {
|
|
201
|
+
this.accessTokenMap.delete(accessTokenKey);
|
|
202
|
+
}
|
|
203
|
+
// Fetch new access token by refresh token
|
|
204
|
+
const { clientId } = this.logtoConfig;
|
|
205
|
+
if (!this.refreshToken) {
|
|
206
|
+
throw new errors_1.LogtoClientError('not_authenticated');
|
|
207
|
+
}
|
|
208
|
+
try {
|
|
209
|
+
const { tokenEndpoint } = await this.getOidcConfig();
|
|
210
|
+
const { accessToken, refreshToken, idToken, scope, expiresIn } = await (0, js_1.fetchTokenByRefreshToken)({ clientId, tokenEndpoint, refreshToken: this.refreshToken, resource }, this.requester);
|
|
211
|
+
this.accessTokenMap.set(accessTokenKey, {
|
|
212
|
+
token: accessToken,
|
|
213
|
+
scope,
|
|
214
|
+
expiresAt: Math.round(Date.now() / 1000) + expiresIn,
|
|
215
|
+
});
|
|
216
|
+
this.refreshToken = refreshToken;
|
|
217
|
+
if (idToken) {
|
|
218
|
+
await this.verifyIdToken(idToken);
|
|
219
|
+
this.idToken = idToken;
|
|
220
|
+
}
|
|
221
|
+
return accessToken;
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
throw new errors_1.LogtoClientError('get_access_token_by_refresh_token_failed', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
205
227
|
async getOidcConfig() {
|
|
206
228
|
if (!this.oidcConfig) {
|
|
207
229
|
const { endpoint } = this.logtoConfig;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"exports": "./lib/index.js",
|
|
6
6
|
"typings": "./lib/index.d.ts",
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
"lib"
|
|
9
9
|
],
|
|
10
10
|
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/logto-io/js.git",
|
|
14
|
+
"directory": "packages/browser"
|
|
15
|
+
},
|
|
11
16
|
"scripts": {
|
|
12
17
|
"dev:tsc": "tsc -p tsconfig.build.json -w --preserveWatchOutput",
|
|
13
18
|
"preinstall": "npx only-allow pnpm",
|
|
@@ -19,7 +24,7 @@
|
|
|
19
24
|
"prepack": "pnpm test && pnpm build"
|
|
20
25
|
},
|
|
21
26
|
"dependencies": {
|
|
22
|
-
"@logto/js": "^0.1.
|
|
27
|
+
"@logto/js": "^0.1.3",
|
|
23
28
|
"@silverhand/essentials": "^1.1.6",
|
|
24
29
|
"jose": "^4.5.0",
|
|
25
30
|
"lodash.get": "^4.4.2",
|
|
@@ -48,5 +53,5 @@
|
|
|
48
53
|
"publishConfig": {
|
|
49
54
|
"access": "public"
|
|
50
55
|
},
|
|
51
|
-
"gitHead": "
|
|
56
|
+
"gitHead": "2a59d35046744dd1284eb81804cf0ef9a35f41e6"
|
|
52
57
|
}
|