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/src/react.tsx CHANGED
@@ -1,23 +1,48 @@
1
- import {
2
- useEffect,
3
- useState,
4
- createContext,
5
- useContext,
6
- useReducer,
7
- useMemo,
8
- type ReactNode
9
- } from "react";
10
- import { createOidc, type Oidc } from "./oidc";
1
+ import { useEffect, useState, createContext, useContext, useReducer, type ReactNode } from "react";
2
+ import { createOidc, type ParamsOfCreateOidc, type Oidc } from "./oidc";
11
3
  import { assert } from "tsafe/assert";
12
- import { decodeJwt } from "./tools/decodeJwt";
13
4
  import { id } from "tsafe/id";
5
+ import { useGuaranteedMemo } from "./tools/powerhooks/useGuaranteedMemo";
14
6
 
15
- const oidcContext = createContext<Oidc | undefined>(undefined);
7
+ export type OidcReact<DecodedIdToken extends Record<string, unknown>> =
8
+ | OidcReact.NotLoggedIn
9
+ | OidcReact.LoggedIn<DecodedIdToken>;
10
+
11
+ export namespace OidcReact {
12
+ export type Common = Oidc.Common;
13
+
14
+ export type NotLoggedIn = Common & {
15
+ isUserLoggedIn: false;
16
+ login: Oidc.NotLoggedIn["login"];
17
+ oidcTokens?: never;
18
+ logout?: never;
19
+ };
20
+
21
+ export type LoggedIn<DecodedIdToken extends Record<string, unknown>> = Common & {
22
+ isUserLoggedIn: true;
23
+ oidcTokens: Oidc.Tokens<DecodedIdToken>;
24
+ logout: Oidc.LoggedIn["logout"];
25
+ renewTokens: Oidc.LoggedIn["renewTokens"];
26
+ login?: never;
27
+ };
28
+ }
29
+
30
+ const oidcContext = createContext<
31
+ | {
32
+ oidc: Oidc;
33
+ decodedIdTokenSchema: ParamsOfCreateOidc["decodedIdTokenSchema"];
34
+ }
35
+ | undefined
36
+ >(undefined);
16
37
 
17
38
  /** @see: https://github.com/garronej/oidc-spa#option-2-usage-directly-within-react */
18
- export function createOidcProvider(params: Parameters<typeof createOidc>[0]) {
39
+ export function createReactOidc<
40
+ DecodedIdToken extends Record<string, unknown> = Record<string, unknown>
41
+ >(params: ParamsOfCreateOidc<DecodedIdToken>) {
19
42
  const prOidc = createOidc(params);
20
43
 
44
+ const { decodedIdTokenSchema } = params;
45
+
21
46
  function OidcProvider(props: { fallback?: ReactNode; children: ReactNode }) {
22
47
  const { children, fallback } = props;
23
48
 
@@ -31,50 +56,25 @@ export function createOidcProvider(params: Parameters<typeof createOidc>[0]) {
31
56
  return <>{fallback === undefined ? null : fallback}</>;
32
57
  }
33
58
 
34
- return <oidcContext.Provider value={oidc}>{children}</oidcContext.Provider>;
59
+ return (
60
+ <oidcContext.Provider value={{ oidc, decodedIdTokenSchema }}>
61
+ {children}
62
+ </oidcContext.Provider>
63
+ );
35
64
  }
36
65
 
37
- return { OidcProvider, prOidc };
38
- }
39
-
40
- export type ReactiveOidc<DecodedIdToken extends Record<string, unknown>> =
41
- | ReactiveOidc.NotLoggedIn
42
- | ReactiveOidc.LoggedIn<DecodedIdToken>;
43
-
44
- export namespace ReactiveOidc {
45
- export type Common = Oidc.Common;
46
-
47
- export type NotLoggedIn = Common & {
48
- isUserLoggedIn: false;
49
- login: Oidc.NotLoggedIn["login"];
50
- oidcTokens: undefined;
51
- logout: undefined;
52
- };
53
-
54
- export type LoggedIn<DecodedIdToken extends Record<string, unknown>> = Common & {
55
- isUserLoggedIn: true;
56
- oidcTokens: ReturnType<Oidc.LoggedIn["getTokens"]> & {
57
- decodedIdToken: DecodedIdToken;
58
- };
59
- logout: Oidc.LoggedIn["logout"];
60
- renewTokens: Oidc.LoggedIn["renewTokens"];
61
- login: undefined;
62
- };
63
- }
64
-
65
- export function createUseOidc<
66
- DecodedOidcIdToken extends Record<string, unknown> = Record<string, unknown>
67
- >(params?: { decodedIdTokenSchema?: { parse: (data: unknown) => DecodedOidcIdToken } }) {
68
- const { decodedIdTokenSchema } = params ?? {};
69
-
70
- function useOidc(params?: { assertUserLoggedIn: false }): ReactiveOidc<DecodedOidcIdToken>;
71
- function useOidc(params: { assertUserLoggedIn: true }): ReactiveOidc.LoggedIn<DecodedOidcIdToken>;
72
- function useOidc(params?: { assertUserLoggedIn: boolean }): ReactiveOidc<DecodedOidcIdToken> {
66
+ function useOidc(params?: { assertUserLoggedIn: false }): OidcReact<DecodedIdToken>;
67
+ function useOidc(params: { assertUserLoggedIn: true }): OidcReact.LoggedIn<DecodedIdToken>;
68
+ function useOidc(params?: { assertUserLoggedIn: boolean }): OidcReact<DecodedIdToken> {
73
69
  const { assertUserLoggedIn = false } = params ?? {};
74
70
 
75
- const oidc = useContext(oidcContext);
71
+ const { oidc, decodedIdTokenSchema: decodedIdTokenSchema_context } = (function useClosure() {
72
+ const context = useContext(oidcContext);
73
+
74
+ assert(context !== undefined, "You must use useOidc inside a OidcProvider");
76
75
 
77
- assert(oidc !== undefined, "You must use useOidc inside a OidcProvider");
76
+ return context;
77
+ })();
78
78
 
79
79
  const [, forceUpdate] = useReducer(() => [], []);
80
80
 
@@ -94,46 +94,79 @@ export function createUseOidc<
94
94
  );
95
95
  }
96
96
 
97
- const tokens = oidc.isUserLoggedIn ? oidc.getTokens() : undefined;
98
-
99
- const decodedIdToken = useMemo(() => {
100
- if (tokens?.idToken === undefined) {
101
- return undefined;
102
- }
103
-
104
- const decodedIdToken = decodeJwt(tokens.idToken) as DecodedOidcIdToken;
105
-
106
- if (decodedIdTokenSchema !== undefined) {
107
- decodedIdTokenSchema.parse(decodeJwt(tokens.idToken));
108
- }
109
-
110
- return decodedIdToken;
111
- }, [tokens?.idToken]);
112
-
113
- const common: ReactiveOidc.Common = {
97
+ const { oidcTokens } = (function useClosure() {
98
+ const tokens = oidc.isUserLoggedIn ? oidc.getTokens() : undefined;
99
+
100
+ const oidcTokens = useGuaranteedMemo(() => {
101
+ if (tokens === undefined) {
102
+ return undefined;
103
+ }
104
+
105
+ const oidcTokens: Oidc.Tokens<DecodedIdToken> = {
106
+ "accessToken": tokens.accessToken,
107
+ "accessTokenExpirationTime": tokens.accessTokenExpirationTime,
108
+ "idToken": tokens.idToken,
109
+ "refreshToken": tokens.refreshToken,
110
+ "refreshTokenExpirationTime": tokens.refreshTokenExpirationTime,
111
+ "decodedIdToken": null as any
112
+ };
113
+
114
+ let cache: { decodedIdToken: Record<string, unknown> } | undefined = undefined;
115
+
116
+ Object.defineProperty(oidcTokens, "decodedIdToken", {
117
+ "get": () => {
118
+ if (cache !== undefined) {
119
+ return cache.decodedIdToken;
120
+ }
121
+
122
+ let { decodedIdToken } = tokens;
123
+
124
+ if (
125
+ decodedIdTokenSchema !== undefined &&
126
+ decodedIdTokenSchema !== decodedIdTokenSchema_context
127
+ ) {
128
+ decodedIdToken = decodedIdTokenSchema.parse(decodedIdToken);
129
+ }
130
+
131
+ cache = { decodedIdToken };
132
+
133
+ return decodedIdToken;
134
+ }
135
+ });
136
+
137
+ return oidcTokens;
138
+ }, [
139
+ tokens?.accessToken,
140
+ tokens?.accessTokenExpirationTime,
141
+ tokens?.idToken,
142
+ tokens?.refreshToken,
143
+ tokens?.refreshTokenExpirationTime
144
+ ]);
145
+
146
+ return { oidcTokens };
147
+ })();
148
+
149
+ const common: OidcReact.Common = {
114
150
  "params": oidc.params
115
151
  };
116
152
 
117
153
  return oidc.isUserLoggedIn
118
- ? id<ReactiveOidc.LoggedIn<DecodedOidcIdToken>>({
119
- ...common,
120
- "isUserLoggedIn": true,
121
- "oidcTokens": {
122
- ...tokens!,
123
- "decodedIdToken": decodedIdToken!
124
- },
125
- "logout": oidc.logout,
126
- "renewTokens": oidc.renewTokens,
127
- "login": undefined
128
- })
129
- : id<ReactiveOidc.NotLoggedIn>({
154
+ ? id<OidcReact.LoggedIn<DecodedIdToken>>(
155
+ (assert(oidcTokens !== undefined),
156
+ {
157
+ ...common,
158
+ "isUserLoggedIn": true,
159
+ oidcTokens,
160
+ "logout": oidc.logout,
161
+ "renewTokens": oidc.renewTokens
162
+ })
163
+ )
164
+ : id<OidcReact.NotLoggedIn>({
130
165
  ...common,
131
166
  "isUserLoggedIn": false,
132
- "login": oidc.login,
133
- "oidcTokens": undefined,
134
- "logout": undefined
167
+ "login": oidc.login
135
168
  });
136
169
  }
137
170
 
138
- return { useOidc };
171
+ return { OidcProvider, useOidc, prOidc };
139
172
  }
@@ -0,0 +1,18 @@
1
+ import { useRef } from "react";
2
+
3
+ export function useGuaranteedMemo<T>(fn: () => T, deps: readonly any[]): T {
4
+ const ref = useRef<{ v: T; prevDeps: any[] }>();
5
+
6
+ if (
7
+ !ref.current ||
8
+ deps.length !== ref.current.prevDeps.length ||
9
+ ref.current.prevDeps.map((v, i) => v === deps[i]).indexOf(false) >= 0
10
+ ) {
11
+ ref.current = {
12
+ "v": fn(),
13
+ "prevDeps": [...deps]
14
+ };
15
+ }
16
+
17
+ return ref.current.v;
18
+ }
@@ -0,0 +1 @@
1
+ export declare function useGuaranteedMemo<T>(fn: () => T, deps: readonly any[]): T;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __read = (this && this.__read) || function (o, n) {
3
+ var m = typeof Symbol === "function" && o[Symbol.iterator];
4
+ if (!m) return o;
5
+ var i = m.call(o), r, ar = [], e;
6
+ try {
7
+ while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
8
+ }
9
+ catch (error) { e = { error: error }; }
10
+ finally {
11
+ try {
12
+ if (r && !r.done && (m = i["return"])) m.call(i);
13
+ }
14
+ finally { if (e) throw e.error; }
15
+ }
16
+ return ar;
17
+ };
18
+ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
19
+ if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
20
+ if (ar || !(i in from)) {
21
+ if (!ar) ar = Array.prototype.slice.call(from, 0, i);
22
+ ar[i] = from[i];
23
+ }
24
+ }
25
+ return to.concat(ar || Array.prototype.slice.call(from));
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.useGuaranteedMemo = void 0;
29
+ var react_1 = require("react");
30
+ function useGuaranteedMemo(fn, deps) {
31
+ var ref = (0, react_1.useRef)();
32
+ if (!ref.current ||
33
+ deps.length !== ref.current.prevDeps.length ||
34
+ ref.current.prevDeps.map(function (v, i) { return v === deps[i]; }).indexOf(false) >= 0) {
35
+ ref.current = {
36
+ "v": fn(),
37
+ "prevDeps": __spreadArray([], __read(deps), false)
38
+ };
39
+ }
40
+ return ref.current.v;
41
+ }
42
+ exports.useGuaranteedMemo = useGuaranteedMemo;
43
+ //# sourceMappingURL=useGuaranteedMemo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useGuaranteedMemo.js","sourceRoot":"","sources":["../../src/tools/powerhooks/useGuaranteedMemo.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,+BAA+B;AAE/B,SAAgB,iBAAiB,CAAI,EAAW,EAAE,IAAoB;IAClE,IAAM,GAAG,GAAG,IAAA,cAAM,GAA6B,CAAC;IAEhD,IACI,CAAC,GAAG,CAAC,OAAO;QACZ,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM;QAC3C,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,UAAC,CAAC,EAAE,CAAC,IAAK,OAAA,CAAC,KAAK,IAAI,CAAC,CAAC,CAAC,EAAb,CAAa,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EACvE,CAAC;QACC,GAAG,CAAC,OAAO,GAAG;YACV,GAAG,EAAE,EAAE,EAAE;YACT,UAAU,2BAAM,IAAI,SAAC;SACxB,CAAC;IACN,CAAC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AACzB,CAAC;AAfD,8CAeC"}