@logto/client 2.2.0 → 2.2.2
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/adapter/types.d.ts +30 -1
- package/lib/errors.cjs +2 -0
- package/lib/errors.d.ts +2 -0
- package/lib/errors.js +2 -0
- package/lib/index.cjs +128 -24
- package/lib/index.d.ts +91 -1
- package/lib/index.js +128 -24
- package/lib/types/index.d.ts +36 -0
- package/lib/utils/requester.cjs +7 -0
- package/lib/utils/requester.d.ts +7 -0
- package/lib/utils/requester.js +7 -0
- package/package.json +6 -6
package/lib/adapter/types.d.ts
CHANGED
|
@@ -22,13 +22,23 @@ export declare enum CacheKey {
|
|
|
22
22
|
*/
|
|
23
23
|
Jwks = "jwks"
|
|
24
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The storage object that allows the client to persist data.
|
|
27
|
+
*
|
|
28
|
+
* It's compatible with the `localStorage` API.
|
|
29
|
+
*/
|
|
25
30
|
export type Storage<Keys extends string> = {
|
|
26
31
|
getItem(key: Keys): Promise<Nullable<string>>;
|
|
27
32
|
setItem(key: Keys, value: string): Promise<void>;
|
|
28
33
|
removeItem(key: Keys): Promise<void>;
|
|
29
34
|
};
|
|
30
35
|
export type InferStorageKey<S> = S extends Storage<infer Key> ? Key : never;
|
|
31
|
-
|
|
36
|
+
/** The navigation function that redirects the user to the specified URL. */
|
|
37
|
+
export type Navigate = (url: string) => void | Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* The adapter object that allows the customizations of the client behavior
|
|
40
|
+
* for different environments.
|
|
41
|
+
*/
|
|
32
42
|
export type ClientAdapter = {
|
|
33
43
|
requester: Requester;
|
|
34
44
|
storage: Storage<StorageKey | PersistKey>;
|
|
@@ -39,7 +49,26 @@ export type ClientAdapter = {
|
|
|
39
49
|
*/
|
|
40
50
|
unstable_cache?: Storage<CacheKey>;
|
|
41
51
|
navigate: Navigate;
|
|
52
|
+
/**
|
|
53
|
+
* The function that generates a random state string.
|
|
54
|
+
*
|
|
55
|
+
* @returns The state string.
|
|
56
|
+
*/
|
|
42
57
|
generateState: () => string;
|
|
58
|
+
/**
|
|
59
|
+
* The function that generates a random code verifier string for PKCE.
|
|
60
|
+
*
|
|
61
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc7636.html| RFC 7636}
|
|
62
|
+
* @returns The code verifier string.
|
|
63
|
+
*/
|
|
43
64
|
generateCodeVerifier: () => string;
|
|
65
|
+
/**
|
|
66
|
+
* The function that generates a code challenge string based on the code verifier
|
|
67
|
+
* for PKCE.
|
|
68
|
+
*
|
|
69
|
+
* @see {@link https://www.rfc-editor.org/rfc/rfc7636.html| RFC 7636}
|
|
70
|
+
* @param codeVerifier The code verifier string.
|
|
71
|
+
* @returns The code challenge string.
|
|
72
|
+
*/
|
|
44
73
|
generateCodeChallenge: (codeVerifier: string) => Promise<string>;
|
|
45
74
|
};
|
package/lib/errors.cjs
CHANGED
|
@@ -5,10 +5,12 @@ const logtoClientErrorCodes = Object.freeze({
|
|
|
5
5
|
'sign_in_session.not_found': 'Sign-in session not found.',
|
|
6
6
|
not_authenticated: 'Not authenticated.',
|
|
7
7
|
fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',
|
|
8
|
+
user_cancelled: 'The user cancelled the action.',
|
|
8
9
|
});
|
|
9
10
|
class LogtoClientError extends Error {
|
|
10
11
|
constructor(code, data) {
|
|
11
12
|
super(logtoClientErrorCodes[code]);
|
|
13
|
+
this.name = 'LogtoClientError';
|
|
12
14
|
this.code = code;
|
|
13
15
|
this.data = data;
|
|
14
16
|
}
|
package/lib/errors.d.ts
CHANGED
|
@@ -3,9 +3,11 @@ declare const logtoClientErrorCodes: Readonly<{
|
|
|
3
3
|
'sign_in_session.not_found': "Sign-in session not found.";
|
|
4
4
|
not_authenticated: "Not authenticated.";
|
|
5
5
|
fetch_user_info_failed: "Unable to fetch user info. The access token may be invalid.";
|
|
6
|
+
user_cancelled: "The user cancelled the action.";
|
|
6
7
|
}>;
|
|
7
8
|
export type LogtoClientErrorCode = keyof typeof logtoClientErrorCodes;
|
|
8
9
|
export declare class LogtoClientError extends Error {
|
|
10
|
+
name: string;
|
|
9
11
|
code: LogtoClientErrorCode;
|
|
10
12
|
data: unknown;
|
|
11
13
|
constructor(code: LogtoClientErrorCode, data?: unknown);
|
package/lib/errors.js
CHANGED
|
@@ -3,10 +3,12 @@ const logtoClientErrorCodes = Object.freeze({
|
|
|
3
3
|
'sign_in_session.not_found': 'Sign-in session not found.',
|
|
4
4
|
not_authenticated: 'Not authenticated.',
|
|
5
5
|
fetch_user_info_failed: 'Unable to fetch user info. The access token may be invalid.',
|
|
6
|
+
user_cancelled: 'The user cancelled the action.',
|
|
6
7
|
});
|
|
7
8
|
class LogtoClientError extends Error {
|
|
8
9
|
constructor(code, data) {
|
|
9
10
|
super(logtoClientErrorCodes[code]);
|
|
11
|
+
this.name = 'LogtoClientError';
|
|
10
12
|
this.code = code;
|
|
11
13
|
this.data = data;
|
|
12
14
|
}
|
package/lib/index.cjs
CHANGED
|
@@ -14,6 +14,13 @@ var once = require('./utils/once.cjs');
|
|
|
14
14
|
var types = require('./adapter/types.cjs');
|
|
15
15
|
var requester = require('./utils/requester.cjs');
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* The Logto base client class that provides the essential methods for
|
|
19
|
+
* interacting with the Logto server.
|
|
20
|
+
*
|
|
21
|
+
* It also provides an adapter object that allows the customizations of the
|
|
22
|
+
* client behavior for different environments.
|
|
23
|
+
*/
|
|
17
24
|
class LogtoClient {
|
|
18
25
|
constructor(logtoConfig, adapter) {
|
|
19
26
|
this.getOidcConfig = memoize.memoize(this.#getOidcConfig);
|
|
@@ -27,15 +34,37 @@ class LogtoClient {
|
|
|
27
34
|
this.adapter = new index$1.ClientAdapterInstance(adapter);
|
|
28
35
|
void this.loadAccessTokenMap();
|
|
29
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Check if the user is authenticated by checking if the ID token exists.
|
|
39
|
+
*/
|
|
30
40
|
async isAuthenticated() {
|
|
31
41
|
return Boolean(await this.getIdToken());
|
|
32
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Get the Refresh Token from the storage.
|
|
45
|
+
*/
|
|
33
46
|
async getRefreshToken() {
|
|
34
47
|
return this.adapter.storage.getItem('refreshToken');
|
|
35
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Get the ID Token from the storage. If you want to get the ID Token claims,
|
|
51
|
+
* use {@link getIdTokenClaims} instead.
|
|
52
|
+
*/
|
|
36
53
|
async getIdToken() {
|
|
37
54
|
return this.adapter.storage.getItem('idToken');
|
|
38
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Get the Access Token from the storage. If the Access Token has expired, it
|
|
58
|
+
* will try to fetch a new one using the Refresh Token.
|
|
59
|
+
*
|
|
60
|
+
* If you want to get the Access Token claims, use {@link getAccessTokenClaims} instead.
|
|
61
|
+
*
|
|
62
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
63
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
64
|
+
* resource, as specified in the Logto Console.
|
|
65
|
+
* @returns The Access Token string.
|
|
66
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
67
|
+
*/
|
|
39
68
|
async getAccessToken(resource) {
|
|
40
69
|
if (!(await this.getIdToken())) {
|
|
41
70
|
throw new errors.LogtoClientError('not_authenticated');
|
|
@@ -54,6 +83,9 @@ class LogtoClient {
|
|
|
54
83
|
*/
|
|
55
84
|
return this.getAccessTokenByRefreshToken(resource);
|
|
56
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Get the ID Token claims.
|
|
88
|
+
*/
|
|
57
89
|
async getIdTokenClaims() {
|
|
58
90
|
const idToken = await this.getIdToken();
|
|
59
91
|
if (!idToken) {
|
|
@@ -61,10 +93,27 @@ class LogtoClient {
|
|
|
61
93
|
}
|
|
62
94
|
return js.decodeIdToken(idToken);
|
|
63
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the Access Token claims for the specified resource.
|
|
98
|
+
*
|
|
99
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
100
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
101
|
+
* resource, as specified in the Logto Console.
|
|
102
|
+
*/
|
|
64
103
|
async getAccessTokenClaims(resource) {
|
|
65
104
|
const accessToken = await this.getAccessToken(resource);
|
|
66
105
|
return js.decodeAccessToken(accessToken);
|
|
67
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Get the user information from the Userinfo Endpoint.
|
|
109
|
+
*
|
|
110
|
+
* Note the Userinfo Endpoint will return more claims than the ID Token. See
|
|
111
|
+
* {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#fetch-user-information | Fetch user information}
|
|
112
|
+
* for more information.
|
|
113
|
+
*
|
|
114
|
+
* @returns The user information.
|
|
115
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
116
|
+
*/
|
|
68
117
|
async fetchUserInfo() {
|
|
69
118
|
const { userinfoEndpoint } = await this.getOidcConfig();
|
|
70
119
|
const accessToken = await this.getAccessToken();
|
|
@@ -73,6 +122,22 @@ class LogtoClient {
|
|
|
73
122
|
}
|
|
74
123
|
return js.fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);
|
|
75
124
|
}
|
|
125
|
+
/**
|
|
126
|
+
* Start the sign-in flow with the specified redirect URI. The URI must be
|
|
127
|
+
* registered in the Logto Console.
|
|
128
|
+
*
|
|
129
|
+
* The user will be redirected to that URI after the sign-in flow is completed,
|
|
130
|
+
* and the client will be able to get the authorization code from the URI.
|
|
131
|
+
* To fetch the tokens from the authorization code, use {@link handleSignInCallback}
|
|
132
|
+
* after the user is redirected in the callback URI.
|
|
133
|
+
*
|
|
134
|
+
* @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
|
|
135
|
+
* @param interactionMode The interaction mode to be used for the authorization request. Note it's not
|
|
136
|
+
* a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
|
|
137
|
+
*
|
|
138
|
+
* @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
|
|
139
|
+
* @see {@link InteractionMode}
|
|
140
|
+
*/
|
|
76
141
|
async signIn(redirectUri, interactionMode) {
|
|
77
142
|
const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;
|
|
78
143
|
const { authorizationEndpoint } = await this.getOidcConfig();
|
|
@@ -90,11 +155,21 @@ class LogtoClient {
|
|
|
90
155
|
prompt,
|
|
91
156
|
interactionMode,
|
|
92
157
|
});
|
|
93
|
-
await
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
158
|
+
await Promise.all([
|
|
159
|
+
this.setSignInSession({ redirectUri, codeVerifier, state }),
|
|
160
|
+
this.setRefreshToken(null),
|
|
161
|
+
this.setIdToken(null),
|
|
162
|
+
]);
|
|
163
|
+
await this.adapter.navigate(signInUri);
|
|
97
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Check if the user is redirected from the sign-in page by checking if the
|
|
167
|
+
* current URL matches the redirect URI in the sign-in session.
|
|
168
|
+
*
|
|
169
|
+
* If there's no sign-in session, it will return `false`.
|
|
170
|
+
*
|
|
171
|
+
* @param url The current URL.
|
|
172
|
+
*/
|
|
98
173
|
async isSignInRedirected(url) {
|
|
99
174
|
const signInSession = await this.getSignInSession();
|
|
100
175
|
if (!signInSession) {
|
|
@@ -104,28 +179,59 @@ class LogtoClient {
|
|
|
104
179
|
const { origin, pathname } = new URL(url);
|
|
105
180
|
return `${origin}${pathname}` === redirectUri;
|
|
106
181
|
}
|
|
182
|
+
/**
|
|
183
|
+
* Handle the sign-in callback by parsing the authorization code from the
|
|
184
|
+
* callback URI and exchanging it for the tokens.
|
|
185
|
+
*
|
|
186
|
+
* @param callbackUri The callback URI that the user is redirected to after the sign-in flow is completed.
|
|
187
|
+
* This URI must match the redirect URI specified in {@link signIn}.
|
|
188
|
+
* @throws LogtoClientError if the sign-in session is not found.
|
|
189
|
+
*/
|
|
107
190
|
async handleSignInCallback(callbackUri) {
|
|
108
|
-
const {
|
|
109
|
-
const { requester } = adapter;
|
|
191
|
+
const { requester } = this.adapter;
|
|
110
192
|
const signInSession = await this.getSignInSession();
|
|
111
193
|
if (!signInSession) {
|
|
112
194
|
throw new errors.LogtoClientError('sign_in_session.not_found');
|
|
113
195
|
}
|
|
114
196
|
const { redirectUri, state, codeVerifier } = signInSession;
|
|
115
197
|
const code = js.verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);
|
|
116
|
-
|
|
198
|
+
// NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
|
|
199
|
+
const accessTokenKey = index$2.buildAccessTokenKey();
|
|
200
|
+
const { appId: clientId } = this.logtoConfig;
|
|
117
201
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
118
|
-
const
|
|
202
|
+
const requestedAt = Math.round(Date.now() / 1000);
|
|
203
|
+
const { idToken, refreshToken, accessToken, scope, expiresIn } = await js.fetchTokenByAuthorizationCode({
|
|
119
204
|
clientId,
|
|
120
205
|
tokenEndpoint,
|
|
121
206
|
redirectUri,
|
|
122
207
|
codeVerifier,
|
|
123
208
|
code,
|
|
124
209
|
}, requester);
|
|
125
|
-
await this.verifyIdToken(
|
|
126
|
-
await this.
|
|
210
|
+
await this.verifyIdToken(idToken);
|
|
211
|
+
await this.setRefreshToken(refreshToken ?? null);
|
|
212
|
+
await this.setIdToken(idToken);
|
|
213
|
+
this.accessTokenMap.set(accessTokenKey, {
|
|
214
|
+
token: accessToken,
|
|
215
|
+
scope,
|
|
216
|
+
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
|
|
217
|
+
* in the token claims. It is utilized by the client to determine if the cached access token
|
|
218
|
+
* has expired and when a new access token should be requested.
|
|
219
|
+
*/
|
|
220
|
+
expiresAt: requestedAt + expiresIn,
|
|
221
|
+
});
|
|
222
|
+
await this.saveAccessTokenMap();
|
|
127
223
|
await this.setSignInSession(null);
|
|
128
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* Start the sign-out flow with the specified redirect URI. The URI must be
|
|
227
|
+
* registered in the Logto Console.
|
|
228
|
+
*
|
|
229
|
+
* It will also revoke all the tokens and clean up the storage.
|
|
230
|
+
*
|
|
231
|
+
* The user will be redirected that URI after the sign-out flow is completed.
|
|
232
|
+
* If the `postLogoutRedirectUri` is not specified, the user will be redirected
|
|
233
|
+
* to a default page.
|
|
234
|
+
*/
|
|
129
235
|
async signOut(postLogoutRedirectUri) {
|
|
130
236
|
const { appId: clientId } = this.logtoConfig;
|
|
131
237
|
const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();
|
|
@@ -144,10 +250,12 @@ class LogtoClient {
|
|
|
144
250
|
clientId,
|
|
145
251
|
});
|
|
146
252
|
this.accessTokenMap.clear();
|
|
147
|
-
await
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
253
|
+
await Promise.all([
|
|
254
|
+
this.setRefreshToken(null),
|
|
255
|
+
this.setIdToken(null),
|
|
256
|
+
this.adapter.storage.removeItem('accessToken'),
|
|
257
|
+
]);
|
|
258
|
+
await this.adapter.navigate(url);
|
|
151
259
|
}
|
|
152
260
|
async getSignInSession() {
|
|
153
261
|
const jsonItem = await this.adapter.storage.getItem('signInSession');
|
|
@@ -177,6 +285,7 @@ class LogtoClient {
|
|
|
177
285
|
const accessTokenKey = index$2.buildAccessTokenKey(resource);
|
|
178
286
|
const { appId: clientId } = this.logtoConfig;
|
|
179
287
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
288
|
+
const requestedAt = Math.round(Date.now() / 1000);
|
|
180
289
|
const { accessToken, refreshToken, idToken, scope, expiresIn } = await js.fetchTokenByRefreshToken({
|
|
181
290
|
clientId,
|
|
182
291
|
tokenEndpoint,
|
|
@@ -186,7 +295,11 @@ class LogtoClient {
|
|
|
186
295
|
this.accessTokenMap.set(accessTokenKey, {
|
|
187
296
|
token: accessToken,
|
|
188
297
|
scope,
|
|
189
|
-
expiresAt
|
|
298
|
+
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
|
|
299
|
+
* in the token claims. It is utilized by the client to determine if the cached access token
|
|
300
|
+
* has expired and when a new access token should be requested.
|
|
301
|
+
*/
|
|
302
|
+
expiresAt: requestedAt + expiresIn,
|
|
190
303
|
});
|
|
191
304
|
await this.saveAccessTokenMap();
|
|
192
305
|
await this.setRefreshToken(refreshToken);
|
|
@@ -202,15 +315,6 @@ class LogtoClient {
|
|
|
202
315
|
const jwtVerifyGetKey = await this.getJwtVerifyGetKey();
|
|
203
316
|
await js.verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);
|
|
204
317
|
}
|
|
205
|
-
async saveCodeToken({ refreshToken, idToken, scope, accessToken, expiresIn, }) {
|
|
206
|
-
await this.setRefreshToken(refreshToken ?? null);
|
|
207
|
-
await this.setIdToken(idToken);
|
|
208
|
-
// NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
|
|
209
|
-
const accessTokenKey = index$2.buildAccessTokenKey();
|
|
210
|
-
const expiresAt = Date.now() / 1000 + expiresIn;
|
|
211
|
-
this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });
|
|
212
|
-
await this.saveAccessTokenMap();
|
|
213
|
-
}
|
|
214
318
|
async saveAccessTokenMap() {
|
|
215
319
|
const data = {};
|
|
216
320
|
for (const [key, accessToken] of this.accessTokenMap.entries()) {
|
package/lib/index.d.ts
CHANGED
|
@@ -10,6 +10,13 @@ export type { Storage, StorageKey, ClientAdapter } from './adapter/index.js';
|
|
|
10
10
|
export { PersistKey, CacheKey } from './adapter/index.js';
|
|
11
11
|
export { createRequester } from './utils/index.js';
|
|
12
12
|
export * from './types/index.js';
|
|
13
|
+
/**
|
|
14
|
+
* The Logto base client class that provides the essential methods for
|
|
15
|
+
* interacting with the Logto server.
|
|
16
|
+
*
|
|
17
|
+
* It also provides an adapter object that allows the customizations of the
|
|
18
|
+
* client behavior for different environments.
|
|
19
|
+
*/
|
|
13
20
|
export default class LogtoClient {
|
|
14
21
|
#private;
|
|
15
22
|
protected readonly logtoConfig: LogtoConfig;
|
|
@@ -26,16 +33,100 @@ export default class LogtoClient {
|
|
|
26
33
|
protected readonly adapter: ClientAdapterInstance;
|
|
27
34
|
protected readonly accessTokenMap: Map<string, AccessToken>;
|
|
28
35
|
constructor(logtoConfig: LogtoConfig, adapter: ClientAdapter);
|
|
36
|
+
/**
|
|
37
|
+
* Check if the user is authenticated by checking if the ID token exists.
|
|
38
|
+
*/
|
|
29
39
|
isAuthenticated(): Promise<boolean>;
|
|
40
|
+
/**
|
|
41
|
+
* Get the Refresh Token from the storage.
|
|
42
|
+
*/
|
|
30
43
|
getRefreshToken(): Promise<Nullable<string>>;
|
|
44
|
+
/**
|
|
45
|
+
* Get the ID Token from the storage. If you want to get the ID Token claims,
|
|
46
|
+
* use {@link getIdTokenClaims} instead.
|
|
47
|
+
*/
|
|
31
48
|
getIdToken(): Promise<Nullable<string>>;
|
|
49
|
+
/**
|
|
50
|
+
* Get the Access Token from the storage. If the Access Token has expired, it
|
|
51
|
+
* will try to fetch a new one using the Refresh Token.
|
|
52
|
+
*
|
|
53
|
+
* If you want to get the Access Token claims, use {@link getAccessTokenClaims} instead.
|
|
54
|
+
*
|
|
55
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
56
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
57
|
+
* resource, as specified in the Logto Console.
|
|
58
|
+
* @returns The Access Token string.
|
|
59
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
60
|
+
*/
|
|
32
61
|
getAccessToken(resource?: string): Promise<string>;
|
|
62
|
+
/**
|
|
63
|
+
* Get the ID Token claims.
|
|
64
|
+
*/
|
|
33
65
|
getIdTokenClaims(): Promise<IdTokenClaims>;
|
|
66
|
+
/**
|
|
67
|
+
* Get the Access Token claims for the specified resource.
|
|
68
|
+
*
|
|
69
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
70
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
71
|
+
* resource, as specified in the Logto Console.
|
|
72
|
+
*/
|
|
34
73
|
getAccessTokenClaims(resource?: string): Promise<AccessTokenClaims>;
|
|
74
|
+
/**
|
|
75
|
+
* Get the user information from the Userinfo Endpoint.
|
|
76
|
+
*
|
|
77
|
+
* Note the Userinfo Endpoint will return more claims than the ID Token. See
|
|
78
|
+
* {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#fetch-user-information | Fetch user information}
|
|
79
|
+
* for more information.
|
|
80
|
+
*
|
|
81
|
+
* @returns The user information.
|
|
82
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
83
|
+
*/
|
|
35
84
|
fetchUserInfo(): Promise<UserInfoResponse>;
|
|
85
|
+
/**
|
|
86
|
+
* Start the sign-in flow with the specified redirect URI. The URI must be
|
|
87
|
+
* registered in the Logto Console.
|
|
88
|
+
*
|
|
89
|
+
* The user will be redirected to that URI after the sign-in flow is completed,
|
|
90
|
+
* and the client will be able to get the authorization code from the URI.
|
|
91
|
+
* To fetch the tokens from the authorization code, use {@link handleSignInCallback}
|
|
92
|
+
* after the user is redirected in the callback URI.
|
|
93
|
+
*
|
|
94
|
+
* @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
|
|
95
|
+
* @param interactionMode The interaction mode to be used for the authorization request. Note it's not
|
|
96
|
+
* a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
|
|
97
|
+
*
|
|
98
|
+
* @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
|
|
99
|
+
* @see {@link InteractionMode}
|
|
100
|
+
*/
|
|
36
101
|
signIn(redirectUri: string, interactionMode?: InteractionMode): Promise<void>;
|
|
102
|
+
/**
|
|
103
|
+
* Check if the user is redirected from the sign-in page by checking if the
|
|
104
|
+
* current URL matches the redirect URI in the sign-in session.
|
|
105
|
+
*
|
|
106
|
+
* If there's no sign-in session, it will return `false`.
|
|
107
|
+
*
|
|
108
|
+
* @param url The current URL.
|
|
109
|
+
*/
|
|
37
110
|
isSignInRedirected(url: string): Promise<boolean>;
|
|
111
|
+
/**
|
|
112
|
+
* Handle the sign-in callback by parsing the authorization code from the
|
|
113
|
+
* callback URI and exchanging it for the tokens.
|
|
114
|
+
*
|
|
115
|
+
* @param callbackUri The callback URI that the user is redirected to after the sign-in flow is completed.
|
|
116
|
+
* This URI must match the redirect URI specified in {@link signIn}.
|
|
117
|
+
* @throws LogtoClientError if the sign-in session is not found.
|
|
118
|
+
*/
|
|
38
119
|
handleSignInCallback(callbackUri: string): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Start the sign-out flow with the specified redirect URI. The URI must be
|
|
122
|
+
* registered in the Logto Console.
|
|
123
|
+
*
|
|
124
|
+
* It will also revoke all the tokens and clean up the storage.
|
|
125
|
+
*
|
|
126
|
+
* The user will be redirected that URI after the sign-out flow is completed.
|
|
127
|
+
* If the `postLogoutRedirectUri` is not specified, the user will be redirected
|
|
128
|
+
* to a default page.
|
|
129
|
+
*/
|
|
39
130
|
signOut(postLogoutRedirectUri?: string): Promise<void>;
|
|
40
131
|
protected getSignInSession(): Promise<Nullable<LogtoSignInSessionItem>>;
|
|
41
132
|
protected setSignInSession(value: Nullable<LogtoSignInSessionItem>): Promise<void>;
|
|
@@ -43,7 +134,6 @@ export default class LogtoClient {
|
|
|
43
134
|
private setRefreshToken;
|
|
44
135
|
private getAccessTokenByRefreshToken;
|
|
45
136
|
private verifyIdToken;
|
|
46
|
-
private saveCodeToken;
|
|
47
137
|
private saveAccessTokenMap;
|
|
48
138
|
private loadAccessTokenMap;
|
|
49
139
|
}
|
package/lib/index.js
CHANGED
|
@@ -11,6 +11,13 @@ import { once } from './utils/once.js';
|
|
|
11
11
|
import { PersistKey, CacheKey } from './adapter/types.js';
|
|
12
12
|
export { createRequester } from './utils/requester.js';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* The Logto base client class that provides the essential methods for
|
|
16
|
+
* interacting with the Logto server.
|
|
17
|
+
*
|
|
18
|
+
* It also provides an adapter object that allows the customizations of the
|
|
19
|
+
* client behavior for different environments.
|
|
20
|
+
*/
|
|
14
21
|
class LogtoClient {
|
|
15
22
|
constructor(logtoConfig, adapter) {
|
|
16
23
|
this.getOidcConfig = memoize(this.#getOidcConfig);
|
|
@@ -24,15 +31,37 @@ class LogtoClient {
|
|
|
24
31
|
this.adapter = new ClientAdapterInstance(adapter);
|
|
25
32
|
void this.loadAccessTokenMap();
|
|
26
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Check if the user is authenticated by checking if the ID token exists.
|
|
36
|
+
*/
|
|
27
37
|
async isAuthenticated() {
|
|
28
38
|
return Boolean(await this.getIdToken());
|
|
29
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the Refresh Token from the storage.
|
|
42
|
+
*/
|
|
30
43
|
async getRefreshToken() {
|
|
31
44
|
return this.adapter.storage.getItem('refreshToken');
|
|
32
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Get the ID Token from the storage. If you want to get the ID Token claims,
|
|
48
|
+
* use {@link getIdTokenClaims} instead.
|
|
49
|
+
*/
|
|
33
50
|
async getIdToken() {
|
|
34
51
|
return this.adapter.storage.getItem('idToken');
|
|
35
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the Access Token from the storage. If the Access Token has expired, it
|
|
55
|
+
* will try to fetch a new one using the Refresh Token.
|
|
56
|
+
*
|
|
57
|
+
* If you want to get the Access Token claims, use {@link getAccessTokenClaims} instead.
|
|
58
|
+
*
|
|
59
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
60
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
61
|
+
* resource, as specified in the Logto Console.
|
|
62
|
+
* @returns The Access Token string.
|
|
63
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
64
|
+
*/
|
|
36
65
|
async getAccessToken(resource) {
|
|
37
66
|
if (!(await this.getIdToken())) {
|
|
38
67
|
throw new LogtoClientError('not_authenticated');
|
|
@@ -51,6 +80,9 @@ class LogtoClient {
|
|
|
51
80
|
*/
|
|
52
81
|
return this.getAccessTokenByRefreshToken(resource);
|
|
53
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Get the ID Token claims.
|
|
85
|
+
*/
|
|
54
86
|
async getIdTokenClaims() {
|
|
55
87
|
const idToken = await this.getIdToken();
|
|
56
88
|
if (!idToken) {
|
|
@@ -58,10 +90,27 @@ class LogtoClient {
|
|
|
58
90
|
}
|
|
59
91
|
return decodeIdToken(idToken);
|
|
60
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the Access Token claims for the specified resource.
|
|
95
|
+
*
|
|
96
|
+
* @param resource The resource that the Access Token is granted for. If not
|
|
97
|
+
* specified, the Access Token will be used for OpenID Connect or the default
|
|
98
|
+
* resource, as specified in the Logto Console.
|
|
99
|
+
*/
|
|
61
100
|
async getAccessTokenClaims(resource) {
|
|
62
101
|
const accessToken = await this.getAccessToken(resource);
|
|
63
102
|
return decodeAccessToken(accessToken);
|
|
64
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Get the user information from the Userinfo Endpoint.
|
|
106
|
+
*
|
|
107
|
+
* Note the Userinfo Endpoint will return more claims than the ID Token. See
|
|
108
|
+
* {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#fetch-user-information | Fetch user information}
|
|
109
|
+
* for more information.
|
|
110
|
+
*
|
|
111
|
+
* @returns The user information.
|
|
112
|
+
* @throws LogtoClientError if the user is not authenticated.
|
|
113
|
+
*/
|
|
65
114
|
async fetchUserInfo() {
|
|
66
115
|
const { userinfoEndpoint } = await this.getOidcConfig();
|
|
67
116
|
const accessToken = await this.getAccessToken();
|
|
@@ -70,6 +119,22 @@ class LogtoClient {
|
|
|
70
119
|
}
|
|
71
120
|
return fetchUserInfo(userinfoEndpoint, accessToken, this.adapter.requester);
|
|
72
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Start the sign-in flow with the specified redirect URI. The URI must be
|
|
124
|
+
* registered in the Logto Console.
|
|
125
|
+
*
|
|
126
|
+
* The user will be redirected to that URI after the sign-in flow is completed,
|
|
127
|
+
* and the client will be able to get the authorization code from the URI.
|
|
128
|
+
* To fetch the tokens from the authorization code, use {@link handleSignInCallback}
|
|
129
|
+
* after the user is redirected in the callback URI.
|
|
130
|
+
*
|
|
131
|
+
* @param redirectUri The redirect URI that the user will be redirected to after the sign-in flow is completed.
|
|
132
|
+
* @param interactionMode The interaction mode to be used for the authorization request. Note it's not
|
|
133
|
+
* a part of the OIDC standard, but a Logto-specific extension. Defaults to `signIn`.
|
|
134
|
+
*
|
|
135
|
+
* @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#sign-in | Sign in} for more information.
|
|
136
|
+
* @see {@link InteractionMode}
|
|
137
|
+
*/
|
|
73
138
|
async signIn(redirectUri, interactionMode) {
|
|
74
139
|
const { appId: clientId, prompt, resources, scopes } = this.logtoConfig;
|
|
75
140
|
const { authorizationEndpoint } = await this.getOidcConfig();
|
|
@@ -87,11 +152,21 @@ class LogtoClient {
|
|
|
87
152
|
prompt,
|
|
88
153
|
interactionMode,
|
|
89
154
|
});
|
|
90
|
-
await
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
155
|
+
await Promise.all([
|
|
156
|
+
this.setSignInSession({ redirectUri, codeVerifier, state }),
|
|
157
|
+
this.setRefreshToken(null),
|
|
158
|
+
this.setIdToken(null),
|
|
159
|
+
]);
|
|
160
|
+
await this.adapter.navigate(signInUri);
|
|
94
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Check if the user is redirected from the sign-in page by checking if the
|
|
164
|
+
* current URL matches the redirect URI in the sign-in session.
|
|
165
|
+
*
|
|
166
|
+
* If there's no sign-in session, it will return `false`.
|
|
167
|
+
*
|
|
168
|
+
* @param url The current URL.
|
|
169
|
+
*/
|
|
95
170
|
async isSignInRedirected(url) {
|
|
96
171
|
const signInSession = await this.getSignInSession();
|
|
97
172
|
if (!signInSession) {
|
|
@@ -101,28 +176,59 @@ class LogtoClient {
|
|
|
101
176
|
const { origin, pathname } = new URL(url);
|
|
102
177
|
return `${origin}${pathname}` === redirectUri;
|
|
103
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Handle the sign-in callback by parsing the authorization code from the
|
|
181
|
+
* callback URI and exchanging it for the tokens.
|
|
182
|
+
*
|
|
183
|
+
* @param callbackUri The callback URI that the user is redirected to after the sign-in flow is completed.
|
|
184
|
+
* This URI must match the redirect URI specified in {@link signIn}.
|
|
185
|
+
* @throws LogtoClientError if the sign-in session is not found.
|
|
186
|
+
*/
|
|
104
187
|
async handleSignInCallback(callbackUri) {
|
|
105
|
-
const {
|
|
106
|
-
const { requester } = adapter;
|
|
188
|
+
const { requester } = this.adapter;
|
|
107
189
|
const signInSession = await this.getSignInSession();
|
|
108
190
|
if (!signInSession) {
|
|
109
191
|
throw new LogtoClientError('sign_in_session.not_found');
|
|
110
192
|
}
|
|
111
193
|
const { redirectUri, state, codeVerifier } = signInSession;
|
|
112
194
|
const code = verifyAndParseCodeFromCallbackUri(callbackUri, redirectUri, state);
|
|
113
|
-
|
|
195
|
+
// NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
|
|
196
|
+
const accessTokenKey = buildAccessTokenKey();
|
|
197
|
+
const { appId: clientId } = this.logtoConfig;
|
|
114
198
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
115
|
-
const
|
|
199
|
+
const requestedAt = Math.round(Date.now() / 1000);
|
|
200
|
+
const { idToken, refreshToken, accessToken, scope, expiresIn } = await fetchTokenByAuthorizationCode({
|
|
116
201
|
clientId,
|
|
117
202
|
tokenEndpoint,
|
|
118
203
|
redirectUri,
|
|
119
204
|
codeVerifier,
|
|
120
205
|
code,
|
|
121
206
|
}, requester);
|
|
122
|
-
await this.verifyIdToken(
|
|
123
|
-
await this.
|
|
207
|
+
await this.verifyIdToken(idToken);
|
|
208
|
+
await this.setRefreshToken(refreshToken ?? null);
|
|
209
|
+
await this.setIdToken(idToken);
|
|
210
|
+
this.accessTokenMap.set(accessTokenKey, {
|
|
211
|
+
token: accessToken,
|
|
212
|
+
scope,
|
|
213
|
+
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
|
|
214
|
+
* in the token claims. It is utilized by the client to determine if the cached access token
|
|
215
|
+
* has expired and when a new access token should be requested.
|
|
216
|
+
*/
|
|
217
|
+
expiresAt: requestedAt + expiresIn,
|
|
218
|
+
});
|
|
219
|
+
await this.saveAccessTokenMap();
|
|
124
220
|
await this.setSignInSession(null);
|
|
125
221
|
}
|
|
222
|
+
/**
|
|
223
|
+
* Start the sign-out flow with the specified redirect URI. The URI must be
|
|
224
|
+
* registered in the Logto Console.
|
|
225
|
+
*
|
|
226
|
+
* It will also revoke all the tokens and clean up the storage.
|
|
227
|
+
*
|
|
228
|
+
* The user will be redirected that URI after the sign-out flow is completed.
|
|
229
|
+
* If the `postLogoutRedirectUri` is not specified, the user will be redirected
|
|
230
|
+
* to a default page.
|
|
231
|
+
*/
|
|
126
232
|
async signOut(postLogoutRedirectUri) {
|
|
127
233
|
const { appId: clientId } = this.logtoConfig;
|
|
128
234
|
const { endSessionEndpoint, revocationEndpoint } = await this.getOidcConfig();
|
|
@@ -141,10 +247,12 @@ class LogtoClient {
|
|
|
141
247
|
clientId,
|
|
142
248
|
});
|
|
143
249
|
this.accessTokenMap.clear();
|
|
144
|
-
await
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
250
|
+
await Promise.all([
|
|
251
|
+
this.setRefreshToken(null),
|
|
252
|
+
this.setIdToken(null),
|
|
253
|
+
this.adapter.storage.removeItem('accessToken'),
|
|
254
|
+
]);
|
|
255
|
+
await this.adapter.navigate(url);
|
|
148
256
|
}
|
|
149
257
|
async getSignInSession() {
|
|
150
258
|
const jsonItem = await this.adapter.storage.getItem('signInSession');
|
|
@@ -174,6 +282,7 @@ class LogtoClient {
|
|
|
174
282
|
const accessTokenKey = buildAccessTokenKey(resource);
|
|
175
283
|
const { appId: clientId } = this.logtoConfig;
|
|
176
284
|
const { tokenEndpoint } = await this.getOidcConfig();
|
|
285
|
+
const requestedAt = Math.round(Date.now() / 1000);
|
|
177
286
|
const { accessToken, refreshToken, idToken, scope, expiresIn } = await fetchTokenByRefreshToken({
|
|
178
287
|
clientId,
|
|
179
288
|
tokenEndpoint,
|
|
@@ -183,7 +292,11 @@ class LogtoClient {
|
|
|
183
292
|
this.accessTokenMap.set(accessTokenKey, {
|
|
184
293
|
token: accessToken,
|
|
185
294
|
scope,
|
|
186
|
-
expiresAt
|
|
295
|
+
/** The `expiresAt` variable provides an approximate estimation of the actual `exp` property
|
|
296
|
+
* in the token claims. It is utilized by the client to determine if the cached access token
|
|
297
|
+
* has expired and when a new access token should be requested.
|
|
298
|
+
*/
|
|
299
|
+
expiresAt: requestedAt + expiresIn,
|
|
187
300
|
});
|
|
188
301
|
await this.saveAccessTokenMap();
|
|
189
302
|
await this.setRefreshToken(refreshToken);
|
|
@@ -199,15 +312,6 @@ class LogtoClient {
|
|
|
199
312
|
const jwtVerifyGetKey = await this.getJwtVerifyGetKey();
|
|
200
313
|
await verifyIdToken(idToken, appId, issuer, jwtVerifyGetKey);
|
|
201
314
|
}
|
|
202
|
-
async saveCodeToken({ refreshToken, idToken, scope, accessToken, expiresIn, }) {
|
|
203
|
-
await this.setRefreshToken(refreshToken ?? null);
|
|
204
|
-
await this.setIdToken(idToken);
|
|
205
|
-
// NOTE: Will add scope to accessTokenKey when needed. (Linear issue LOG-1589)
|
|
206
|
-
const accessTokenKey = buildAccessTokenKey();
|
|
207
|
-
const expiresAt = Date.now() / 1000 + expiresIn;
|
|
208
|
-
this.accessTokenMap.set(accessTokenKey, { token: accessToken, scope, expiresAt });
|
|
209
|
-
await this.saveAccessTokenMap();
|
|
210
|
-
}
|
|
211
315
|
async saveAccessTokenMap() {
|
|
212
316
|
const data = {};
|
|
213
317
|
for (const [key, accessToken] of this.accessTokenMap.entries()) {
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
import type { Prompt } from '@logto/js';
|
|
2
|
+
/** The configuration object for the Logto client. */
|
|
2
3
|
export type LogtoConfig = {
|
|
4
|
+
/**
|
|
5
|
+
* The endpoint for the Logto server, you can get it from the integration guide
|
|
6
|
+
* or the team settings page of the Logto Console.
|
|
7
|
+
*
|
|
8
|
+
* @example https://foo.logto.app
|
|
9
|
+
*/
|
|
3
10
|
endpoint: string;
|
|
11
|
+
/**
|
|
12
|
+
* The client ID of your application, you can get it from the integration guide
|
|
13
|
+
* or the application details page of the Logto Console.
|
|
14
|
+
*/
|
|
4
15
|
appId: string;
|
|
16
|
+
/**
|
|
17
|
+
* The client secret of your application, you can get it from the application
|
|
18
|
+
* details page of the Logto Console.
|
|
19
|
+
*/
|
|
5
20
|
appSecret?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The scopes (permissions) that your application needs to access.
|
|
23
|
+
* Scopes that will be added by default: `openid`, `offline_access` and `profile`.
|
|
24
|
+
*
|
|
25
|
+
* If resources are specified, scopes will be applied to every resource.
|
|
26
|
+
*
|
|
27
|
+
* @see {@link https://docs.logto.io/docs/recipes/integrate-logto/vanilla-js/#fetch-user-information | Fetch user information}
|
|
28
|
+
* for more information of available scopes for user information.
|
|
29
|
+
*/
|
|
6
30
|
scopes?: string[];
|
|
31
|
+
/**
|
|
32
|
+
* The API resources that your application needs to access. You can specify
|
|
33
|
+
* multiple resources by providing an array of strings.
|
|
34
|
+
*
|
|
35
|
+
* @see {@link https://docs.logto.io/docs/recipes/rbac/ | RBAC} to learn more about how to use role-based access control (RBAC) to protect API resources.
|
|
36
|
+
*/
|
|
7
37
|
resources?: string[];
|
|
38
|
+
/**
|
|
39
|
+
* The prompt parameter to be used for the authorization request.
|
|
40
|
+
*/
|
|
8
41
|
prompt?: Prompt;
|
|
9
42
|
};
|
|
10
43
|
export type AccessToken = {
|
|
44
|
+
/** The access token string. */
|
|
11
45
|
token: string;
|
|
46
|
+
/** The scopes that the access token is granted for. */
|
|
12
47
|
scope: string;
|
|
48
|
+
/** The timestamp of the access token expiration. */
|
|
13
49
|
expiresAt: number;
|
|
14
50
|
};
|
|
15
51
|
export declare const isLogtoSignInSessionItem: (data: unknown) => data is LogtoSignInSessionItem;
|
package/lib/utils/requester.cjs
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
var js = require('@logto/js');
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A factory function that creates a requester by accepting a `fetch`-like function.
|
|
7
|
+
*
|
|
8
|
+
* @param fetchFunction A `fetch`-like function.
|
|
9
|
+
* @returns A requester function.
|
|
10
|
+
* @see {@link Requester}
|
|
11
|
+
*/
|
|
5
12
|
const createRequester = (fetchFunction) => {
|
|
6
13
|
return async (...args) => {
|
|
7
14
|
const response = await fetchFunction(...args);
|
package/lib/utils/requester.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import type { Requester } from '@logto/js';
|
|
2
|
+
/**
|
|
3
|
+
* A factory function that creates a requester by accepting a `fetch`-like function.
|
|
4
|
+
*
|
|
5
|
+
* @param fetchFunction A `fetch`-like function.
|
|
6
|
+
* @returns A requester function.
|
|
7
|
+
* @see {@link Requester}
|
|
8
|
+
*/
|
|
2
9
|
export declare const createRequester: (fetchFunction: typeof fetch) => Requester;
|
package/lib/utils/requester.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { isLogtoRequestError, LogtoError, LogtoRequestError } from '@logto/js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* A factory function that creates a requester by accepting a `fetch`-like function.
|
|
5
|
+
*
|
|
6
|
+
* @param fetchFunction A `fetch`-like function.
|
|
7
|
+
* @returns A requester function.
|
|
8
|
+
* @see {@link Requester}
|
|
9
|
+
*/
|
|
3
10
|
const createRequester = (fetchFunction) => {
|
|
4
11
|
return async (...args) => {
|
|
5
12
|
const response = await fetchFunction(...args);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logto/client",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./lib/index.cjs",
|
|
6
6
|
"module": "./lib/index.js",
|
|
@@ -21,24 +21,24 @@
|
|
|
21
21
|
"directory": "packages/client"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@logto/js": "^2.1.
|
|
24
|
+
"@logto/js": "^2.1.2",
|
|
25
25
|
"@silverhand/essentials": "^2.6.2",
|
|
26
26
|
"camelcase-keys": "^7.0.1",
|
|
27
27
|
"jose": "^4.13.2"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"@silverhand/eslint-config": "^
|
|
31
|
-
"@silverhand/ts-config": "^
|
|
30
|
+
"@silverhand/eslint-config": "^4.0.1",
|
|
31
|
+
"@silverhand/ts-config": "^4.0.0",
|
|
32
32
|
"@swc/core": "^1.3.50",
|
|
33
33
|
"@swc/jest": "^0.2.24",
|
|
34
34
|
"@types/jest": "^29.5.0",
|
|
35
35
|
"@types/node": "^18.0.0",
|
|
36
|
-
"eslint": "^8.
|
|
36
|
+
"eslint": "^8.44.0",
|
|
37
37
|
"jest": "^29.5.0",
|
|
38
38
|
"jest-matcher-specific-error": "^1.0.0",
|
|
39
39
|
"lint-staged": "^13.0.0",
|
|
40
40
|
"nock": "^13.3.0",
|
|
41
|
-
"prettier": "^
|
|
41
|
+
"prettier": "^3.0.0",
|
|
42
42
|
"text-encoder": "^0.0.4",
|
|
43
43
|
"type-fest": "^3.0.0",
|
|
44
44
|
"typescript": "^5.0.0"
|