@forward-software/react-auth 1.1.0 → 2.0.1

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.
@@ -1 +0,0 @@
1
- {"version":3,"file":"react-auth.esm.js","sources":["../src/types/deferred.ts","../src/types/eventEmitter.ts","../src/types/index.ts","../src/index.tsx"],"sourcesContent":["export class Deferred<T> {\n private promise: Promise<T>;\n\n public resolve!: (value: T | PromiseLike<T>) => void;\n\n public reject!: (reason?: any) => void;\n\n constructor() {\n this.promise = new Promise<T>((resolve, reject) => {\n this.reject = reject;\n this.resolve = resolve;\n });\n }\n\n public getPromise(): Promise<T> {\n return this.promise;\n }\n}\n","type EventsMap = Record<string, any>;\n\nexport type EventKey<T extends EventsMap> = string & keyof T;\n\nexport type EventReceiver<T> = (params: T) => void;\n\ninterface Emitter<T extends EventsMap> {\n on<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;\n off<K extends EventKey<T>>(eventName: K, fn: EventReceiver<T[K]>): void;\n emit<K extends EventKey<T>>(eventName: K, params: T[K]): void;\n}\n\n// TODO: Improve -> `listeners` are unbounded -- don't use this in practice!\nexport function createEventEmitter<T extends EventsMap>(): Emitter<T> {\n const listeners: {\n [K in keyof EventsMap]?: Array<(p: EventsMap[K]) => void>;\n } = {};\n\n return {\n on(key, fn) {\n listeners[key] = (listeners[key] || []).concat(fn);\n },\n off(key, fn) {\n listeners[key] = (listeners[key] || []).filter(f => f !== fn);\n },\n emit(key, data) {\n (listeners[key] || []).forEach(function(fn) {\n try {\n fn(data);\n } catch {}\n });\n },\n };\n}\n","import { Deferred } from './deferred';\nimport { createEventEmitter, EventKey, EventReceiver } from './eventEmitter';\n\ntype AuthTokens = {};\n\ntype AuthCredentials = {};\n\ntype AuthEventsMap<E extends Error> = {\n initSuccess: undefined;\n\n initFailed: E;\n\n loginStarted: undefined;\n\n loginSuccess: undefined;\n\n loginFailed: E;\n\n refreshStarted: undefined;\n\n refreshSuccess: undefined;\n\n refreshFailed: E;\n\n logoutStarted: undefined;\n\n logoutSuccess: undefined;\n\n logoutFailed: E;\n};\n\ntype SubscribeFn = () => void;\n\ntype UnsubscribeFn = () => boolean;\n\ntype AuthClientState<T> = {\n isAuthenticated: boolean;\n\n isInitialized: boolean;\n\n tokens: Partial<T>;\n};\n\nexport abstract class BaseAuthClient<\n T = AuthTokens,\n C = AuthCredentials,\n E extends Error = Error\n> {\n private _state: Readonly<AuthClientState<T>> = {\n isAuthenticated: false,\n isInitialized: false,\n tokens: {},\n };\n\n // refresh queue - used to avoid concurrency issue during Token refresh\n private refreshQ: Array<Deferred<boolean>> = [];\n\n private eventEmitter = createEventEmitter<AuthEventsMap<E>>();\n\n private subscribers: Set<SubscribeFn> = new Set();\n\n //\n // Getters\n //\n\n public get isInitialized() {\n return this._state.isInitialized;\n }\n\n public get isAuthenticated() {\n return this._state.isAuthenticated;\n }\n\n public get tokens() {\n return this._state.tokens;\n }\n\n //\n // Public methods\n //\n\n public async init(): Promise<boolean> {\n try {\n await this.onInit();\n\n this.setState({\n isInitialized: true,\n });\n\n this.emit('initSuccess', undefined);\n } catch (error) {\n this.setState({\n isInitialized: false,\n });\n\n this.emit('initFailed', error as E);\n }\n\n await this.onPostInit?.();\n\n return this.isInitialized;\n }\n\n public async login(credentials?: C): Promise<boolean> {\n this.emit('loginStarted', undefined);\n\n await this.onPreLogin?.();\n\n let isSuccess: boolean = false;\n\n try {\n const tokens = await this.onLogin(credentials);\n\n this.setState({\n isAuthenticated: true,\n tokens,\n });\n\n this.emit('loginSuccess', undefined);\n\n isSuccess = true;\n } catch (err) {\n this.setState({\n isAuthenticated: false,\n tokens: {},\n });\n\n this.emit('loginFailed', err as E);\n\n isSuccess = false;\n }\n\n await this.onPostLogin?.(isSuccess);\n\n return this.isAuthenticated;\n }\n\n public async refresh(minValidity?: number): Promise<boolean> {\n const deferred = new Deferred<boolean>();\n\n this.runRefresh(deferred, minValidity);\n\n return deferred.getPromise();\n }\n\n public async logout(): Promise<void> {\n this.emit('logoutStarted', undefined);\n\n await this.onPreLogout?.();\n\n let isSuccess: boolean = false;\n\n try {\n await this.onLogout();\n\n this.setState({\n isAuthenticated: false,\n tokens: {},\n });\n\n this.emit('logoutSuccess', undefined);\n\n isSuccess = true;\n } catch (err) {\n this.emit('logoutFailed', err as E);\n\n isSuccess = false;\n }\n\n await this.onPostLogout?.(isSuccess);\n }\n\n public on<K extends EventKey<AuthEventsMap<E>>>(\n eventName: K,\n listener: EventReceiver<AuthEventsMap<E>[K]>\n ): void {\n this.eventEmitter.on(eventName, listener);\n }\n\n public off<K extends EventKey<AuthEventsMap<E>>>(\n eventName: K,\n listener: EventReceiver<AuthEventsMap<E>[K]>\n ): void {\n this.eventEmitter.off(eventName, listener);\n }\n\n // Should be declared like this to avoid binding issues when used by useSyncExternalStore\n public subscribe = (subscription: SubscribeFn): UnsubscribeFn => {\n this.subscribers.add(subscription);\n\n return () => this.subscribers.delete(subscription);\n };\n\n // Should be declared like this to avoid binding issues when used by useSyncExternalStore\n public getSnapshot = (): AuthClientState<T> => {\n return this._state;\n };\n\n //\n // Protected methods\n //\n\n protected setState(stateUpdate: Partial<AuthClientState<T>>): void {\n this._state = {\n ...this._state,\n ...stateUpdate,\n };\n\n this.notifySubscribers();\n }\n\n //\n // Private methods\n //\n\n private async runRefresh(\n deferred: Deferred<boolean>,\n minValidity?: number\n ): Promise<void> {\n // Add deferred Promise to refresh queue\n this.refreshQ.push(deferred);\n\n // If refresh queue already has promises enqueued do not attempt a new refresh - one is already in progress\n if (this.refreshQ.length !== 1) {\n return;\n }\n\n this.emit('refreshStarted', undefined);\n\n await this.onPreRefresh?.();\n\n let isAuthenticated: boolean = false;\n let tokens: Partial<T> = {};\n\n try {\n tokens = await this.onRefresh(minValidity);\n isAuthenticated = true;\n\n this.emit('refreshSuccess', undefined);\n } catch (err) {\n isAuthenticated = false;\n\n this.emit('refreshFailed', err as E);\n }\n\n this.setState({\n isAuthenticated,\n tokens,\n });\n\n await this.onPostRefresh?.(isAuthenticated);\n\n for (let p = this.refreshQ.pop(); p != null; p = this.refreshQ.pop()) {\n p.resolve(isAuthenticated);\n }\n }\n\n private emit<K extends EventKey<AuthEventsMap<E>>>(\n eventName: K,\n error: AuthEventsMap<E>[K]\n ): void {\n this.eventEmitter.emit(eventName, error);\n }\n\n private notifySubscribers() {\n this.subscribers.forEach(s => {\n try {\n s();\n } catch {}\n });\n }\n\n //\n // Abstract methods\n //\n\n protected abstract onInit(): Promise<void>;\n\n protected onPostInit?(): Promise<void>;\n\n protected onPreLogin?(): Promise<void>;\n\n protected abstract onLogin(credentials?: C): Promise<T>;\n\n protected onPostLogin?(isSuccess: boolean): Promise<void>;\n\n protected onPreRefresh?(): Promise<void>;\n\n protected abstract onRefresh(minValidity?: number): Promise<T>;\n\n protected onPostRefresh?(isSuccess: boolean): Promise<void>;\n\n protected onPreLogout?(): Promise<void>;\n\n protected abstract onLogout(): Promise<void>;\n\n protected onPostLogout?(isSuccess: boolean): Promise<void>;\n}\n","import React, { createContext, useContext, useEffect, useState } from 'react';\nimport { useSyncExternalStore } from 'use-sync-external-store/shim';\n\nimport { BaseAuthClient } from './types';\n\n/**\n * Props that can be passed to AuthProvider\n */\nexport type AuthProviderProps = {\n children?: React.ReactNode;\n\n /**\n * An optional component to display if AuthClient initialization failed.\n */\n ErrorComponent?: JSX.Element;\n\n /**\n * An optional component to display while AuthClient instance is being initialized.\n */\n LoadingComponent?: JSX.Element;\n};\n\ntype AuthProviderState = {\n isAuthenticated: boolean;\n\n isInitialized: boolean;\n};\n\ntype AuthContext<C extends BaseAuthClient> = AuthProviderState & {\n authClient: C;\n};\n\nexport function createAuth<C extends BaseAuthClient>(authClient: C) {\n // Create a React context containing a BaseAuthClient instance.\n const authContext = createContext<AuthContext<C> | null>(null);\n\n // Create the React Context Provider for the BaseAuthClient instance.\n const AuthProvider: React.FC<AuthProviderProps> = ({\n children,\n ErrorComponent,\n LoadingComponent,\n }) => {\n const [isInitFailed, setInitFailed] = useState(false);\n const { isAuthenticated, isInitialized } = useSyncExternalStore(\n authClient.subscribe,\n authClient.getSnapshot\n );\n\n useEffect(() => {\n async function initAuthClient() {\n // Call init function\n const initSuccess = await authClient.init();\n setInitFailed(!initSuccess);\n }\n\n // Init AuthClient\n initAuthClient();\n }, []);\n\n if (!!ErrorComponent && isInitFailed) {\n return ErrorComponent;\n }\n\n if (!!LoadingComponent && !isInitialized) {\n return LoadingComponent;\n }\n\n return (\n <authContext.Provider\n value={{\n authClient,\n isAuthenticated,\n isInitialized,\n }}\n >\n {children}\n </authContext.Provider>\n );\n };\n\n // Retrieve the AuthClient from the current context\n const useAuthClient = function(): C {\n const ctx = useContext(authContext);\n\n if (!ctx) {\n throw new Error('useAuthClient hook should be used inside AuthProvider');\n }\n\n return ctx.authClient;\n };\n\n return {\n AuthProvider,\n useAuthClient,\n };\n}\n\nexport { BaseAuthClient };\n"],"names":["Deferred","promise","Promise","resolve","reject","getPromise","createEventEmitter","listeners","on","key","fn","concat","off","filter","f","emit","data","forEach","BaseAuthClient","isAuthenticated","isInitialized","tokens","Set","subscription","subscribers","add","_state","init","onInit","setState","undefined","onPostInit","login","credentials","onPreLogin","isSuccess","onLogin","onPostLogin","refresh","minValidity","deferred","runRefresh","logout","onPreLogout","onLogout","onPostLogout","eventName","listener","eventEmitter","stateUpdate","notifySubscribers","refreshQ","push","length","onPreRefresh","onRefresh","onPostRefresh","p","pop","error","s","createAuth","authClient","authContext","createContext","AuthProvider","children","ErrorComponent","LoadingComponent","useState","isInitFailed","setInitFailed","useSyncExternalStore","subscribe","getSnapshot","useEffect","initAuthClient","initSuccess","React","Provider","value","useAuthClient","ctx","useContext","Error"],"mappings":"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"}
@@ -1,7 +0,0 @@
1
- export declare class Deferred<T> {
2
- private promise;
3
- resolve: (value: T | PromiseLike<T>) => void;
4
- reject: (reason?: any) => void;
5
- constructor();
6
- getPromise(): Promise<T>;
7
- }
@@ -1,56 +0,0 @@
1
- import { EventKey, EventReceiver } from './eventEmitter';
2
- declare type AuthTokens = {};
3
- declare type AuthCredentials = {};
4
- declare type AuthEventsMap<E extends Error> = {
5
- initSuccess: undefined;
6
- initFailed: E;
7
- loginStarted: undefined;
8
- loginSuccess: undefined;
9
- loginFailed: E;
10
- refreshStarted: undefined;
11
- refreshSuccess: undefined;
12
- refreshFailed: E;
13
- logoutStarted: undefined;
14
- logoutSuccess: undefined;
15
- logoutFailed: E;
16
- };
17
- declare type SubscribeFn = () => void;
18
- declare type UnsubscribeFn = () => boolean;
19
- declare type AuthClientState<T> = {
20
- isAuthenticated: boolean;
21
- isInitialized: boolean;
22
- tokens: Partial<T>;
23
- };
24
- export declare abstract class BaseAuthClient<T = AuthTokens, C = AuthCredentials, E extends Error = Error> {
25
- private _state;
26
- private refreshQ;
27
- private eventEmitter;
28
- private subscribers;
29
- get isInitialized(): boolean;
30
- get isAuthenticated(): boolean;
31
- get tokens(): Partial<T>;
32
- init(): Promise<boolean>;
33
- login(credentials?: C): Promise<boolean>;
34
- refresh(minValidity?: number): Promise<boolean>;
35
- logout(): Promise<void>;
36
- on<K extends EventKey<AuthEventsMap<E>>>(eventName: K, listener: EventReceiver<AuthEventsMap<E>[K]>): void;
37
- off<K extends EventKey<AuthEventsMap<E>>>(eventName: K, listener: EventReceiver<AuthEventsMap<E>[K]>): void;
38
- subscribe: (subscription: SubscribeFn) => UnsubscribeFn;
39
- getSnapshot: () => AuthClientState<T>;
40
- protected setState(stateUpdate: Partial<AuthClientState<T>>): void;
41
- private runRefresh;
42
- private emit;
43
- private notifySubscribers;
44
- protected abstract onInit(): Promise<void>;
45
- protected onPostInit?(): Promise<void>;
46
- protected onPreLogin?(): Promise<void>;
47
- protected abstract onLogin(credentials?: C): Promise<T>;
48
- protected onPostLogin?(isSuccess: boolean): Promise<void>;
49
- protected onPreRefresh?(): Promise<void>;
50
- protected abstract onRefresh(minValidity?: number): Promise<T>;
51
- protected onPostRefresh?(isSuccess: boolean): Promise<void>;
52
- protected onPreLogout?(): Promise<void>;
53
- protected abstract onLogout(): Promise<void>;
54
- protected onPostLogout?(isSuccess: boolean): Promise<void>;
55
- }
56
- export {};
package/src/index.tsx DELETED
@@ -1,98 +0,0 @@
1
- import React, { createContext, useContext, useEffect, useState } from 'react';
2
- import { useSyncExternalStore } from 'use-sync-external-store/shim';
3
-
4
- import { BaseAuthClient } from './types';
5
-
6
- /**
7
- * Props that can be passed to AuthProvider
8
- */
9
- export type AuthProviderProps = {
10
- children?: React.ReactNode;
11
-
12
- /**
13
- * An optional component to display if AuthClient initialization failed.
14
- */
15
- ErrorComponent?: JSX.Element;
16
-
17
- /**
18
- * An optional component to display while AuthClient instance is being initialized.
19
- */
20
- LoadingComponent?: JSX.Element;
21
- };
22
-
23
- type AuthProviderState = {
24
- isAuthenticated: boolean;
25
-
26
- isInitialized: boolean;
27
- };
28
-
29
- type AuthContext<C extends BaseAuthClient> = AuthProviderState & {
30
- authClient: C;
31
- };
32
-
33
- export function createAuth<C extends BaseAuthClient>(authClient: C) {
34
- // Create a React context containing a BaseAuthClient instance.
35
- const authContext = createContext<AuthContext<C> | null>(null);
36
-
37
- // Create the React Context Provider for the BaseAuthClient instance.
38
- const AuthProvider: React.FC<AuthProviderProps> = ({
39
- children,
40
- ErrorComponent,
41
- LoadingComponent,
42
- }) => {
43
- const [isInitFailed, setInitFailed] = useState(false);
44
- const { isAuthenticated, isInitialized } = useSyncExternalStore(
45
- authClient.subscribe,
46
- authClient.getSnapshot
47
- );
48
-
49
- useEffect(() => {
50
- async function initAuthClient() {
51
- // Call init function
52
- const initSuccess = await authClient.init();
53
- setInitFailed(!initSuccess);
54
- }
55
-
56
- // Init AuthClient
57
- initAuthClient();
58
- }, []);
59
-
60
- if (!!ErrorComponent && isInitFailed) {
61
- return ErrorComponent;
62
- }
63
-
64
- if (!!LoadingComponent && !isInitialized) {
65
- return LoadingComponent;
66
- }
67
-
68
- return (
69
- <authContext.Provider
70
- value={{
71
- authClient,
72
- isAuthenticated,
73
- isInitialized,
74
- }}
75
- >
76
- {children}
77
- </authContext.Provider>
78
- );
79
- };
80
-
81
- // Retrieve the AuthClient from the current context
82
- const useAuthClient = function(): C {
83
- const ctx = useContext(authContext);
84
-
85
- if (!ctx) {
86
- throw new Error('useAuthClient hook should be used inside AuthProvider');
87
- }
88
-
89
- return ctx.authClient;
90
- };
91
-
92
- return {
93
- AuthProvider,
94
- useAuthClient,
95
- };
96
- }
97
-
98
- export { BaseAuthClient };
@@ -1,18 +0,0 @@
1
- export class Deferred<T> {
2
- private promise: Promise<T>;
3
-
4
- public resolve!: (value: T | PromiseLike<T>) => void;
5
-
6
- public reject!: (reason?: any) => void;
7
-
8
- constructor() {
9
- this.promise = new Promise<T>((resolve, reject) => {
10
- this.reject = reject;
11
- this.resolve = resolve;
12
- });
13
- }
14
-
15
- public getPromise(): Promise<T> {
16
- return this.promise;
17
- }
18
- }
@@ -1,298 +0,0 @@
1
- import { Deferred } from './deferred';
2
- import { createEventEmitter, EventKey, EventReceiver } from './eventEmitter';
3
-
4
- type AuthTokens = {};
5
-
6
- type AuthCredentials = {};
7
-
8
- type AuthEventsMap<E extends Error> = {
9
- initSuccess: undefined;
10
-
11
- initFailed: E;
12
-
13
- loginStarted: undefined;
14
-
15
- loginSuccess: undefined;
16
-
17
- loginFailed: E;
18
-
19
- refreshStarted: undefined;
20
-
21
- refreshSuccess: undefined;
22
-
23
- refreshFailed: E;
24
-
25
- logoutStarted: undefined;
26
-
27
- logoutSuccess: undefined;
28
-
29
- logoutFailed: E;
30
- };
31
-
32
- type SubscribeFn = () => void;
33
-
34
- type UnsubscribeFn = () => boolean;
35
-
36
- type AuthClientState<T> = {
37
- isAuthenticated: boolean;
38
-
39
- isInitialized: boolean;
40
-
41
- tokens: Partial<T>;
42
- };
43
-
44
- export abstract class BaseAuthClient<
45
- T = AuthTokens,
46
- C = AuthCredentials,
47
- E extends Error = Error
48
- > {
49
- private _state: Readonly<AuthClientState<T>> = {
50
- isAuthenticated: false,
51
- isInitialized: false,
52
- tokens: {},
53
- };
54
-
55
- // refresh queue - used to avoid concurrency issue during Token refresh
56
- private refreshQ: Array<Deferred<boolean>> = [];
57
-
58
- private eventEmitter = createEventEmitter<AuthEventsMap<E>>();
59
-
60
- private subscribers: Set<SubscribeFn> = new Set();
61
-
62
- //
63
- // Getters
64
- //
65
-
66
- public get isInitialized() {
67
- return this._state.isInitialized;
68
- }
69
-
70
- public get isAuthenticated() {
71
- return this._state.isAuthenticated;
72
- }
73
-
74
- public get tokens() {
75
- return this._state.tokens;
76
- }
77
-
78
- //
79
- // Public methods
80
- //
81
-
82
- public async init(): Promise<boolean> {
83
- try {
84
- await this.onInit();
85
-
86
- this.setState({
87
- isInitialized: true,
88
- });
89
-
90
- this.emit('initSuccess', undefined);
91
- } catch (error) {
92
- this.setState({
93
- isInitialized: false,
94
- });
95
-
96
- this.emit('initFailed', error as E);
97
- }
98
-
99
- await this.onPostInit?.();
100
-
101
- return this.isInitialized;
102
- }
103
-
104
- public async login(credentials?: C): Promise<boolean> {
105
- this.emit('loginStarted', undefined);
106
-
107
- await this.onPreLogin?.();
108
-
109
- let isSuccess: boolean = false;
110
-
111
- try {
112
- const tokens = await this.onLogin(credentials);
113
-
114
- this.setState({
115
- isAuthenticated: true,
116
- tokens,
117
- });
118
-
119
- this.emit('loginSuccess', undefined);
120
-
121
- isSuccess = true;
122
- } catch (err) {
123
- this.setState({
124
- isAuthenticated: false,
125
- tokens: {},
126
- });
127
-
128
- this.emit('loginFailed', err as E);
129
-
130
- isSuccess = false;
131
- }
132
-
133
- await this.onPostLogin?.(isSuccess);
134
-
135
- return this.isAuthenticated;
136
- }
137
-
138
- public async refresh(minValidity?: number): Promise<boolean> {
139
- const deferred = new Deferred<boolean>();
140
-
141
- this.runRefresh(deferred, minValidity);
142
-
143
- return deferred.getPromise();
144
- }
145
-
146
- public async logout(): Promise<void> {
147
- this.emit('logoutStarted', undefined);
148
-
149
- await this.onPreLogout?.();
150
-
151
- let isSuccess: boolean = false;
152
-
153
- try {
154
- await this.onLogout();
155
-
156
- this.setState({
157
- isAuthenticated: false,
158
- tokens: {},
159
- });
160
-
161
- this.emit('logoutSuccess', undefined);
162
-
163
- isSuccess = true;
164
- } catch (err) {
165
- this.emit('logoutFailed', err as E);
166
-
167
- isSuccess = false;
168
- }
169
-
170
- await this.onPostLogout?.(isSuccess);
171
- }
172
-
173
- public on<K extends EventKey<AuthEventsMap<E>>>(
174
- eventName: K,
175
- listener: EventReceiver<AuthEventsMap<E>[K]>
176
- ): void {
177
- this.eventEmitter.on(eventName, listener);
178
- }
179
-
180
- public off<K extends EventKey<AuthEventsMap<E>>>(
181
- eventName: K,
182
- listener: EventReceiver<AuthEventsMap<E>[K]>
183
- ): void {
184
- this.eventEmitter.off(eventName, listener);
185
- }
186
-
187
- // Should be declared like this to avoid binding issues when used by useSyncExternalStore
188
- public subscribe = (subscription: SubscribeFn): UnsubscribeFn => {
189
- this.subscribers.add(subscription);
190
-
191
- return () => this.subscribers.delete(subscription);
192
- };
193
-
194
- // Should be declared like this to avoid binding issues when used by useSyncExternalStore
195
- public getSnapshot = (): AuthClientState<T> => {
196
- return this._state;
197
- };
198
-
199
- //
200
- // Protected methods
201
- //
202
-
203
- protected setState(stateUpdate: Partial<AuthClientState<T>>): void {
204
- this._state = {
205
- ...this._state,
206
- ...stateUpdate,
207
- };
208
-
209
- this.notifySubscribers();
210
- }
211
-
212
- //
213
- // Private methods
214
- //
215
-
216
- private async runRefresh(
217
- deferred: Deferred<boolean>,
218
- minValidity?: number
219
- ): Promise<void> {
220
- // Add deferred Promise to refresh queue
221
- this.refreshQ.push(deferred);
222
-
223
- // If refresh queue already has promises enqueued do not attempt a new refresh - one is already in progress
224
- if (this.refreshQ.length !== 1) {
225
- return;
226
- }
227
-
228
- this.emit('refreshStarted', undefined);
229
-
230
- await this.onPreRefresh?.();
231
-
232
- let isAuthenticated: boolean = false;
233
- let tokens: Partial<T> = {};
234
-
235
- try {
236
- tokens = await this.onRefresh(minValidity);
237
- isAuthenticated = true;
238
-
239
- this.emit('refreshSuccess', undefined);
240
- } catch (err) {
241
- isAuthenticated = false;
242
-
243
- this.emit('refreshFailed', err as E);
244
- }
245
-
246
- this.setState({
247
- isAuthenticated,
248
- tokens,
249
- });
250
-
251
- await this.onPostRefresh?.(isAuthenticated);
252
-
253
- for (let p = this.refreshQ.pop(); p != null; p = this.refreshQ.pop()) {
254
- p.resolve(isAuthenticated);
255
- }
256
- }
257
-
258
- private emit<K extends EventKey<AuthEventsMap<E>>>(
259
- eventName: K,
260
- error: AuthEventsMap<E>[K]
261
- ): void {
262
- this.eventEmitter.emit(eventName, error);
263
- }
264
-
265
- private notifySubscribers() {
266
- this.subscribers.forEach(s => {
267
- try {
268
- s();
269
- } catch {}
270
- });
271
- }
272
-
273
- //
274
- // Abstract methods
275
- //
276
-
277
- protected abstract onInit(): Promise<void>;
278
-
279
- protected onPostInit?(): Promise<void>;
280
-
281
- protected onPreLogin?(): Promise<void>;
282
-
283
- protected abstract onLogin(credentials?: C): Promise<T>;
284
-
285
- protected onPostLogin?(isSuccess: boolean): Promise<void>;
286
-
287
- protected onPreRefresh?(): Promise<void>;
288
-
289
- protected abstract onRefresh(minValidity?: number): Promise<T>;
290
-
291
- protected onPostRefresh?(isSuccess: boolean): Promise<void>;
292
-
293
- protected onPreLogout?(): Promise<void>;
294
-
295
- protected abstract onLogout(): Promise<void>;
296
-
297
- protected onPostLogout?(isSuccess: boolean): Promise<void>;
298
- }