@logto/browser 0.1.3 → 0.1.7
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 +18 -9
- package/lib/index.js +34 -25
- package/package.json +8 -6
package/lib/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { IdTokenClaims,
|
|
1
|
+
import { IdTokenClaims, Requester, UserInfoResponse } from '@logto/js';
|
|
2
2
|
import { Nullable } from '@silverhand/essentials';
|
|
3
3
|
import { Infer } from 'superstruct';
|
|
4
4
|
export type { IdTokenClaims, UserInfoResponse } from '@logto/js';
|
|
5
5
|
export * from './errors';
|
|
6
6
|
export declare type LogtoConfig = {
|
|
7
7
|
endpoint: string;
|
|
8
|
-
|
|
8
|
+
appId: string;
|
|
9
9
|
scopes?: string[];
|
|
10
10
|
resources?: string[];
|
|
11
11
|
usingPersistStorage?: boolean;
|
|
@@ -26,13 +26,21 @@ export declare const LogtoSignInSessionItemSchema: import("superstruct").Struct<
|
|
|
26
26
|
}>;
|
|
27
27
|
export declare type LogtoSignInSessionItem = Infer<typeof LogtoSignInSessionItemSchema>;
|
|
28
28
|
export default class LogtoClient {
|
|
29
|
-
protected logtoConfig: LogtoConfig;
|
|
30
|
-
protected
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
protected readonly logtoConfig: LogtoConfig;
|
|
30
|
+
protected readonly getOidcConfig: () => Promise<import("@silverhand/essentials").KeysToCamelCase<{
|
|
31
|
+
authorization_endpoint: string;
|
|
32
|
+
token_endpoint: string;
|
|
33
|
+
userinfo_endpoint: string;
|
|
34
|
+
end_session_endpoint: string;
|
|
35
|
+
revocation_endpoint: string;
|
|
36
|
+
jwks_uri: string;
|
|
37
|
+
issuer: string;
|
|
38
|
+
}>>;
|
|
39
|
+
protected readonly getJwtVerifyGetKey: () => Promise<import("jose/dist/types/types").GetKeyFunction<import("jose").JWSHeaderParameters, import("jose").FlattenedJWSInput>>;
|
|
40
|
+
protected readonly logtoStorageKey: string;
|
|
41
|
+
protected readonly requester: Requester;
|
|
42
|
+
protected readonly accessTokenMap: Map<string, AccessToken>;
|
|
34
43
|
private readonly getAccessTokenPromiseMap;
|
|
35
|
-
private _refreshToken;
|
|
36
44
|
private _idToken;
|
|
37
45
|
constructor(logtoConfig: LogtoConfig, requester?: <T>(input: RequestInfo, init?: RequestInit | undefined) => Promise<T>);
|
|
38
46
|
get isAuthenticated(): boolean;
|
|
@@ -50,7 +58,8 @@ export default class LogtoClient {
|
|
|
50
58
|
handleSignInCallback(callbackUri: string): Promise<void>;
|
|
51
59
|
signOut(postLogoutRedirectUri?: string): Promise<void>;
|
|
52
60
|
private getAccessTokenByRefreshToken;
|
|
53
|
-
private
|
|
61
|
+
private _getOidcConfig;
|
|
62
|
+
private _getJwtVerifyGetKey;
|
|
54
63
|
private verifyIdToken;
|
|
55
64
|
private saveCodeToken;
|
|
56
65
|
}
|
package/lib/index.js
CHANGED
|
@@ -9,10 +9,14 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
|
|
|
9
9
|
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
10
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
11
|
};
|
|
12
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
13
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
14
|
+
};
|
|
12
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
16
|
exports.LogtoSignInSessionItemSchema = void 0;
|
|
14
17
|
const js_1 = require("@logto/js");
|
|
15
18
|
const jose_1 = require("jose");
|
|
19
|
+
const lodash_once_1 = __importDefault(require("lodash.once"));
|
|
16
20
|
const superstruct_1 = require("superstruct");
|
|
17
21
|
const errors_1 = require("./errors");
|
|
18
22
|
const utils_1 = require("./utils");
|
|
@@ -24,12 +28,13 @@ exports.LogtoSignInSessionItemSchema = (0, superstruct_1.type)({
|
|
|
24
28
|
});
|
|
25
29
|
class LogtoClient {
|
|
26
30
|
constructor(logtoConfig, requester = (0, js_1.createRequester)()) {
|
|
31
|
+
this.getOidcConfig = (0, lodash_once_1.default)(this._getOidcConfig);
|
|
32
|
+
this.getJwtVerifyGetKey = (0, lodash_once_1.default)(this._getJwtVerifyGetKey);
|
|
27
33
|
this.accessTokenMap = new Map();
|
|
28
34
|
this.getAccessTokenPromiseMap = new Map();
|
|
29
35
|
this.logtoConfig = logtoConfig;
|
|
30
|
-
this.logtoStorageKey = (0, utils_1.buildLogtoKey)(logtoConfig.
|
|
36
|
+
this.logtoStorageKey = (0, utils_1.buildLogtoKey)(logtoConfig.appId);
|
|
31
37
|
this.requester = requester;
|
|
32
|
-
this._refreshToken = localStorage.getItem((0, utils_1.buildRefreshTokenKey)(this.logtoStorageKey));
|
|
33
38
|
this._idToken = localStorage.getItem((0, utils_1.buildIdTokenKey)(this.logtoStorageKey));
|
|
34
39
|
}
|
|
35
40
|
get isAuthenticated() {
|
|
@@ -58,10 +63,9 @@ class LogtoClient {
|
|
|
58
63
|
sessionStorage.setItem(this.logtoStorageKey, jsonItem);
|
|
59
64
|
}
|
|
60
65
|
get refreshToken() {
|
|
61
|
-
return this.
|
|
66
|
+
return localStorage.getItem((0, utils_1.buildRefreshTokenKey)(this.logtoStorageKey));
|
|
62
67
|
}
|
|
63
68
|
set refreshToken(refreshToken) {
|
|
64
|
-
this._refreshToken = refreshToken;
|
|
65
69
|
const refreshTokenKey = (0, utils_1.buildRefreshTokenKey)(this.logtoStorageKey);
|
|
66
70
|
if (!refreshToken) {
|
|
67
71
|
localStorage.removeItem(refreshTokenKey);
|
|
@@ -81,6 +85,7 @@ class LogtoClient {
|
|
|
81
85
|
}
|
|
82
86
|
localStorage.setItem(idTokenKey, idToken);
|
|
83
87
|
}
|
|
88
|
+
// eslint-disable-next-line complexity
|
|
84
89
|
async getAccessToken(resource) {
|
|
85
90
|
if (!this.idToken) {
|
|
86
91
|
throw new errors_1.LogtoClientError('not_authenticated');
|
|
@@ -90,8 +95,12 @@ class LogtoClient {
|
|
|
90
95
|
if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
|
|
91
96
|
return accessToken.token;
|
|
92
97
|
}
|
|
98
|
+
// Since the access token has expired, delete it from the map.
|
|
99
|
+
if (accessToken) {
|
|
100
|
+
this.accessTokenMap.delete(accessTokenKey);
|
|
101
|
+
}
|
|
93
102
|
/**
|
|
94
|
-
*
|
|
103
|
+
* Need to fetch a new access token using refresh token.
|
|
95
104
|
* Reuse the cached promise if exists.
|
|
96
105
|
*/
|
|
97
106
|
const cachedPromise = this.getAccessTokenPromiseMap.get(accessTokenKey);
|
|
@@ -124,7 +133,7 @@ class LogtoClient {
|
|
|
124
133
|
return (0, js_1.fetchUserInfo)(userinfoEndpoint, accessToken, this.requester);
|
|
125
134
|
}
|
|
126
135
|
async signIn(redirectUri) {
|
|
127
|
-
const { clientId, resources, scopes: customScopes } = this.logtoConfig;
|
|
136
|
+
const { appId: clientId, resources, scopes: customScopes } = this.logtoConfig;
|
|
128
137
|
const { authorizationEndpoint } = await this.getOidcConfig();
|
|
129
138
|
const codeVerifier = (0, js_1.generateCodeVerifier)();
|
|
130
139
|
const codeChallenge = await (0, js_1.generateCodeChallenge)(codeVerifier);
|
|
@@ -140,6 +149,8 @@ class LogtoClient {
|
|
|
140
149
|
resources,
|
|
141
150
|
});
|
|
142
151
|
this.signInSession = { redirectUri, codeVerifier, state };
|
|
152
|
+
this.refreshToken = null;
|
|
153
|
+
this.idToken = null;
|
|
143
154
|
window.location.assign(signInUri);
|
|
144
155
|
}
|
|
145
156
|
isSignInRedirected(url) {
|
|
@@ -158,7 +169,7 @@ class LogtoClient {
|
|
|
158
169
|
}
|
|
159
170
|
const { redirectUri, state, codeVerifier } = signInSession;
|
|
160
171
|
const code = (0, js_1.verifyAndParseCodeFromCallbackUri)(callbackUri, redirectUri, state);
|
|
161
|
-
const { clientId } = logtoConfig;
|
|
172
|
+
const { appId: clientId } = logtoConfig;
|
|
162
173
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
163
174
|
const codeTokenResponse = await (0, js_1.fetchTokenByAuthorizationCode)({
|
|
164
175
|
clientId,
|
|
@@ -169,12 +180,13 @@ class LogtoClient {
|
|
|
169
180
|
}, requester);
|
|
170
181
|
await this.verifyIdToken(codeTokenResponse.idToken);
|
|
171
182
|
this.saveCodeToken(codeTokenResponse);
|
|
183
|
+
this.signInSession = null;
|
|
172
184
|
}
|
|
173
185
|
async signOut(postLogoutRedirectUri) {
|
|
174
186
|
if (!this.idToken) {
|
|
175
187
|
throw new errors_1.LogtoClientError('not_authenticated');
|
|
176
188
|
}
|
|
177
|
-
const { clientId } = this.logtoConfig;
|
|
189
|
+
const { appId: clientId } = this.logtoConfig;
|
|
178
190
|
const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();
|
|
179
191
|
if (this.refreshToken) {
|
|
180
192
|
try {
|
|
@@ -195,17 +207,12 @@ class LogtoClient {
|
|
|
195
207
|
window.location.assign(url);
|
|
196
208
|
}
|
|
197
209
|
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
210
|
if (!this.refreshToken) {
|
|
206
211
|
throw new errors_1.LogtoClientError('not_authenticated');
|
|
207
212
|
}
|
|
208
213
|
try {
|
|
214
|
+
const accessTokenKey = (0, utils_1.buildAccessTokenKey)(resource);
|
|
215
|
+
const { appId: clientId } = this.logtoConfig;
|
|
209
216
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
210
217
|
const { accessToken, refreshToken, idToken, scope, expiresIn } = await (0, js_1.fetchTokenByRefreshToken)({ clientId, tokenEndpoint, refreshToken: this.refreshToken, resource }, this.requester);
|
|
211
218
|
this.accessTokenMap.set(accessTokenKey, {
|
|
@@ -224,19 +231,21 @@ class LogtoClient {
|
|
|
224
231
|
throw new errors_1.LogtoClientError('get_access_token_by_refresh_token_failed', error);
|
|
225
232
|
}
|
|
226
233
|
}
|
|
227
|
-
async
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
+
async _getOidcConfig() {
|
|
235
|
+
const { endpoint } = this.logtoConfig;
|
|
236
|
+
const discoveryEndpoint = (0, utils_1.getDiscoveryEndpoint)(endpoint);
|
|
237
|
+
return (0, js_1.fetchOidcConfig)(discoveryEndpoint, this.requester);
|
|
238
|
+
}
|
|
239
|
+
async _getJwtVerifyGetKey() {
|
|
240
|
+
const { jwksUri } = await this.getOidcConfig();
|
|
241
|
+
return (0, jose_1.createRemoteJWKSet)(new URL(jwksUri));
|
|
234
242
|
}
|
|
235
243
|
async verifyIdToken(idToken) {
|
|
236
|
-
const {
|
|
237
|
-
const { issuer
|
|
244
|
+
const { appId } = this.logtoConfig;
|
|
245
|
+
const { issuer } = await this.getOidcConfig();
|
|
246
|
+
const jwtVerifyGetKey = await this.getJwtVerifyGetKey();
|
|
238
247
|
try {
|
|
239
|
-
await (0, js_1.verifyIdToken)(idToken,
|
|
248
|
+
await (0, js_1.verifyIdToken)(idToken, appId, issuer, jwtVerifyGetKey);
|
|
240
249
|
}
|
|
241
250
|
catch (error) {
|
|
242
251
|
throw new errors_1.LogtoClientError('invalid_id_token', error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/browser",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"exports": "./lib/index.js",
|
|
6
6
|
"typings": "./lib/index.d.ts",
|
|
@@ -21,21 +21,23 @@
|
|
|
21
21
|
"lint": "eslint --ext .ts src",
|
|
22
22
|
"test": "jest",
|
|
23
23
|
"test:coverage": "jest --silent --coverage",
|
|
24
|
-
"prepack": "pnpm test
|
|
24
|
+
"prepack": "pnpm test"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@logto/js": "^0.1.
|
|
27
|
+
"@logto/js": "^0.1.7",
|
|
28
28
|
"@silverhand/essentials": "^1.1.6",
|
|
29
29
|
"jose": "^4.5.0",
|
|
30
30
|
"lodash.get": "^4.4.2",
|
|
31
|
+
"lodash.once": "^4.1.1",
|
|
31
32
|
"superstruct": "^0.15.3"
|
|
32
33
|
},
|
|
33
34
|
"devDependencies": {
|
|
34
35
|
"@jest/types": "^27.5.1",
|
|
35
|
-
"@silverhand/eslint-config": "^0.
|
|
36
|
-
"@silverhand/ts-config": "^0.
|
|
36
|
+
"@silverhand/eslint-config": "^0.14.0",
|
|
37
|
+
"@silverhand/ts-config": "^0.14.0",
|
|
37
38
|
"@types/jest": "^27.4.0",
|
|
38
39
|
"@types/lodash.get": "^4.4.6",
|
|
40
|
+
"@types/lodash.once": "^4.1.6",
|
|
39
41
|
"eslint": "^8.9.0",
|
|
40
42
|
"jest": "^27.5.1",
|
|
41
43
|
"jest-location-mock": "^1.0.9",
|
|
@@ -53,5 +55,5 @@
|
|
|
53
55
|
"publishConfig": {
|
|
54
56
|
"access": "public"
|
|
55
57
|
},
|
|
56
|
-
"gitHead": "
|
|
58
|
+
"gitHead": "45f2ad06fa069eac66016696703b1a37e1509ffb"
|
|
57
59
|
}
|