oidc-spa 3.0.2 → 4.0.0

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/react.js CHANGED
@@ -27,17 +27,18 @@ var __read = (this && this.__read) || function (o, n) {
27
27
  return ar;
28
28
  };
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.createUseOidc = exports.createOidcProvider = void 0;
30
+ exports.createReactOidc = void 0;
31
31
  var jsx_runtime_1 = require("react/jsx-runtime");
32
32
  var react_1 = require("react");
33
33
  var oidc_1 = require("./oidc");
34
34
  var assert_1 = require("tsafe/assert");
35
- var decodeJwt_1 = require("./tools/decodeJwt");
36
35
  var id_1 = require("tsafe/id");
36
+ var useGuaranteedMemo_1 = require("./tools/powerhooks/useGuaranteedMemo");
37
37
  var oidcContext = (0, react_1.createContext)(undefined);
38
38
  /** @see: https://github.com/garronej/oidc-spa#option-2-usage-directly-within-react */
39
- function createOidcProvider(params) {
39
+ function createReactOidc(params) {
40
40
  var prOidc = (0, oidc_1.createOidc)(params);
41
+ var decodedIdTokenSchema = params.decodedIdTokenSchema;
41
42
  function OidcProvider(props) {
42
43
  var children = props.children, fallback = props.fallback;
43
44
  var _a = __read((0, react_1.useState)(undefined), 2), oidc = _a[0], setOidc = _a[1];
@@ -47,18 +48,16 @@ function createOidcProvider(params) {
47
48
  if (oidc === undefined) {
48
49
  return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: fallback === undefined ? null : fallback });
49
50
  }
50
- return (0, jsx_runtime_1.jsx)(oidcContext.Provider, { value: oidc, children: children });
51
+ return ((0, jsx_runtime_1.jsx)(oidcContext.Provider, { value: { oidc: oidc, decodedIdTokenSchema: decodedIdTokenSchema }, children: children }));
51
52
  }
52
- return { OidcProvider: OidcProvider, prOidc: prOidc };
53
- }
54
- exports.createOidcProvider = createOidcProvider;
55
- function createUseOidc(params) {
56
- var decodedIdTokenSchema = (params !== null && params !== void 0 ? params : {}).decodedIdTokenSchema;
57
53
  function useOidc(params) {
58
54
  var _a = (params !== null && params !== void 0 ? params : {}).assertUserLoggedIn, assertUserLoggedIn = _a === void 0 ? false : _a;
59
- var oidc = (0, react_1.useContext)(oidcContext);
60
- (0, assert_1.assert)(oidc !== undefined, "You must use useOidc inside a OidcProvider");
61
- var _b = __read((0, react_1.useReducer)(function () { return []; }, []), 2), forceUpdate = _b[1];
55
+ var _b = (function useClosure() {
56
+ var context = (0, react_1.useContext)(oidcContext);
57
+ (0, assert_1.assert)(context !== undefined, "You must use useOidc inside a OidcProvider");
58
+ return context;
59
+ })(), oidc = _b.oidc, decodedIdTokenSchema_context = _b.decodedIdTokenSchema;
60
+ var _c = __read((0, react_1.useReducer)(function () { return []; }, []), 2), forceUpdate = _c[1];
62
61
  (0, react_1.useEffect)(function () {
63
62
  if (!oidc.isUserLoggedIn) {
64
63
  return;
@@ -69,25 +68,53 @@ function createUseOidc(params) {
69
68
  if (assertUserLoggedIn && !oidc.isUserLoggedIn) {
70
69
  throw new Error("The user must be logged in to use this hook (assertUserLoggedIn was set to true)");
71
70
  }
72
- var tokens = oidc.isUserLoggedIn ? oidc.getTokens() : undefined;
73
- var decodedIdToken = (0, react_1.useMemo)(function () {
74
- if ((tokens === null || tokens === void 0 ? void 0 : tokens.idToken) === undefined) {
75
- return undefined;
76
- }
77
- var decodedIdToken = (0, decodeJwt_1.decodeJwt)(tokens.idToken);
78
- if (decodedIdTokenSchema !== undefined) {
79
- decodedIdTokenSchema.parse((0, decodeJwt_1.decodeJwt)(tokens.idToken));
80
- }
81
- return decodedIdToken;
82
- }, [tokens === null || tokens === void 0 ? void 0 : tokens.idToken]);
71
+ var oidcTokens = (function useClosure() {
72
+ var tokens = oidc.isUserLoggedIn ? oidc.getTokens() : undefined;
73
+ var oidcTokens = (0, useGuaranteedMemo_1.useGuaranteedMemo)(function () {
74
+ if (tokens === undefined) {
75
+ return undefined;
76
+ }
77
+ var oidcTokens = {
78
+ "accessToken": tokens.accessToken,
79
+ "accessTokenExpirationTime": tokens.accessTokenExpirationTime,
80
+ "idToken": tokens.idToken,
81
+ "refreshToken": tokens.refreshToken,
82
+ "refreshTokenExpirationTime": tokens.refreshTokenExpirationTime,
83
+ "decodedIdToken": null
84
+ };
85
+ var cache = undefined;
86
+ Object.defineProperty(oidcTokens, "decodedIdToken", {
87
+ "get": function () {
88
+ if (cache !== undefined) {
89
+ return cache.decodedIdToken;
90
+ }
91
+ var decodedIdToken = tokens.decodedIdToken;
92
+ if (decodedIdTokenSchema !== undefined &&
93
+ decodedIdTokenSchema !== decodedIdTokenSchema_context) {
94
+ decodedIdToken = decodedIdTokenSchema.parse(decodedIdToken);
95
+ }
96
+ cache = { decodedIdToken: decodedIdToken };
97
+ return decodedIdToken;
98
+ }
99
+ });
100
+ return oidcTokens;
101
+ }, [
102
+ tokens === null || tokens === void 0 ? void 0 : tokens.accessToken,
103
+ tokens === null || tokens === void 0 ? void 0 : tokens.accessTokenExpirationTime,
104
+ tokens === null || tokens === void 0 ? void 0 : tokens.idToken,
105
+ tokens === null || tokens === void 0 ? void 0 : tokens.refreshToken,
106
+ tokens === null || tokens === void 0 ? void 0 : tokens.refreshTokenExpirationTime
107
+ ]);
108
+ return { oidcTokens: oidcTokens };
109
+ })().oidcTokens;
83
110
  var common = {
84
111
  "params": oidc.params
85
112
  };
86
113
  return oidc.isUserLoggedIn
87
- ? (0, id_1.id)(__assign(__assign({}, common), { "isUserLoggedIn": true, "oidcTokens": __assign(__assign({}, tokens), { "decodedIdToken": decodedIdToken }), "logout": oidc.logout, "renewTokens": oidc.renewTokens, "login": undefined }))
88
- : (0, id_1.id)(__assign(__assign({}, common), { "isUserLoggedIn": false, "login": oidc.login, "oidcTokens": undefined, "logout": undefined }));
114
+ ? (0, id_1.id)(((0, assert_1.assert)(oidcTokens !== undefined), __assign(__assign({}, common), { "isUserLoggedIn": true, oidcTokens: oidcTokens, "logout": oidc.logout, "renewTokens": oidc.renewTokens })))
115
+ : (0, id_1.id)(__assign(__assign({}, common), { "isUserLoggedIn": false, "login": oidc.login }));
89
116
  }
90
- return { useOidc: useOidc };
117
+ return { OidcProvider: OidcProvider, useOidc: useOidc, prOidc: prOidc };
91
118
  }
92
- exports.createUseOidc = createUseOidc;
119
+ exports.createReactOidc = createReactOidc;
93
120
  //# sourceMappingURL=react.js.map
package/react.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["src/react.tsx"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAQe;AACf,+BAA+C;AAC/C,uCAAsC;AACtC,+CAA8C;AAC9C,+BAA8B;AAE9B,IAAM,WAAW,GAAG,IAAA,qBAAa,EAAmB,SAAS,CAAC,CAAC;AAE/D,sFAAsF;AACtF,SAAgB,kBAAkB,CAAC,MAAwC;IACvE,IAAM,MAAM,GAAG,IAAA,iBAAU,EAAC,MAAM,CAAC,CAAC;IAElC,SAAS,YAAY,CAAC,KAAoD;QAC9D,IAAA,QAAQ,GAAe,KAAK,SAApB,EAAE,QAAQ,GAAK,KAAK,SAAV,CAAW;QAE/B,IAAA,KAAA,OAAkB,IAAA,gBAAQ,EAAmB,SAAS,CAAC,IAAA,EAAtD,IAAI,QAAA,EAAE,OAAO,QAAyC,CAAC;QAE9D,IAAA,iBAAS,EAAC;YACN,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,EAAE,EAAE,CAAC,CAAC;QAEP,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACrB,OAAO,2DAAG,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,GAAI,CAAC;QAC3D,CAAC;QAED,OAAO,uBAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,IAAI,YAAG,QAAQ,GAAwB,CAAC;IAChF,CAAC;IAED,OAAO,EAAE,YAAY,cAAA,EAAE,MAAM,QAAA,EAAE,CAAC;AACpC,CAAC;AApBD,gDAoBC;AA2BD,SAAgB,aAAa,CAE3B,MAAoF;IAC1E,IAAA,oBAAoB,GAAK,CAAA,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,EAAE,CAAA,qBAAjB,CAAkB;IAI9C,SAAS,OAAO,CAAC,MAAwC;QAC7C,IAAA,KAA+B,CAAA,MAAM,aAAN,MAAM,cAAN,MAAM,GAAI,EAAE,CAAA,mBAAjB,EAA1B,kBAAkB,mBAAG,KAAK,KAAA,CAAkB;QAEpD,IAAM,IAAI,GAAG,IAAA,kBAAU,EAAC,WAAW,CAAC,CAAC;QAErC,IAAA,eAAM,EAAC,IAAI,KAAK,SAAS,EAAE,4CAA4C,CAAC,CAAC;QAEnE,IAAA,KAAA,OAAkB,IAAA,kBAAU,EAAC,cAAM,OAAA,EAAE,EAAF,CAAE,EAAE,EAAE,CAAC,IAAA,EAAvC,WAAW,QAA4B,CAAC;QAEjD,IAAA,iBAAS,EAAC;YACN,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;gBACvB,OAAO;YACX,CAAC;YAEO,IAAA,WAAW,GAAK,IAAI,CAAC,uBAAuB,CAAC,WAAW,CAAC,YAA9C,CAA+C;YAElE,OAAO,WAAW,CAAC;QACvB,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC;QAEX,IAAI,kBAAkB,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;YAC7C,MAAM,IAAI,KAAK,CACX,kFAAkF,CACrF,CAAC;QACN,CAAC;QAED,IAAM,MAAM,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;QAElE,IAAM,cAAc,GAAG,IAAA,eAAO,EAAC;YAC3B,IAAI,CAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,MAAK,SAAS,EAAE,CAAC;gBAChC,OAAO,SAAS,CAAC;YACrB,CAAC;YAED,IAAM,cAAc,GAAG,IAAA,qBAAS,EAAC,MAAM,CAAC,OAAO,CAAuB,CAAC;YAEvE,IAAI,oBAAoB,KAAK,SAAS,EAAE,CAAC;gBACrC,oBAAoB,CAAC,KAAK,CAAC,IAAA,qBAAS,EAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;YAC1D,CAAC;YAED,OAAO,cAAc,CAAC;QAC1B,CAAC,EAAE,CAAC,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,OAAO,CAAC,CAAC,CAAC;QAEtB,IAAM,MAAM,GAAwB;YAChC,QAAQ,EAAE,IAAI,CAAC,MAAM;SACxB,CAAC;QAEF,OAAO,IAAI,CAAC,cAAc;YACtB,CAAC,CAAC,IAAA,OAAE,wBACK,MAAM,KACT,gBAAgB,EAAE,IAAI,EACtB,YAAY,wBACL,MAAO,KACV,gBAAgB,EAAE,cAAe,KAErC,QAAQ,EAAE,IAAI,CAAC,MAAM,EACrB,aAAa,EAAE,IAAI,CAAC,WAAW,EAC/B,OAAO,EAAE,SAAS,IACpB;YACJ,CAAC,CAAC,IAAA,OAAE,wBACK,MAAM,KACT,gBAAgB,EAAE,KAAK,EACvB,OAAO,EAAE,IAAI,CAAC,KAAK,EACnB,YAAY,EAAE,SAAS,EACvB,QAAQ,EAAE,SAAS,IACrB,CAAC;IACb,CAAC;IAED,OAAO,EAAE,OAAO,SAAA,EAAE,CAAC;AACvB,CAAC;AA1ED,sCA0EC"}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["src/react.tsx"],"names":[],"mappings":"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"}
package/src/oidc.ts CHANGED
@@ -1,12 +1,15 @@
1
- import { UserManager, type User } from "oidc-client-ts";
1
+ import { UserManager as OidcClientTsUserManager, type User as OidcClientTsUser } from "oidc-client-ts";
2
2
  import { id } from "tsafe/id";
3
3
  import { readExpirationTimeInJwt } from "./tools/readExpirationTimeInJwt";
4
4
  import { assert, type Equals } from "tsafe/assert";
5
5
  import { addQueryParamToUrl, retrieveQueryParamFromUrl } from "./tools/urlQueryParams";
6
6
  import { fnv1aHashToHex } from "./tools/fnv1aHashToHex";
7
7
  import { Deferred } from "./tools/Deferred";
8
+ import { decodeJwt } from "./tools/decodeJwt";
8
9
 
9
- export declare type Oidc = Oidc.LoggedIn | Oidc.NotLoggedIn;
10
+ export declare type Oidc<DecodedIdToken extends Record<string, unknown> = Record<string, unknown>> =
11
+ | Oidc.LoggedIn<DecodedIdToken>
12
+ | Oidc.NotLoggedIn;
10
13
 
11
14
  export declare namespace Oidc {
12
15
  export type Common = {
@@ -24,33 +27,46 @@ export declare namespace Oidc {
24
27
  }) => Promise<never>;
25
28
  };
26
29
 
27
- export type LoggedIn = Common & {
28
- isUserLoggedIn: true;
29
- renewTokens(): Promise<void>;
30
- getTokens: () => Tokens;
31
- subscribeToTokensChange: (onTokenChange: () => void) => { unsubscribe: () => void };
32
- logout: (
33
- params: { redirectTo: "home" | "current page" } | { redirectTo: "specific url"; url: string }
34
- ) => Promise<never>;
35
- };
30
+ export type LoggedIn<DecodedIdToken extends Record<string, unknown> = Record<string, unknown>> =
31
+ Common & {
32
+ isUserLoggedIn: true;
33
+ renewTokens(): Promise<void>;
34
+ getTokens: () => Tokens<DecodedIdToken>;
35
+ subscribeToTokensChange: (onTokenChange: () => void) => { unsubscribe: () => void };
36
+ logout: (
37
+ params:
38
+ | { redirectTo: "home" | "current page" }
39
+ | { redirectTo: "specific url"; url: string }
40
+ ) => Promise<never>;
41
+ };
36
42
 
37
- export type Tokens = {
38
- accessToken: string;
39
- accessTokenExpirationTime: number;
40
- idToken: string;
41
- refreshToken: string;
42
- refreshTokenExpirationTime: number;
43
- };
43
+ export type Tokens<DecodedIdToken extends Record<string, unknown> = Record<string, unknown>> =
44
+ Readonly<{
45
+ accessToken: string;
46
+ accessTokenExpirationTime: number;
47
+ idToken: string;
48
+ refreshToken: string;
49
+ refreshTokenExpirationTime: number;
50
+ decodedIdToken: DecodedIdToken;
51
+ }>;
44
52
  }
45
53
 
46
54
  const paramsToRetrieveFromSuccessfulLogin = ["code", "state", "session_state", "iss"] as const;
47
55
 
48
- /** @see: https://github.com/garronej/oidc-spa#option-1-usage-without-involving-the-ui-framework */
49
- export async function createOidc(params: {
56
+ export type ParamsOfCreateOidc<
57
+ DecodedIdToken extends Record<string, unknown> = Record<string, unknown>
58
+ > = {
50
59
  issuerUri: string;
51
60
  clientId: string;
52
61
  transformUrlBeforeRedirect?: (url: string) => string;
53
- getExtraQueryParams?: () => Record<string, string>;
62
+ /**
63
+ * Extra query params to be added on the login url.
64
+ * You can provide a function that returns those extra query params, it will be called
65
+ * when login() is called.
66
+ *
67
+ * Example: extraQueryParams: ()=> ({ ui_locales: "fr" })
68
+ */
69
+ extraQueryParams?: Record<string, string> | (() => Record<string, string>);
54
70
  /**
55
71
  * This parameter have to be provided if your App is not hosted at the origin of the subdomain.
56
72
  * For example if your site is hosted by navigating to `https://www.example.com`
@@ -64,15 +80,34 @@ export async function createOidc(params: {
64
80
  * you are supposed to have created in your `public/` directory.
65
81
  */
66
82
  publicUrl?: string;
67
- }): Promise<Oidc> {
83
+ decodedIdTokenSchema?: { parse: (data: unknown) => DecodedIdToken };
84
+ };
85
+
86
+ /** @see: https://github.com/garronej/oidc-spa#option-1-usage-without-involving-the-ui-framework */
87
+ export async function createOidc<
88
+ DecodedIdToken extends Record<string, unknown> = Record<string, unknown>
89
+ >(params: ParamsOfCreateOidc<DecodedIdToken>): Promise<Oidc<DecodedIdToken>> {
68
90
  const {
69
91
  issuerUri,
70
92
  clientId,
71
93
  transformUrlBeforeRedirect = url => url,
72
- getExtraQueryParams,
73
- publicUrl: publicUrl_params
94
+ extraQueryParams: extraQueryParamsOrGetter,
95
+ publicUrl: publicUrl_params,
96
+ decodedIdTokenSchema
74
97
  } = params;
75
98
 
99
+ const getExtraQueryParams = (() => {
100
+ if (typeof extraQueryParamsOrGetter === "function") {
101
+ return extraQueryParamsOrGetter;
102
+ }
103
+
104
+ if (extraQueryParamsOrGetter !== undefined) {
105
+ return () => extraQueryParamsOrGetter;
106
+ }
107
+
108
+ return undefined;
109
+ })();
110
+
76
111
  const publicUrl = (() => {
77
112
  if (publicUrl_params === undefined) {
78
113
  return window.location.origin;
@@ -88,7 +123,7 @@ export async function createOidc(params: {
88
123
  const configHash = fnv1aHashToHex(`${issuerUri} ${clientId}`);
89
124
  const configHashKey = "configHash";
90
125
 
91
- const userManager = new UserManager({
126
+ const oidcClientTsUserManager = new OidcClientTsUserManager({
92
127
  "authority": issuerUri,
93
128
  "client_id": clientId,
94
129
  "redirect_uri": "" /* provided when calling login */,
@@ -181,14 +216,14 @@ export async function createOidc(params: {
181
216
  document.addEventListener("visibilitychange", callback);
182
217
  }
183
218
 
184
- await userManager.signinRedirect({
219
+ await oidcClientTsUserManager.signinRedirect({
185
220
  redirect_uri,
186
221
  "redirectMethod": doesCurrentHrefRequiresAuth ? "replace" : "assign"
187
222
  });
188
223
  return new Promise<never>(() => {});
189
224
  };
190
225
 
191
- const currentTokens = await (async function getUser() {
226
+ const initialTokens = await (async function getUser() {
192
227
  read_successful_login_query_params: {
193
228
  let url = window.location.href;
194
229
 
@@ -215,7 +250,12 @@ export async function createOidc(params: {
215
250
  for (const name of paramsToRetrieveFromSuccessfulLogin) {
216
251
  const result = retrieveQueryParamFromUrl({ name, url });
217
252
 
218
- assert(result.wasPresent);
253
+ if (!result.wasPresent) {
254
+ if (name === "iss") {
255
+ continue;
256
+ }
257
+ throw new Error(`Missing query param: ${name}`);
258
+ }
219
259
 
220
260
  loginSuccessUrl = addQueryParamToUrl({
221
261
  "url": loginSuccessUrl,
@@ -228,33 +268,33 @@ export async function createOidc(params: {
228
268
 
229
269
  window.history.pushState(null, "", url);
230
270
 
231
- let user: User | undefined = undefined;
271
+ let oidcClientTsUser: OidcClientTsUser | undefined = undefined;
232
272
 
233
273
  try {
234
- user = await userManager.signinRedirectCallback(loginSuccessUrl);
274
+ oidcClientTsUser = await oidcClientTsUserManager.signinRedirectCallback(loginSuccessUrl);
235
275
  } catch {
236
276
  //NOTE: The user has likely pressed the back button just after logging in.
237
277
  return undefined;
238
278
  }
239
279
 
240
- return user;
280
+ return oidcClientTsUser;
241
281
  }
242
282
 
243
283
  restore_from_session: {
244
- const user = await userManager.getUser();
284
+ const oidcClientTsUser = await oidcClientTsUserManager.getUser();
245
285
 
246
- if (user === null) {
286
+ if (oidcClientTsUser === null) {
247
287
  break restore_from_session;
248
288
  }
249
289
 
250
290
  // The server might have restarted and the session might have been lost.
251
291
  try {
252
- await userManager.signinSilent();
292
+ await oidcClientTsUserManager.signinSilent();
253
293
  } catch {
254
294
  return undefined;
255
295
  }
256
296
 
257
- return user;
297
+ return oidcClientTsUser;
258
298
  }
259
299
 
260
300
  restore_from_http_only_cookie: {
@@ -310,7 +350,12 @@ export async function createOidc(params: {
310
350
  for (const name of paramsToRetrieveFromSuccessfulLogin) {
311
351
  const result = retrieveQueryParamFromUrl({ name, url });
312
352
 
313
- assert(result.wasPresent);
353
+ if (!result.wasPresent) {
354
+ if (name === "iss") {
355
+ continue;
356
+ }
357
+ throw new Error(`Missing query param: ${name}`);
358
+ }
314
359
 
315
360
  loginSuccessUrl = addQueryParamToUrl({
316
361
  "url": loginSuccessUrl,
@@ -324,7 +369,7 @@ export async function createOidc(params: {
324
369
 
325
370
  window.addEventListener("message", listener, false);
326
371
 
327
- userManager
372
+ oidcClientTsUserManager
328
373
  .signinSilent({ "silentRequestTimeoutInSeconds": timeoutDelayMs / 1000 })
329
374
  .catch(() => {
330
375
  /* error expected */
@@ -336,19 +381,24 @@ export async function createOidc(params: {
336
381
  break restore_from_http_only_cookie;
337
382
  }
338
383
 
339
- const user = await userManager.signinRedirectCallback(loginSuccessUrl);
384
+ const oidcClientTsUser = await oidcClientTsUserManager.signinRedirectCallback(
385
+ loginSuccessUrl
386
+ );
340
387
 
341
- return user;
388
+ return oidcClientTsUser;
342
389
  }
343
390
 
344
391
  return undefined;
345
392
  })().then(
346
- user => {
347
- if (user === undefined) {
393
+ oidcClientTsUser => {
394
+ if (oidcClientTsUser === undefined) {
348
395
  return undefined;
349
396
  }
350
397
 
351
- const tokens = userToTokens(user);
398
+ const tokens = oidcClientTsUserToTokens({
399
+ oidcClientTsUser,
400
+ decodedIdTokenSchema
401
+ });
352
402
 
353
403
  if (tokens.refreshTokenExpirationTime < tokens.accessTokenExpirationTime) {
354
404
  console.warn(
@@ -375,8 +425,8 @@ export async function createOidc(params: {
375
425
  }
376
426
  };
377
427
 
378
- if (currentTokens instanceof Error) {
379
- const error = currentTokens;
428
+ if (initialTokens instanceof Error) {
429
+ const error = initialTokens;
380
430
 
381
431
  console.error(`The OIDC server is down or misconfigured: ${error.message}`);
382
432
 
@@ -390,7 +440,7 @@ export async function createOidc(params: {
390
440
  });
391
441
  }
392
442
 
393
- if (currentTokens === undefined) {
443
+ if (initialTokens === undefined) {
394
444
  return id<Oidc.NotLoggedIn>({
395
445
  ...common,
396
446
  "isUserLoggedIn": false,
@@ -398,20 +448,16 @@ export async function createOidc(params: {
398
448
  });
399
449
  }
400
450
 
451
+ let currentTokens = initialTokens;
452
+
401
453
  const onTokenChanges = new Set<() => void>();
402
454
 
403
- const oidc = id<Oidc.LoggedIn>({
455
+ const oidc = id<Oidc.LoggedIn<DecodedIdToken>>({
404
456
  ...common,
405
457
  "isUserLoggedIn": true,
406
- "getTokens": () => ({
407
- "accessToken": currentTokens.accessToken,
408
- "idToken": currentTokens.idToken,
409
- "refreshToken": currentTokens.refreshToken,
410
- "refreshTokenExpirationTime": currentTokens.refreshTokenExpirationTime,
411
- "accessTokenExpirationTime": currentTokens.accessTokenExpirationTime
412
- }),
458
+ "getTokens": () => currentTokens,
413
459
  "logout": async params => {
414
- await userManager.signoutRedirect({
460
+ await oidcClientTsUserManager.signoutRedirect({
415
461
  "post_logout_redirect_uri": (() => {
416
462
  switch (params.redirectTo) {
417
463
  case "current page":
@@ -427,11 +473,24 @@ export async function createOidc(params: {
427
473
  return new Promise<never>(() => {});
428
474
  },
429
475
  "renewTokens": async () => {
430
- const user = await userManager.signinSilent();
476
+ const oidcClientTsUser = await oidcClientTsUserManager.signinSilent();
477
+
478
+ assert(oidcClientTsUser !== null);
479
+
480
+ const decodedIdTokenPropertyDescriptor = Object.getOwnPropertyDescriptor(
481
+ currentTokens,
482
+ "decodedIdToken"
483
+ );
431
484
 
432
- assert(user !== null);
485
+ assert(decodedIdTokenPropertyDescriptor !== undefined);
433
486
 
434
- Object.assign(currentTokens, userToTokens(user));
487
+ currentTokens = oidcClientTsUserToTokens({
488
+ oidcClientTsUser,
489
+ decodedIdTokenSchema
490
+ });
491
+
492
+ // NOTE: We do that to preserve the cache and the object reference.
493
+ Object.defineProperty(currentTokens, "decodedIdToken", decodedIdTokenPropertyDescriptor);
435
494
 
436
495
  onTokenChanges.forEach(onTokenChange => onTokenChange());
437
496
  },
@@ -465,12 +524,17 @@ export async function createOidc(params: {
465
524
  return oidc;
466
525
  }
467
526
 
468
- function userToTokens(user: User): Oidc.Tokens {
469
- const accessToken = user.access_token;
527
+ function oidcClientTsUserToTokens<DecodedIdToken extends Record<string, unknown>>(params: {
528
+ oidcClientTsUser: OidcClientTsUser;
529
+ decodedIdTokenSchema?: { parse: (data: unknown) => DecodedIdToken };
530
+ }): Oidc.Tokens<DecodedIdToken> {
531
+ const { oidcClientTsUser, decodedIdTokenSchema } = params;
532
+
533
+ const accessToken = oidcClientTsUser.access_token;
470
534
 
471
535
  const accessTokenExpirationTime = (() => {
472
536
  read_from_metadata: {
473
- const { expires_at } = user;
537
+ const { expires_at } = oidcClientTsUser;
474
538
 
475
539
  if (expires_at === undefined) {
476
540
  break read_from_metadata;
@@ -492,7 +556,7 @@ function userToTokens(user: User): Oidc.Tokens {
492
556
  assert(false, "Failed to get access token expiration time");
493
557
  })();
494
558
 
495
- const refreshToken = user.refresh_token;
559
+ const refreshToken = oidcClientTsUser.refresh_token;
496
560
 
497
561
  assert(refreshToken !== undefined, "No refresh token provided by the oidc server");
498
562
 
@@ -510,15 +574,48 @@ function userToTokens(user: User): Oidc.Tokens {
510
574
  assert(false, "Failed to get refresh token expiration time");
511
575
  })();
512
576
 
513
- const idToken = user.id_token;
577
+ const idToken = oidcClientTsUser.id_token;
514
578
 
515
579
  assert(idToken !== undefined, "No id token provided by the oidc server");
516
580
 
517
- return {
581
+ const tokens: Oidc.Tokens<DecodedIdToken> = {
518
582
  accessToken,
519
583
  accessTokenExpirationTime,
520
584
  refreshToken,
521
585
  refreshTokenExpirationTime,
522
- idToken
586
+ idToken,
587
+ "decodedIdToken": null as any
523
588
  };
589
+
590
+ let cache:
591
+ | {
592
+ idToken: string;
593
+ decodedIdToken: DecodedIdToken;
594
+ }
595
+ | undefined = undefined;
596
+
597
+ Object.defineProperty(tokens, "decodedIdToken", {
598
+ "get": function (this: Oidc.Tokens<DecodedIdToken>) {
599
+ if (cache !== undefined && cache.idToken === this.idToken) {
600
+ return cache.decodedIdToken;
601
+ }
602
+
603
+ let decodedIdToken = decodeJwt(this.idToken) as DecodedIdToken;
604
+
605
+ if (decodedIdTokenSchema !== undefined) {
606
+ decodedIdToken = decodedIdTokenSchema.parse(decodedIdToken);
607
+ }
608
+
609
+ cache = {
610
+ "idToken": this.idToken,
611
+ decodedIdToken
612
+ };
613
+
614
+ return decodedIdToken;
615
+ },
616
+ "configurable": true,
617
+ "enumerable": true
618
+ });
619
+
620
+ return tokens;
524
621
  }