@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 CHANGED
@@ -1,11 +1,11 @@
1
- import { IdTokenClaims, OidcConfigResponse, Requester, UserInfoResponse } from '@logto/js';
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
- clientId: string;
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 oidcConfig?: OidcConfigResponse;
31
- protected logtoStorageKey: string;
32
- protected requester: Requester;
33
- protected accessTokenMap: Map<string, AccessToken>;
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 getOidcConfig;
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.clientId);
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._refreshToken;
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
- * Token has expired, need to fetch a new one using refresh token.
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 getOidcConfig() {
228
- if (!this.oidcConfig) {
229
- const { endpoint } = this.logtoConfig;
230
- const discoveryEndpoint = (0, utils_1.getDiscoveryEndpoint)(endpoint);
231
- this.oidcConfig = await (0, js_1.fetchOidcConfig)(discoveryEndpoint, this.requester);
232
- }
233
- return this.oidcConfig;
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 { clientId } = this.logtoConfig;
237
- const { issuer, jwksUri } = await this.getOidcConfig();
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, clientId, issuer, (0, jose_1.createRemoteJWKSet)(new URL(jwksUri)));
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",
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 && pnpm build"
24
+ "prepack": "pnpm test"
25
25
  },
26
26
  "dependencies": {
27
- "@logto/js": "^0.1.3",
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.9.1",
36
- "@silverhand/ts-config": "^0.9.1",
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": "2a59d35046744dd1284eb81804cf0ef9a35f41e6"
58
+ "gitHead": "45f2ad06fa069eac66016696703b1a37e1509ffb"
57
59
  }