@logto/client 2.2.2 → 2.2.4

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/README.md CHANGED
@@ -5,8 +5,6 @@
5
5
 
6
6
  The Logto JavaScript Client SDK written in TypeScript. Check out our [docs](https://docs.logto.io/sdk/JavaScript/client/) for more information.
7
7
 
8
- We also provide [文档](https://docs.logto.io/zh-cn/sdk/JavaScript/sdk/client/) in Simplified Chinese.
9
-
10
8
  ## Installation
11
9
 
12
10
  ### Using npm
package/lib/index.cjs CHANGED
@@ -24,6 +24,21 @@ var requester = require('./utils/requester.cjs');
24
24
  class LogtoClient {
25
25
  constructor(logtoConfig, adapter) {
26
26
  this.getOidcConfig = memoize.memoize(this.#getOidcConfig);
27
+ /**
28
+ * Get the access token from the storage.
29
+ *
30
+ * - If the access token has expired, it will try to fetch a new one using the Refresh Token.
31
+ * - If there's an ongoing Promise to fetch the access token, it will return the Promise.
32
+ *
33
+ * If you want to get the access token claims, use {@link getAccessTokenClaims} instead.
34
+ *
35
+ * @param resource The resource that the access token is granted for. If not
36
+ * specified, the access token will be used for OpenID Connect or the default
37
+ * resource, as specified in the Logto Console.
38
+ * @returns The access token string.
39
+ * @throws LogtoClientError if the user is not authenticated.
40
+ */
41
+ this.getAccessToken = memoize.memoize(this.#getAccessToken);
27
42
  this.getJwtVerifyGetKey = once.once(this.#getJwtVerifyGetKey);
28
43
  this.accessTokenMap = new Map();
29
44
  this.logtoConfig = {
@@ -53,36 +68,6 @@ class LogtoClient {
53
68
  async getIdToken() {
54
69
  return this.adapter.storage.getItem('idToken');
55
70
  }
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
- */
68
- async getAccessToken(resource) {
69
- if (!(await this.getIdToken())) {
70
- throw new errors.LogtoClientError('not_authenticated');
71
- }
72
- const accessTokenKey = index$2.buildAccessTokenKey(resource);
73
- const accessToken = this.accessTokenMap.get(accessTokenKey);
74
- if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
75
- return accessToken.token;
76
- }
77
- // Since the access token has expired, delete it from the map.
78
- if (accessToken) {
79
- this.accessTokenMap.delete(accessTokenKey);
80
- }
81
- /**
82
- * Need to fetch a new access token using refresh token.
83
- */
84
- return this.getAccessTokenByRefreshToken(resource);
85
- }
86
71
  /**
87
72
  * Get the ID Token claims.
88
73
  */
@@ -94,10 +79,10 @@ class LogtoClient {
94
79
  return js.decodeIdToken(idToken);
95
80
  }
96
81
  /**
97
- * Get the Access Token claims for the specified resource.
82
+ * Get the access token claims for the specified resource.
98
83
  *
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
84
+ * @param resource The resource that the access token is granted for. If not
85
+ * specified, the access token will be used for OpenID Connect or the default
101
86
  * resource, as specified in the Logto Console.
102
87
  */
103
88
  async getAccessTokenClaims(resource) {
@@ -183,8 +168,9 @@ class LogtoClient {
183
168
  * Handle the sign-in callback by parsing the authorization code from the
184
169
  * callback URI and exchanging it for the tokens.
185
170
  *
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}.
171
+ * @param callbackUri The callback URI, including the search params, that the user is redirected to after the sign-in flow is completed.
172
+ * The origin and pathname of this URI must match the origin and pathname of the redirect URI specified in {@link signIn}.
173
+ * In many cases you'll probably end up passing `window.location.href` as the argument to this function.
188
174
  * @throws LogtoClientError if the sign-in session is not found.
189
175
  */
190
176
  async handleSignInCallback(callbackUri) {
@@ -302,7 +288,9 @@ class LogtoClient {
302
288
  expiresAt: requestedAt + expiresIn,
303
289
  });
304
290
  await this.saveAccessTokenMap();
305
- await this.setRefreshToken(refreshToken);
291
+ if (refreshToken) {
292
+ await this.setRefreshToken(refreshToken);
293
+ }
306
294
  if (idToken) {
307
295
  await this.verifyIdToken(idToken);
308
296
  await this.setIdToken(idToken);
@@ -355,6 +343,24 @@ class LogtoClient {
355
343
  const cachedJwkSet = new remoteJwkSet.CachedRemoteJwkSet(new URL(jwksUri), this.adapter);
356
344
  return async (...args) => cachedJwkSet.getKey(...args);
357
345
  }
346
+ async #getAccessToken(resource) {
347
+ if (!(await this.getIdToken())) {
348
+ throw new errors.LogtoClientError('not_authenticated');
349
+ }
350
+ const accessTokenKey = index$2.buildAccessTokenKey(resource);
351
+ const accessToken = this.accessTokenMap.get(accessTokenKey);
352
+ if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
353
+ return accessToken.token;
354
+ }
355
+ // Since the access token has expired, delete it from the map.
356
+ if (accessToken) {
357
+ this.accessTokenMap.delete(accessTokenKey);
358
+ }
359
+ /**
360
+ * Need to fetch a new access token using refresh token.
361
+ */
362
+ return this.getAccessTokenByRefreshToken(resource);
363
+ }
358
364
  }
359
365
 
360
366
  Object.defineProperty(exports, 'LogtoError', {
package/lib/index.d.ts CHANGED
@@ -4,7 +4,7 @@ import { type JWTVerifyGetKey } from 'jose';
4
4
  import { ClientAdapterInstance, type ClientAdapter } from './adapter/index.js';
5
5
  import type { AccessToken, LogtoConfig, LogtoSignInSessionItem } from './types/index.js';
6
6
  export type { IdTokenClaims, LogtoErrorCode, UserInfoResponse, InteractionMode } from '@logto/js';
7
- export { LogtoError, OidcError, Prompt, LogtoRequestError, ReservedScope, UserScope, } from '@logto/js';
7
+ export { LogtoError, LogtoRequestError, OidcError, Prompt, ReservedScope, UserScope, } from '@logto/js';
8
8
  export * from './errors.js';
9
9
  export type { Storage, StorageKey, ClientAdapter } from './adapter/index.js';
10
10
  export { PersistKey, CacheKey } from './adapter/index.js';
@@ -19,8 +19,8 @@ export * from './types/index.js';
19
19
  */
20
20
  export default class LogtoClient {
21
21
  #private;
22
- protected readonly logtoConfig: LogtoConfig;
23
- protected readonly getOidcConfig: (this: unknown) => Promise<import("@silverhand/essentials").KeysToCamelCase<{
22
+ readonly logtoConfig: LogtoConfig;
23
+ readonly getOidcConfig: (this: unknown) => Promise<import("@silverhand/essentials").KeysToCamelCase<{
24
24
  authorization_endpoint: string;
25
25
  token_endpoint: string;
26
26
  userinfo_endpoint: string;
@@ -29,6 +29,21 @@ export default class LogtoClient {
29
29
  jwks_uri: string;
30
30
  issuer: string;
31
31
  }>>;
32
+ /**
33
+ * Get the access token from the storage.
34
+ *
35
+ * - If the access token has expired, it will try to fetch a new one using the Refresh Token.
36
+ * - If there's an ongoing Promise to fetch the access token, it will return the Promise.
37
+ *
38
+ * If you want to get the access token claims, use {@link getAccessTokenClaims} instead.
39
+ *
40
+ * @param resource The resource that the access token is granted for. If not
41
+ * specified, the access token will be used for OpenID Connect or the default
42
+ * resource, as specified in the Logto Console.
43
+ * @returns The access token string.
44
+ * @throws LogtoClientError if the user is not authenticated.
45
+ */
46
+ readonly getAccessToken: (this: unknown, resource?: string | undefined) => Promise<string>;
32
47
  protected readonly getJwtVerifyGetKey: (...args: unknown[]) => Promise<JWTVerifyGetKey>;
33
48
  protected readonly adapter: ClientAdapterInstance;
34
49
  protected readonly accessTokenMap: Map<string, AccessToken>;
@@ -46,28 +61,15 @@ export default class LogtoClient {
46
61
  * use {@link getIdTokenClaims} instead.
47
62
  */
48
63
  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
- */
61
- getAccessToken(resource?: string): Promise<string>;
62
64
  /**
63
65
  * Get the ID Token claims.
64
66
  */
65
67
  getIdTokenClaims(): Promise<IdTokenClaims>;
66
68
  /**
67
- * Get the Access Token claims for the specified resource.
69
+ * Get the access token claims for the specified resource.
68
70
  *
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
+ * @param resource The resource that the access token is granted for. If not
72
+ * specified, the access token will be used for OpenID Connect or the default
71
73
  * resource, as specified in the Logto Console.
72
74
  */
73
75
  getAccessTokenClaims(resource?: string): Promise<AccessTokenClaims>;
@@ -112,8 +114,9 @@ export default class LogtoClient {
112
114
  * Handle the sign-in callback by parsing the authorization code from the
113
115
  * callback URI and exchanging it for the tokens.
114
116
  *
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
+ * @param callbackUri The callback URI, including the search params, that the user is redirected to after the sign-in flow is completed.
118
+ * The origin and pathname of this URI must match the origin and pathname of the redirect URI specified in {@link signIn}.
119
+ * In many cases you'll probably end up passing `window.location.href` as the argument to this function.
117
120
  * @throws LogtoClientError if the sign-in session is not found.
118
121
  */
119
122
  handleSignInCallback(callbackUri: string): Promise<void>;
package/lib/index.js CHANGED
@@ -21,6 +21,21 @@ export { createRequester } from './utils/requester.js';
21
21
  class LogtoClient {
22
22
  constructor(logtoConfig, adapter) {
23
23
  this.getOidcConfig = memoize(this.#getOidcConfig);
24
+ /**
25
+ * Get the access token from the storage.
26
+ *
27
+ * - If the access token has expired, it will try to fetch a new one using the Refresh Token.
28
+ * - If there's an ongoing Promise to fetch the access token, it will return the Promise.
29
+ *
30
+ * If you want to get the access token claims, use {@link getAccessTokenClaims} instead.
31
+ *
32
+ * @param resource The resource that the access token is granted for. If not
33
+ * specified, the access token will be used for OpenID Connect or the default
34
+ * resource, as specified in the Logto Console.
35
+ * @returns The access token string.
36
+ * @throws LogtoClientError if the user is not authenticated.
37
+ */
38
+ this.getAccessToken = memoize(this.#getAccessToken);
24
39
  this.getJwtVerifyGetKey = once(this.#getJwtVerifyGetKey);
25
40
  this.accessTokenMap = new Map();
26
41
  this.logtoConfig = {
@@ -50,36 +65,6 @@ class LogtoClient {
50
65
  async getIdToken() {
51
66
  return this.adapter.storage.getItem('idToken');
52
67
  }
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
- */
65
- async getAccessToken(resource) {
66
- if (!(await this.getIdToken())) {
67
- throw new LogtoClientError('not_authenticated');
68
- }
69
- const accessTokenKey = buildAccessTokenKey(resource);
70
- const accessToken = this.accessTokenMap.get(accessTokenKey);
71
- if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
72
- return accessToken.token;
73
- }
74
- // Since the access token has expired, delete it from the map.
75
- if (accessToken) {
76
- this.accessTokenMap.delete(accessTokenKey);
77
- }
78
- /**
79
- * Need to fetch a new access token using refresh token.
80
- */
81
- return this.getAccessTokenByRefreshToken(resource);
82
- }
83
68
  /**
84
69
  * Get the ID Token claims.
85
70
  */
@@ -91,10 +76,10 @@ class LogtoClient {
91
76
  return decodeIdToken(idToken);
92
77
  }
93
78
  /**
94
- * Get the Access Token claims for the specified resource.
79
+ * Get the access token claims for the specified resource.
95
80
  *
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
81
+ * @param resource The resource that the access token is granted for. If not
82
+ * specified, the access token will be used for OpenID Connect or the default
98
83
  * resource, as specified in the Logto Console.
99
84
  */
100
85
  async getAccessTokenClaims(resource) {
@@ -180,8 +165,9 @@ class LogtoClient {
180
165
  * Handle the sign-in callback by parsing the authorization code from the
181
166
  * callback URI and exchanging it for the tokens.
182
167
  *
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}.
168
+ * @param callbackUri The callback URI, including the search params, that the user is redirected to after the sign-in flow is completed.
169
+ * The origin and pathname of this URI must match the origin and pathname of the redirect URI specified in {@link signIn}.
170
+ * In many cases you'll probably end up passing `window.location.href` as the argument to this function.
185
171
  * @throws LogtoClientError if the sign-in session is not found.
186
172
  */
187
173
  async handleSignInCallback(callbackUri) {
@@ -299,7 +285,9 @@ class LogtoClient {
299
285
  expiresAt: requestedAt + expiresIn,
300
286
  });
301
287
  await this.saveAccessTokenMap();
302
- await this.setRefreshToken(refreshToken);
288
+ if (refreshToken) {
289
+ await this.setRefreshToken(refreshToken);
290
+ }
303
291
  if (idToken) {
304
292
  await this.verifyIdToken(idToken);
305
293
  await this.setIdToken(idToken);
@@ -352,6 +340,24 @@ class LogtoClient {
352
340
  const cachedJwkSet = new CachedRemoteJwkSet(new URL(jwksUri), this.adapter);
353
341
  return async (...args) => cachedJwkSet.getKey(...args);
354
342
  }
343
+ async #getAccessToken(resource) {
344
+ if (!(await this.getIdToken())) {
345
+ throw new LogtoClientError('not_authenticated');
346
+ }
347
+ const accessTokenKey = buildAccessTokenKey(resource);
348
+ const accessToken = this.accessTokenMap.get(accessTokenKey);
349
+ if (accessToken && accessToken.expiresAt > Date.now() / 1000) {
350
+ return accessToken.token;
351
+ }
352
+ // Since the access token has expired, delete it from the map.
353
+ if (accessToken) {
354
+ this.accessTokenMap.delete(accessTokenKey);
355
+ }
356
+ /**
357
+ * Need to fetch a new access token using refresh token.
358
+ */
359
+ return this.getAccessTokenByRefreshToken(resource);
360
+ }
355
361
  }
356
362
 
357
363
  export { CacheKey, LogtoClientError, PersistKey, LogtoClient as default, isLogtoAccessTokenMap, isLogtoSignInSessionItem };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logto/client",
3
- "version": "2.2.2",
3
+ "version": "2.2.4",
4
4
  "type": "module",
5
5
  "main": "./lib/index.cjs",
6
6
  "module": "./lib/index.js",
@@ -21,7 +21,7 @@
21
21
  "directory": "packages/client"
22
22
  },
23
23
  "dependencies": {
24
- "@logto/js": "^2.1.2",
24
+ "@logto/js": "^2.1.3",
25
25
  "@silverhand/essentials": "^2.6.2",
26
26
  "camelcase-keys": "^7.0.1",
27
27
  "jose": "^4.13.2"
@@ -36,11 +36,11 @@
36
36
  "eslint": "^8.44.0",
37
37
  "jest": "^29.5.0",
38
38
  "jest-matcher-specific-error": "^1.0.0",
39
- "lint-staged": "^13.0.0",
39
+ "lint-staged": "^15.0.0",
40
40
  "nock": "^13.3.0",
41
41
  "prettier": "^3.0.0",
42
42
  "text-encoder": "^0.0.4",
43
- "type-fest": "^3.0.0",
43
+ "type-fest": "^4.0.0",
44
44
  "typescript": "^5.0.0"
45
45
  },
46
46
  "eslintConfig": {