@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.
- package/LICENSE +2 -2
- package/README.md +91 -44
- package/dist/auth.d.ts +237 -0
- package/dist/auth.js +215 -0
- package/dist/index.d.ts +2 -21
- package/dist/index.js +1 -8
- package/dist/{types/eventEmitter.d.ts → utils.d.ts} +17 -10
- package/dist/utils.js +30 -0
- package/package.json +30 -59
- package/src/auth.tsx +546 -0
- package/src/index.ts +2 -0
- package/src/{types/eventEmitter.ts → utils.ts} +25 -2
- package/dist/react-auth.cjs.development.js +0 -789
- package/dist/react-auth.cjs.development.js.map +0 -1
- package/dist/react-auth.cjs.production.min.js +0 -2
- package/dist/react-auth.cjs.production.min.js.map +0 -1
- package/dist/react-auth.esm.js +0 -781
- package/dist/react-auth.esm.js.map +0 -1
- package/dist/types/deferred.d.ts +0 -7
- package/dist/types/index.d.ts +0 -56
- package/src/index.tsx +0 -98
- package/src/types/deferred.ts +0 -18
- package/src/types/index.ts +0 -298
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c)
|
|
3
|
+
Copyright (c) 2025 ForWarD Software (https://forwardsoftware.solutions/)
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
18
18
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
# React Auth
|
|
2
2
|
|
|
3
|
-
<div align="center">
|
|
4
|
-
<img src="./assets/cover.jpeg" width="100%" />
|
|
5
|
-
</div>
|
|
6
|
-
|
|
7
3
|
> Simplify your Auth flow when working with React apps
|
|
8
4
|
|
|
9
|
-
[](https://github.com/forwardsoftware/react-auth/blob/main/LICENSE) [](https://github.com/forwardsoftware/react-auth/actions/workflows/build-test.yml) [](https://github.com/forwardsoftware/react-auth/issues)
|
|
10
6
|
|
|
11
7
|
[](https://npmjs.com/package/@forward-software/react-auth) [](https://npmjs.com/package/@forward-software/react-auth)
|
|
12
8
|
|
|
@@ -17,86 +13,137 @@ This React package allows you to streamline the integration of user authenticati
|
|
|
17
13
|
## Install
|
|
18
14
|
|
|
19
15
|
```sh
|
|
20
|
-
|
|
16
|
+
npm install @forward-software/react-auth
|
|
21
17
|
```
|
|
22
18
|
|
|
23
19
|
## Setup
|
|
24
20
|
|
|
25
|
-
### Define an AuthClient
|
|
26
|
-
|
|
27
|
-
Create a new `AuthClient` class which extends the `BaseAuthClient` provided by this library and implements the 4 required methods:
|
|
21
|
+
### Define an AuthClient
|
|
28
22
|
|
|
29
|
-
|
|
30
|
-
- **onLogin**, invoked when the `login` method of the AuthClient gets called
|
|
31
|
-
- **onRefresh**, invoked when the `refresh` method of the AuthClient gets called
|
|
32
|
-
- **onLogout**, invoked when the `logout` method of the AuthClient gets called
|
|
23
|
+
Create a new object that implements the `AuthClient` interface provided by this library. The interface includes several lifecycle methods, some of which are optional:
|
|
33
24
|
|
|
34
25
|
```ts
|
|
35
|
-
import {
|
|
26
|
+
import type { AuthClient } from '@forward-software/react-auth';
|
|
36
27
|
|
|
37
28
|
// The type for your credentials
|
|
38
29
|
type AuthCredentials = {
|
|
39
30
|
username: string;
|
|
40
|
-
|
|
41
31
|
password: string;
|
|
42
32
|
};
|
|
43
33
|
|
|
44
34
|
// The type for your tokens
|
|
45
35
|
type AuthTokens = {
|
|
46
36
|
authToken: string;
|
|
47
|
-
|
|
48
37
|
refreshToken: string;
|
|
49
38
|
};
|
|
50
39
|
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
const authClient: AuthClient<AuthTokens, AuthCredentials> = {
|
|
41
|
+
// Optional: Called when the AuthClient gets initialized
|
|
42
|
+
onInit: async (): Promise<AuthTokens | null> => {
|
|
53
43
|
// Implement the initialization logic for your client
|
|
54
|
-
|
|
44
|
+
return null;
|
|
45
|
+
},
|
|
55
46
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
47
|
+
// Optional: Called after initialization completes
|
|
48
|
+
onPostInit: async (): Promise<void> => {
|
|
49
|
+
// Implement any post-initialization logic
|
|
50
|
+
},
|
|
59
51
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
// Optional: Called before login starts
|
|
53
|
+
onPreLogin: async (): Promise<void> => {
|
|
54
|
+
// Implement any pre-login logic
|
|
55
|
+
},
|
|
63
56
|
|
|
64
|
-
|
|
57
|
+
// Required: Called when login is requested
|
|
58
|
+
onLogin: async (credentials?: AuthCredentials): Promise<AuthTokens> => {
|
|
59
|
+
// Implement the logic required to exchange the provided credentials for user tokens
|
|
60
|
+
return {
|
|
61
|
+
authToken: '...',
|
|
62
|
+
refreshToken: '...'
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
|
|
66
|
+
// Optional: Called after login completes
|
|
67
|
+
onPostLogin: async (isSuccess: boolean): Promise<void> => {
|
|
68
|
+
// Implement any post-login logic
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
// Optional: Called before refresh starts
|
|
72
|
+
onPreRefresh: async (): Promise<void> => {
|
|
73
|
+
// Implement any pre-refresh logic
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
// Optional: Called when refresh is requested
|
|
77
|
+
// The current tokens are passed as the first argument
|
|
78
|
+
onRefresh: async (currentTokens: AuthTokens, minValidity?: number): Promise<AuthTokens> => {
|
|
79
|
+
// Implement the logic required to refresh the current user tokens
|
|
80
|
+
return {
|
|
81
|
+
authToken: '...',
|
|
82
|
+
refreshToken: '...'
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
// Optional: Called after refresh completes
|
|
87
|
+
onPostRefresh: async (isSuccess: boolean): Promise<void> => {
|
|
88
|
+
// Implement any post-refresh logic
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
// Optional: Called before logout starts
|
|
92
|
+
onPreLogout: async (): Promise<void> => {
|
|
93
|
+
// Implement any pre-logout logic
|
|
94
|
+
},
|
|
95
|
+
|
|
96
|
+
// Optional: Called when logout is requested
|
|
97
|
+
onLogout: async (): Promise<void> => {
|
|
65
98
|
// Implement the logic required to invalidate the current user tokens
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
// Optional: Called after logout completes
|
|
102
|
+
onPostLogout: async (isSuccess: boolean): Promise<void> => {
|
|
103
|
+
// Implement any post-logout logic
|
|
66
104
|
}
|
|
67
|
-
}
|
|
105
|
+
};
|
|
68
106
|
```
|
|
69
107
|
|
|
70
|
-
###
|
|
108
|
+
### Use the AuthClient
|
|
71
109
|
|
|
72
|
-
|
|
110
|
+
The `AuthClient` instance can be used directly with the `createAuth` function:
|
|
73
111
|
|
|
74
112
|
```ts
|
|
75
|
-
|
|
113
|
+
import { createAuth } from '@forward-software/react-auth';
|
|
114
|
+
|
|
115
|
+
export const { AuthProvider, useAuthClient, authClient: enhancedAuthClient } = createAuth(authClient);
|
|
76
116
|
```
|
|
77
117
|
|
|
78
|
-
|
|
118
|
+
The `createAuth` function returns:
|
|
79
119
|
|
|
80
|
-
- `
|
|
81
|
-
- `
|
|
82
|
-
- `
|
|
120
|
+
- `AuthProvider`, the context Provider component that should wrap your app and provide access to your AuthClient
|
|
121
|
+
- `useAuthClient`, the hook to retrieve and interact with your AuthClient
|
|
122
|
+
- `authClient`, the enhanced authentication client instance
|
|
123
|
+
|
|
124
|
+
#### AuthProvider
|
|
125
|
+
|
|
126
|
+
The context Provider component that should wrap your app and provide access to your AuthClient, this component also accepts 2 additional props
|
|
127
|
+
|
|
128
|
+
- `ErrorComponent`, displayed when the AuthClient initialization fails
|
|
129
|
+
- `LoadingComponent`, displayed while the AuthClient is being initialized
|
|
130
|
+
|
|
131
|
+
#### EnhancedAuthClient
|
|
83
132
|
|
|
84
|
-
|
|
133
|
+
The `createAuth` function wraps your `AuthClient` implementation with an `EnhancedAuthClient` that provides additional functionality:
|
|
85
134
|
|
|
86
|
-
#####
|
|
135
|
+
##### Properties
|
|
136
|
+
- `isInitialized`, a boolean indicating if the AuthClient has been initialized
|
|
137
|
+
- `isAuthenticated`, a boolean indicating if the login process has been successful and the user is authenticated
|
|
138
|
+
- `tokens`, the current tokens returned by the `login` or the `refresh` process
|
|
87
139
|
|
|
88
|
-
|
|
140
|
+
##### Methods
|
|
141
|
+
- `init()`, initialize the AuthClient (**N.B.** this shouldn't be called if using `AuthProvider` - see above)
|
|
89
142
|
- `login(credentials)`, start the login process
|
|
90
143
|
- `refresh()`, refresh the current tokens
|
|
91
144
|
- `logout()`, logout and invalidate the current tokens
|
|
92
|
-
|
|
93
|
-
##### EventEmitter
|
|
94
|
-
|
|
95
145
|
- `on(eventName, listenerFn)`, subscribe to `eventName` events emitted by the AuthClient
|
|
96
146
|
- `off(eventName, listenerFn)`, unsubscribe from `eventName` events emitted by the AuthClient
|
|
97
|
-
|
|
98
|
-
##### Observable
|
|
99
|
-
|
|
100
147
|
- `subscribe(() => { })`, subscribe to AuthClient state changes
|
|
101
148
|
- `getSnapshot()`, returns the current state of the AuthClient
|
|
102
149
|
|
|
@@ -136,6 +183,6 @@ MIT
|
|
|
136
183
|
|
|
137
184
|
---
|
|
138
185
|
|
|
139
|
-
Made with ✨ & ❤️ by [ForWarD Software](https://github.com/
|
|
186
|
+
Made with ✨ & ❤️ by [ForWarD Software](https://github.com/forwardsoftware) and [contributors](https://github.com/forwardsoftware/react-auth/graphs/contributors)
|
|
140
187
|
|
|
141
188
|
If you found this project to be helpful, please consider contacting us to develop your React and React Native projects.
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { PropsWithChildren } from 'react';
|
|
3
|
+
import { EventReceiver } from "./utils";
|
|
4
|
+
import type { EventKey } from "./utils";
|
|
5
|
+
/**
|
|
6
|
+
* Represents authentication tokens used for API authorization
|
|
7
|
+
*/
|
|
8
|
+
type AuthTokens = {};
|
|
9
|
+
/**
|
|
10
|
+
* Represents user credentials used for authentication
|
|
11
|
+
*/
|
|
12
|
+
type AuthCredentials = {};
|
|
13
|
+
/**
|
|
14
|
+
* Maps authentication events to their corresponding payload types
|
|
15
|
+
* @template E - The error type used throughout the authentication flow
|
|
16
|
+
*/
|
|
17
|
+
type AuthEventsMap<E extends Error> = {
|
|
18
|
+
initSuccess: undefined;
|
|
19
|
+
initFailed: E;
|
|
20
|
+
loginStarted: undefined;
|
|
21
|
+
loginSuccess: undefined;
|
|
22
|
+
loginFailed: E;
|
|
23
|
+
refreshStarted: undefined;
|
|
24
|
+
refreshSuccess: undefined;
|
|
25
|
+
refreshFailed: E;
|
|
26
|
+
logoutStarted: undefined;
|
|
27
|
+
logoutSuccess: undefined;
|
|
28
|
+
logoutFailed: E;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Function type for subscription callbacks
|
|
32
|
+
*/
|
|
33
|
+
type SubscribeFn = () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Function type for unsubscribing from events
|
|
36
|
+
* @returns {boolean} - Returns true if the subscription was successfully removed
|
|
37
|
+
*/
|
|
38
|
+
type UnsubscribeFn = () => boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Interface defining the core authentication client functionality
|
|
41
|
+
* @template T - The type of authentication tokens
|
|
42
|
+
* @template C - The type of authentication credentials
|
|
43
|
+
*/
|
|
44
|
+
export interface AuthClient<T = AuthTokens, C = AuthCredentials> {
|
|
45
|
+
/**
|
|
46
|
+
* Optional initialization hook called before authentication
|
|
47
|
+
* @returns {Promise<T | null>} - Returns authentication tokens if available
|
|
48
|
+
*/
|
|
49
|
+
onInit?(): Promise<T | null>;
|
|
50
|
+
/**
|
|
51
|
+
* Optional post-initialization hook
|
|
52
|
+
*/
|
|
53
|
+
onPostInit?(): Promise<void>;
|
|
54
|
+
/**
|
|
55
|
+
* Optional pre-login hook
|
|
56
|
+
*/
|
|
57
|
+
onPreLogin?(): Promise<void>;
|
|
58
|
+
/**
|
|
59
|
+
* Handles the login process
|
|
60
|
+
* @param {C} [credentials] - Optional credentials for authentication
|
|
61
|
+
* @returns {Promise<T>} - Returns authentication tokens upon successful login
|
|
62
|
+
*/
|
|
63
|
+
onLogin(credentials?: C): Promise<T>;
|
|
64
|
+
/**
|
|
65
|
+
* Optional post-login hook
|
|
66
|
+
* @param {boolean} isSuccess - Indicates whether the login was successful
|
|
67
|
+
*/
|
|
68
|
+
onPostLogin?(isSuccess: boolean): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* Optional pre-refresh hook
|
|
71
|
+
*/
|
|
72
|
+
onPreRefresh?(): Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Optional token refresh handler.
|
|
75
|
+
* Implement this method to handle token refresh logic.
|
|
76
|
+
* @param {T} currentTokens - The current authentication tokens.
|
|
77
|
+
* @param {number} [minValidity] - Optional minimum token validity period in seconds.
|
|
78
|
+
* @returns {Promise<T>} - A promise that resolves with the refreshed authentication tokens.
|
|
79
|
+
*/
|
|
80
|
+
onRefresh?(currentTokens: T, minValidity?: number): Promise<T>;
|
|
81
|
+
/**
|
|
82
|
+
* Optional post-refresh hook
|
|
83
|
+
* @param {boolean} isSuccess - Indicates whether the token refresh was successful
|
|
84
|
+
*/
|
|
85
|
+
onPostRefresh?(isSuccess: boolean): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Optional pre-logout hook
|
|
88
|
+
*/
|
|
89
|
+
onPreLogout?(): Promise<void>;
|
|
90
|
+
/**
|
|
91
|
+
* Optional logout handler
|
|
92
|
+
*/
|
|
93
|
+
onLogout?(): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Optional post-logout hook
|
|
96
|
+
* @param {boolean} isSuccess - Indicates whether the logout was successful
|
|
97
|
+
*/
|
|
98
|
+
onPostLogout?(isSuccess: boolean): Promise<void>;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Extracts token type from an AuthClient implementation
|
|
102
|
+
* @template AC - The AuthClient implementation type
|
|
103
|
+
*/
|
|
104
|
+
type AuthClientTokens<AC extends AuthClient> = Partial<Awaited<ReturnType<AC["onLogin"]>>>;
|
|
105
|
+
/**
|
|
106
|
+
* Extracts credentials type from an AuthClient implementation
|
|
107
|
+
* @template AC - The AuthClient implementation type
|
|
108
|
+
*/
|
|
109
|
+
type AuthClientCredentials<AC extends AuthClient> = Parameters<AC["onLogin"]>;
|
|
110
|
+
/**
|
|
111
|
+
* Represents the current state of an AuthClient
|
|
112
|
+
* @template AC - The AuthClient implementation type
|
|
113
|
+
*/
|
|
114
|
+
type AuthClientState<AC extends AuthClient> = {
|
|
115
|
+
isAuthenticated: boolean;
|
|
116
|
+
isInitialized: boolean;
|
|
117
|
+
tokens: AuthClientTokens<AC>;
|
|
118
|
+
};
|
|
119
|
+
declare class AuthClientEnhancements<AC extends AuthClient, E extends Error> {
|
|
120
|
+
private _state;
|
|
121
|
+
private refreshQ;
|
|
122
|
+
private eventEmitter;
|
|
123
|
+
private subscribers;
|
|
124
|
+
private _authClient;
|
|
125
|
+
constructor(authClient: AC);
|
|
126
|
+
/**
|
|
127
|
+
* Indicates whether the authentication client has been initialized
|
|
128
|
+
* @readonly
|
|
129
|
+
*/
|
|
130
|
+
get isInitialized(): boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Indicates whether the user is currently authenticated
|
|
133
|
+
* @readonly
|
|
134
|
+
*/
|
|
135
|
+
get isAuthenticated(): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* Current authentication tokens
|
|
138
|
+
* @readonly
|
|
139
|
+
*/
|
|
140
|
+
get tokens(): Partial<Awaited<ReturnType<AC["onLogin"]>>>;
|
|
141
|
+
/**
|
|
142
|
+
* Initializes the authentication client
|
|
143
|
+
* @returns {Promise<boolean>} - Returns true if initialization was successful
|
|
144
|
+
*/
|
|
145
|
+
init(): Promise<boolean>;
|
|
146
|
+
/**
|
|
147
|
+
* Attempts to authenticate the user with provided credentials
|
|
148
|
+
* @param {...AuthClientCredentials<AC>} params - Authentication credentials
|
|
149
|
+
* @returns {Promise<boolean>} - Returns true if login was successful
|
|
150
|
+
*/
|
|
151
|
+
login(...params: AuthClientCredentials<AC>): Promise<boolean>;
|
|
152
|
+
/**
|
|
153
|
+
* Refreshes the authentication tokens
|
|
154
|
+
* @param {number} [minValidity] - Minimum token validity period in seconds
|
|
155
|
+
* @returns {Promise<boolean>} - Returns true if token refresh was successful
|
|
156
|
+
*/
|
|
157
|
+
refresh(minValidity?: number): Promise<boolean>;
|
|
158
|
+
/**
|
|
159
|
+
* Logs out the current user
|
|
160
|
+
* @returns {Promise<void>}
|
|
161
|
+
*/
|
|
162
|
+
logout(): Promise<void>;
|
|
163
|
+
/**
|
|
164
|
+
* Registers an event listener for authentication events
|
|
165
|
+
* @template K - The event key type
|
|
166
|
+
* @param {K} eventName - The name of the event to listen for
|
|
167
|
+
* @param {EventReceiver<AuthEventsMap<E>[K]>} listener - The event handler function
|
|
168
|
+
*/
|
|
169
|
+
on<K extends EventKey<AuthEventsMap<E>>>(eventName: K, listener: EventReceiver<AuthEventsMap<E>[K]>): void;
|
|
170
|
+
/**
|
|
171
|
+
* Removes an event listener for authentication events
|
|
172
|
+
* @template K - The event key type
|
|
173
|
+
* @param {K} eventName - The name of the event to stop listening for
|
|
174
|
+
* @param {EventReceiver<AuthEventsMap<E>[K]>} listener - The event handler function to remove
|
|
175
|
+
*/
|
|
176
|
+
off<K extends EventKey<AuthEventsMap<E>>>(eventName: K, listener: EventReceiver<AuthEventsMap<E>[K]>): void;
|
|
177
|
+
/**
|
|
178
|
+
* Subscribes to authentication state changes
|
|
179
|
+
* @param {SubscribeFn} subscription - The callback function to be called on state changes
|
|
180
|
+
* @returns {UnsubscribeFn} - A function to unsubscribe from state changes
|
|
181
|
+
*/
|
|
182
|
+
subscribe: (subscription: SubscribeFn) => UnsubscribeFn;
|
|
183
|
+
/**
|
|
184
|
+
* Gets the current authentication state
|
|
185
|
+
* @returns {AuthClientState<AC>} - The current authentication state
|
|
186
|
+
*/
|
|
187
|
+
getSnapshot: () => AuthClientState<AC>;
|
|
188
|
+
private setState;
|
|
189
|
+
private runRefresh;
|
|
190
|
+
private emit;
|
|
191
|
+
private notifySubscribers;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Enhanced authentication client with additional functionality and state management
|
|
195
|
+
* @template AC - The AuthClient implementation type
|
|
196
|
+
* @template E - The error type used throughout the authentication flow
|
|
197
|
+
*/
|
|
198
|
+
export type EnhancedAuthClient<AC extends AuthClient, E extends Error> = AC & AuthClientEnhancements<AC, E>;
|
|
199
|
+
/**
|
|
200
|
+
* Wraps a basic AuthClient implementation with enhanced functionality
|
|
201
|
+
* @template AC - The AuthClient implementation type
|
|
202
|
+
* @template E - The error type used throughout the authentication flow
|
|
203
|
+
* @param {AC} authClient - The base authentication client to enhance
|
|
204
|
+
* @returns {EnhancedAuthClient<AC, E>} - An enhanced authentication client with additional features
|
|
205
|
+
*/
|
|
206
|
+
export declare function wrapAuthClient<AC extends AuthClient, E extends Error = Error>(authClient: AC): EnhancedAuthClient<AC, E>;
|
|
207
|
+
/**
|
|
208
|
+
* Props that can be passed to AuthProvider
|
|
209
|
+
*/
|
|
210
|
+
export type AuthProviderProps = PropsWithChildren<{
|
|
211
|
+
/**
|
|
212
|
+
* An optional component to display if AuthClient initialization failed.
|
|
213
|
+
*/
|
|
214
|
+
ErrorComponent?: React.ReactNode;
|
|
215
|
+
/**
|
|
216
|
+
* An optional component to display while AuthClient instance is being initialized.
|
|
217
|
+
*/
|
|
218
|
+
LoadingComponent?: React.ReactNode;
|
|
219
|
+
}>;
|
|
220
|
+
/**
|
|
221
|
+
* Creates an authentication context and provider for a React application.
|
|
222
|
+
* It wraps the provided `authClient` with enhanced state management and event handling.
|
|
223
|
+
*
|
|
224
|
+
* @template AC - The type of the base `AuthClient` implementation.
|
|
225
|
+
* @template E - The type of error expected during authentication flows. Defaults to `Error`.
|
|
226
|
+
* @param {AC} authClient - The base authentication client instance to use.
|
|
227
|
+
* @returns An object containing:
|
|
228
|
+
* - `AuthProvider`: A React component to wrap the application or parts of it.
|
|
229
|
+
* - `authClient`: The enhanced authentication client instance.
|
|
230
|
+
* - `useAuthClient`: A hook to access the enhanced `authClient` within the `AuthProvider`.
|
|
231
|
+
*/
|
|
232
|
+
export declare function createAuth<AC extends AuthClient, E extends Error = Error>(authClient: AC): {
|
|
233
|
+
AuthProvider: React.FC<AuthProviderProps>;
|
|
234
|
+
authClient: EnhancedAuthClient<AC, E>;
|
|
235
|
+
useAuthClient: () => EnhancedAuthClient<AC, E>;
|
|
236
|
+
};
|
|
237
|
+
export {};
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
11
|
+
import { createContext, useContext, useEffect, useState } from 'react';
|
|
12
|
+
import { useSyncExternalStore } from 'use-sync-external-store/shim';
|
|
13
|
+
import { createEventEmitter, Deferred } from "./utils";
|
|
14
|
+
class AuthClientEnhancements {
|
|
15
|
+
constructor(authClient) {
|
|
16
|
+
this._state = {
|
|
17
|
+
isAuthenticated: false,
|
|
18
|
+
isInitialized: false,
|
|
19
|
+
tokens: {},
|
|
20
|
+
};
|
|
21
|
+
this.refreshQ = [];
|
|
22
|
+
this.eventEmitter = createEventEmitter();
|
|
23
|
+
this.subscribers = new Set();
|
|
24
|
+
this.subscribe = (subscription) => {
|
|
25
|
+
this.subscribers.add(subscription);
|
|
26
|
+
return () => this.subscribers.delete(subscription);
|
|
27
|
+
};
|
|
28
|
+
this.getSnapshot = () => {
|
|
29
|
+
return this._state;
|
|
30
|
+
};
|
|
31
|
+
this._authClient = authClient;
|
|
32
|
+
}
|
|
33
|
+
get isInitialized() {
|
|
34
|
+
return this._state.isInitialized;
|
|
35
|
+
}
|
|
36
|
+
get isAuthenticated() {
|
|
37
|
+
return this._state.isAuthenticated;
|
|
38
|
+
}
|
|
39
|
+
get tokens() {
|
|
40
|
+
return this._state.tokens;
|
|
41
|
+
}
|
|
42
|
+
init() {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
var _a, _b, _c, _d;
|
|
45
|
+
try {
|
|
46
|
+
const prevTokens = yield ((_b = (_a = this._authClient).onInit) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
47
|
+
this.setState({
|
|
48
|
+
isInitialized: true,
|
|
49
|
+
isAuthenticated: !!prevTokens,
|
|
50
|
+
tokens: prevTokens || {},
|
|
51
|
+
});
|
|
52
|
+
this.emit("initSuccess", undefined);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
this.setState({
|
|
56
|
+
isInitialized: false,
|
|
57
|
+
});
|
|
58
|
+
this.emit("initFailed", error);
|
|
59
|
+
}
|
|
60
|
+
yield ((_d = (_c = this._authClient).onPostInit) === null || _d === void 0 ? void 0 : _d.call(_c));
|
|
61
|
+
return this.isInitialized;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
login(...params) {
|
|
65
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
66
|
+
var _a, _b, _c, _d;
|
|
67
|
+
this.emit("loginStarted", undefined);
|
|
68
|
+
yield ((_b = (_a = this._authClient).onPreLogin) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
69
|
+
let isSuccess = false;
|
|
70
|
+
try {
|
|
71
|
+
const tokens = yield this._authClient.onLogin(...params);
|
|
72
|
+
this.setState({
|
|
73
|
+
isAuthenticated: !!tokens,
|
|
74
|
+
tokens,
|
|
75
|
+
});
|
|
76
|
+
this.emit("loginSuccess", undefined);
|
|
77
|
+
isSuccess = true;
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
this.setState({
|
|
81
|
+
isAuthenticated: false,
|
|
82
|
+
tokens: {},
|
|
83
|
+
});
|
|
84
|
+
this.emit("loginFailed", err);
|
|
85
|
+
isSuccess = false;
|
|
86
|
+
}
|
|
87
|
+
yield ((_d = (_c = this._authClient).onPostLogin) === null || _d === void 0 ? void 0 : _d.call(_c, isSuccess));
|
|
88
|
+
return this.isAuthenticated;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
refresh(minValidity) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const deferred = new Deferred();
|
|
94
|
+
this.runRefresh(deferred, minValidity);
|
|
95
|
+
return deferred.getPromise();
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
logout() {
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
var _a, _b, _c, _d, _e, _f;
|
|
101
|
+
this.emit("logoutStarted", undefined);
|
|
102
|
+
yield ((_b = (_a = this._authClient).onPreLogout) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
103
|
+
let isSuccess = false;
|
|
104
|
+
try {
|
|
105
|
+
yield ((_d = (_c = this._authClient).onLogout) === null || _d === void 0 ? void 0 : _d.call(_c));
|
|
106
|
+
this.setState({
|
|
107
|
+
isAuthenticated: false,
|
|
108
|
+
tokens: {},
|
|
109
|
+
});
|
|
110
|
+
this.emit("logoutSuccess", undefined);
|
|
111
|
+
isSuccess = true;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
this.emit("logoutFailed", err);
|
|
115
|
+
isSuccess = false;
|
|
116
|
+
}
|
|
117
|
+
yield ((_f = (_e = this._authClient).onPostLogout) === null || _f === void 0 ? void 0 : _f.call(_e, isSuccess));
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
on(eventName, listener) {
|
|
121
|
+
this.eventEmitter.on(eventName, listener);
|
|
122
|
+
}
|
|
123
|
+
off(eventName, listener) {
|
|
124
|
+
this.eventEmitter.off(eventName, listener);
|
|
125
|
+
}
|
|
126
|
+
setState(stateUpdate) {
|
|
127
|
+
this._state = Object.assign(Object.assign({}, this._state), stateUpdate);
|
|
128
|
+
this.notifySubscribers();
|
|
129
|
+
}
|
|
130
|
+
runRefresh(deferred, minValidity) {
|
|
131
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
132
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
133
|
+
this.refreshQ.push(deferred);
|
|
134
|
+
if (this.refreshQ.length !== 1) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.emit("refreshStarted", undefined);
|
|
138
|
+
yield ((_b = (_a = this._authClient).onPreRefresh) === null || _b === void 0 ? void 0 : _b.call(_a));
|
|
139
|
+
let isAuthenticated = false;
|
|
140
|
+
let tokens = {};
|
|
141
|
+
try {
|
|
142
|
+
tokens = (_e = (yield ((_d = (_c = this._authClient).onRefresh) === null || _d === void 0 ? void 0 : _d.call(_c, this.tokens, minValidity)))) !== null && _e !== void 0 ? _e : {};
|
|
143
|
+
isAuthenticated = true;
|
|
144
|
+
this.emit("refreshSuccess", undefined);
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
isAuthenticated = false;
|
|
148
|
+
this.emit("refreshFailed", err);
|
|
149
|
+
}
|
|
150
|
+
this.setState({
|
|
151
|
+
isAuthenticated,
|
|
152
|
+
tokens,
|
|
153
|
+
});
|
|
154
|
+
yield ((_g = (_f = this._authClient).onPostRefresh) === null || _g === void 0 ? void 0 : _g.call(_f, isAuthenticated));
|
|
155
|
+
for (let p = this.refreshQ.pop(); p != null; p = this.refreshQ.pop()) {
|
|
156
|
+
p.resolve(isAuthenticated);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
emit(eventName, error) {
|
|
161
|
+
this.eventEmitter.emit(eventName, error);
|
|
162
|
+
}
|
|
163
|
+
notifySubscribers() {
|
|
164
|
+
this.subscribers.forEach((s) => {
|
|
165
|
+
try {
|
|
166
|
+
s();
|
|
167
|
+
}
|
|
168
|
+
catch (_a) { }
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export function wrapAuthClient(authClient) {
|
|
173
|
+
Object.setPrototypeOf(AuthClientEnhancements.prototype, authClient);
|
|
174
|
+
return new AuthClientEnhancements(authClient);
|
|
175
|
+
}
|
|
176
|
+
export function createAuth(authClient) {
|
|
177
|
+
const authContext = createContext(null);
|
|
178
|
+
const enhancedAuthClient = wrapAuthClient(authClient);
|
|
179
|
+
const AuthProvider = ({ children, ErrorComponent, LoadingComponent }) => {
|
|
180
|
+
const [isInitFailed, setInitFailed] = useState(false);
|
|
181
|
+
const { isAuthenticated, isInitialized } = useSyncExternalStore(enhancedAuthClient.subscribe, enhancedAuthClient.getSnapshot);
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
function initAuthClient() {
|
|
184
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
185
|
+
const initSuccess = yield enhancedAuthClient.init();
|
|
186
|
+
setInitFailed(!initSuccess);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
initAuthClient();
|
|
190
|
+
}, []);
|
|
191
|
+
if (!!ErrorComponent && isInitFailed) {
|
|
192
|
+
return ErrorComponent;
|
|
193
|
+
}
|
|
194
|
+
if (!!LoadingComponent && !isInitialized) {
|
|
195
|
+
return LoadingComponent;
|
|
196
|
+
}
|
|
197
|
+
return (_jsx(authContext.Provider, { value: {
|
|
198
|
+
authClient: enhancedAuthClient,
|
|
199
|
+
isAuthenticated,
|
|
200
|
+
isInitialized,
|
|
201
|
+
}, children: children }));
|
|
202
|
+
};
|
|
203
|
+
const useAuthClient = function () {
|
|
204
|
+
const ctx = useContext(authContext);
|
|
205
|
+
if (!ctx) {
|
|
206
|
+
throw new Error('useAuthClient hook should be used inside AuthProvider');
|
|
207
|
+
}
|
|
208
|
+
return ctx.authClient;
|
|
209
|
+
};
|
|
210
|
+
return {
|
|
211
|
+
AuthProvider,
|
|
212
|
+
authClient: enhancedAuthClient,
|
|
213
|
+
useAuthClient,
|
|
214
|
+
};
|
|
215
|
+
}
|