@authu/react 0.1.17

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/README.md ADDED
@@ -0,0 +1,169 @@
1
+ # @authu/react
2
+
3
+ React SDK for AuthU - Centralized Multi-Tenant Authentication Service.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install @authu/react
9
+ # or
10
+ pnpm add @authu/react
11
+ # or
12
+ yarn add @authu/react
13
+ ```
14
+
15
+ ## Usage
16
+
17
+ ### 1. Configure the Provider
18
+
19
+ Wrap your app with `AuthUProvider`:
20
+
21
+ ```tsx
22
+ import {AuthUProvider} from '@authu/react';
23
+
24
+ function App() {
25
+ return (
26
+ <AuthUProvider
27
+ domain="https://auth.example.com"
28
+ clientId="your-client-id"
29
+ redirectUri={window.location.origin + '/callback'}
30
+ >
31
+ <YourApp />
32
+ </AuthUProvider>
33
+ );
34
+ }
35
+ ```
36
+
37
+ ### 2. Use the Hook
38
+
39
+ Access authentication state and methods with `useAuthU`:
40
+
41
+ ```tsx
42
+ import {useAuthU} from '@authu/react';
43
+
44
+ function Profile() {
45
+ const {isAuthenticated, isLoading, user, login, logout} = useAuthU();
46
+
47
+ if (isLoading) return <div>Loading...</div>;
48
+
49
+ if (!isAuthenticated) {
50
+ return <button onClick={() => login()}>Log in</button>;
51
+ }
52
+
53
+ return (
54
+ <div>
55
+ <p>Welcome, {user?.name}</p>
56
+ <button onClick={() => logout()}>Log out</button>
57
+ </div>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ### 3. Protect Routes
63
+
64
+ Use `PrivateRoute` to protect authenticated routes:
65
+
66
+ ```tsx
67
+ import {PrivateRoute} from '@authu/react';
68
+
69
+ function AppRoutes() {
70
+ return (
71
+ <Routes>
72
+ <Route path="/" element={<Home />} />
73
+ <Route
74
+ path="/dashboard"
75
+ element={
76
+ <PrivateRoute>
77
+ <Dashboard />
78
+ </PrivateRoute>
79
+ }
80
+ />
81
+ </Routes>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ### 4. Get Access Token for API Calls
87
+
88
+ Use `useApiToken` to get tokens for authenticated API requests:
89
+
90
+ ```tsx
91
+ import {useApiToken} from '@authu/react';
92
+
93
+ function ApiComponent() {
94
+ const {getToken} = useApiToken();
95
+
96
+ const fetchData = async () => {
97
+ const token = await getToken();
98
+ const response = await fetch('/api/data', {
99
+ headers: {
100
+ Authorization: `Bearer ${token}`,
101
+ },
102
+ });
103
+ return response.json();
104
+ };
105
+
106
+ return <button onClick={fetchData}>Fetch Data</button>;
107
+ }
108
+ ```
109
+
110
+ ## API Reference
111
+
112
+ ### AuthUProvider Props
113
+
114
+ | Prop | Type | Required | Description |
115
+ |------|------|----------|-------------|
116
+ | `domain` | `string` | Yes | AuthU server URL |
117
+ | `clientId` | `string` | Yes | OAuth2 client ID |
118
+ | `redirectUri` | `string` | Yes | Callback URL after login |
119
+ | `scope` | `string` | No | OAuth2 scopes (default: `openid profile email`) |
120
+ | `audience` | `string` | No | API audience for access tokens |
121
+
122
+ ### useAuthU Returns
123
+
124
+ | Property | Type | Description |
125
+ |----------|------|-------------|
126
+ | `isLoading` | `boolean` | True while checking auth state |
127
+ | `isAuthenticated` | `boolean` | True if user is logged in |
128
+ | `user` | `AuthUUser \| null` | User profile info |
129
+ | `error` | `Error \| null` | Auth error if any |
130
+ | `login(options?)` | `function` | Redirect to login |
131
+ | `logout(options?)` | `function` | Log out user |
132
+ | `getAccessToken()` | `function` | Get current access token |
133
+
134
+ ## Development
135
+
136
+ ### Build
137
+
138
+ ```sh
139
+ pnpm run build
140
+ ```
141
+
142
+ ### Lint
143
+
144
+ ```sh
145
+ pnpm run lint
146
+ ```
147
+
148
+ ## Publishing
149
+
150
+ ### Prerequisites
151
+
152
+ - Be logged in to npm: `npm login`
153
+ - Have publish rights on `@authu` scope
154
+
155
+ ### Publish a New Version
156
+
157
+ 1. Update version in `package.json`
158
+ 2. Build and publish:
159
+
160
+ ```sh
161
+ pnpm run build
162
+ pnpm publish --access public
163
+ ```
164
+
165
+ The `--access public` flag is required for scoped packages.
166
+
167
+ ## License
168
+
169
+ MIT
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { AuthUConfig, AuthUContextValue } from './types.js';
3
+ export declare const AuthUContext: import("react").Context<AuthUContextValue | null>;
4
+ interface AuthUProviderProps {
5
+ config: AuthUConfig;
6
+ children: ReactNode;
7
+ }
8
+ export declare function AuthUProvider({ config, children }: AuthUProviderProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
10
+ //# sourceMappingURL=AuthUProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthUProvider.d.ts","sourceRoot":"","sources":["../src/AuthUProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAgBpB,eAAO,MAAM,YAAY,mDAAgD,CAAC;AAE1E,UAAU,kBAAkB;IAC1B,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAID,wBAAgB,aAAa,CAAC,EAAC,MAAM,EAAE,QAAQ,EAAC,EAAE,kBAAkB,2CAySnE"}
@@ -0,0 +1,250 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useEffect, useState, useCallback } from 'react';
3
+ import { generateCodeVerifier, generateCodeChallenge, generateState, generateNonce, storeCodeVerifier, storeState, storeNonce, getCodeVerifier, getStoredState, clearCodeVerifier, clearState, clearNonce } from './pkce.js';
4
+ export const AuthUContext = createContext(null);
5
+ const TOKEN_STORAGE_KEY = 'authu_tokens';
6
+ export function AuthUProvider({ config, children }) {
7
+ const [state, setState] = useState({
8
+ isLoading: true,
9
+ isAuthenticated: false,
10
+ user: null,
11
+ accessToken: null,
12
+ refreshToken: null,
13
+ expiresAt: null,
14
+ error: null
15
+ });
16
+ const getAuthorizationUrl = useCallback(async () => {
17
+ const codeVerifier = generateCodeVerifier();
18
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
19
+ const stateValue = generateState();
20
+ const nonceValue = generateNonce();
21
+ storeCodeVerifier(codeVerifier);
22
+ storeState(stateValue);
23
+ storeNonce(nonceValue);
24
+ const params = new URLSearchParams({
25
+ client_id: config.clientId,
26
+ redirect_uri: config.redirectUri,
27
+ response_type: 'code',
28
+ scope: config.scope || 'openid profile email',
29
+ state: stateValue,
30
+ nonce: nonceValue,
31
+ code_challenge: codeChallenge,
32
+ code_challenge_method: 'S256'
33
+ });
34
+ return `https://${config.domain}/authorize?${params.toString()}`;
35
+ }, [config]);
36
+ const exchangeCodeForTokens = useCallback(async (code) => {
37
+ const codeVerifier = getCodeVerifier();
38
+ if (!codeVerifier) {
39
+ throw new Error('No code verifier found');
40
+ }
41
+ const response = await fetch(`https://${config.domain}/oauth/token`, {
42
+ method: 'POST',
43
+ headers: { 'Content-Type': 'application/json' },
44
+ body: JSON.stringify({
45
+ grant_type: 'authorization_code',
46
+ code,
47
+ redirect_uri: config.redirectUri,
48
+ client_id: config.clientId,
49
+ code_verifier: codeVerifier
50
+ })
51
+ });
52
+ if (!response.ok) {
53
+ const error = await response.json();
54
+ throw new Error(error.error_description || 'Token exchange failed');
55
+ }
56
+ const tokens = await response.json();
57
+ clearCodeVerifier();
58
+ clearState();
59
+ clearNonce();
60
+ return tokens;
61
+ }, [config]);
62
+ const parseIdToken = (idToken) => {
63
+ const parts = idToken.split('.');
64
+ if (parts.length !== 3) {
65
+ throw new Error('Invalid ID token');
66
+ }
67
+ const payload = JSON.parse(atob(parts[1]));
68
+ return {
69
+ sub: payload.sub,
70
+ email: payload.email,
71
+ emailVerified: payload.email_verified,
72
+ name: payload.name,
73
+ picture: payload.picture
74
+ };
75
+ };
76
+ const login = useCallback(async () => {
77
+ const url = await getAuthorizationUrl();
78
+ window.location.href = url;
79
+ }, [getAuthorizationUrl]);
80
+ const logout = useCallback(() => {
81
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
82
+ setState({
83
+ isLoading: false,
84
+ isAuthenticated: false,
85
+ user: null,
86
+ accessToken: null,
87
+ refreshToken: null,
88
+ expiresAt: null,
89
+ error: null
90
+ });
91
+ }, []);
92
+ const fetchUserInfo = useCallback(async (accessToken) => {
93
+ try {
94
+ const response = await fetch(`https://${config.domain}/oauth/userinfo`, {
95
+ headers: { Authorization: `Bearer ${accessToken}` }
96
+ });
97
+ if (!response.ok)
98
+ return null;
99
+ const userInfo = await response.json();
100
+ return {
101
+ sub: userInfo.sub,
102
+ email: userInfo.email,
103
+ emailVerified: userInfo.email_verified,
104
+ name: userInfo.name,
105
+ picture: userInfo.picture
106
+ };
107
+ }
108
+ catch {
109
+ return null;
110
+ }
111
+ }, [config.domain]);
112
+ const refreshTokens = useCallback(async () => {
113
+ if (!state.refreshToken)
114
+ return false;
115
+ try {
116
+ const response = await fetch(`https://${config.domain}/oauth/token`, {
117
+ method: 'POST',
118
+ headers: { 'Content-Type': 'application/json' },
119
+ body: JSON.stringify({
120
+ grant_type: 'refresh_token',
121
+ refresh_token: state.refreshToken,
122
+ client_id: config.clientId
123
+ })
124
+ });
125
+ if (!response.ok) {
126
+ logout();
127
+ return false;
128
+ }
129
+ const tokens = await response.json();
130
+ const expiresAt = Date.now() + tokens.expires_in * 1000;
131
+ const user = tokens.id_token ? parseIdToken(tokens.id_token) : state.user;
132
+ localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify({
133
+ accessToken: tokens.access_token,
134
+ refreshToken: tokens.refresh_token,
135
+ expiresAt
136
+ }));
137
+ setState(prev => ({
138
+ ...prev,
139
+ accessToken: tokens.access_token,
140
+ refreshToken: tokens.refresh_token,
141
+ expiresAt,
142
+ user: user || prev.user
143
+ }));
144
+ return true;
145
+ }
146
+ catch {
147
+ logout();
148
+ return false;
149
+ }
150
+ }, [state.refreshToken, state.user, config.domain, config.clientId, logout]);
151
+ const getAccessToken = useCallback(async () => {
152
+ if (!state.accessToken)
153
+ return null;
154
+ if (state.expiresAt && Date.now() >= state.expiresAt - 60000) {
155
+ const refreshed = await refreshTokens();
156
+ if (!refreshed)
157
+ return null;
158
+ }
159
+ return state.accessToken;
160
+ }, [state.accessToken, state.expiresAt, refreshTokens]);
161
+ useEffect(() => {
162
+ const handleCallback = async () => {
163
+ const params = new URLSearchParams(window.location.search);
164
+ const code = params.get('code');
165
+ const returnedState = params.get('state');
166
+ const error = params.get('error');
167
+ if (error) {
168
+ setState(prev => ({
169
+ ...prev,
170
+ isLoading: false,
171
+ error: new Error(params.get('error_description') || error)
172
+ }));
173
+ return;
174
+ }
175
+ if (code) {
176
+ const storedState = getStoredState();
177
+ if (returnedState !== storedState) {
178
+ setState(prev => ({
179
+ ...prev,
180
+ isLoading: false,
181
+ error: new Error('Invalid state parameter')
182
+ }));
183
+ return;
184
+ }
185
+ try {
186
+ const tokens = await exchangeCodeForTokens(code);
187
+ const user = parseIdToken(tokens.id_token);
188
+ const expiresAt = Date.now() + tokens.expires_in * 1000;
189
+ localStorage.setItem(TOKEN_STORAGE_KEY, JSON.stringify({
190
+ accessToken: tokens.access_token,
191
+ refreshToken: tokens.refresh_token,
192
+ expiresAt
193
+ }));
194
+ setState({
195
+ isLoading: false,
196
+ isAuthenticated: true,
197
+ user,
198
+ accessToken: tokens.access_token,
199
+ refreshToken: tokens.refresh_token,
200
+ expiresAt,
201
+ error: null
202
+ });
203
+ window.history.replaceState({}, document.title, window.location.pathname);
204
+ }
205
+ catch (err) {
206
+ setState(prev => ({
207
+ ...prev,
208
+ isLoading: false,
209
+ error: err instanceof Error ? err : new Error('Authentication failed')
210
+ }));
211
+ }
212
+ return;
213
+ }
214
+ const stored = localStorage.getItem(TOKEN_STORAGE_KEY);
215
+ if (stored) {
216
+ try {
217
+ const tokens = JSON.parse(stored);
218
+ if (tokens.expiresAt > Date.now()) {
219
+ const user = await fetchUserInfo(tokens.accessToken);
220
+ setState({
221
+ isLoading: false,
222
+ isAuthenticated: true,
223
+ user,
224
+ accessToken: tokens.accessToken,
225
+ refreshToken: tokens.refreshToken,
226
+ expiresAt: tokens.expiresAt,
227
+ error: null
228
+ });
229
+ return;
230
+ }
231
+ }
232
+ catch {
233
+ localStorage.removeItem(TOKEN_STORAGE_KEY);
234
+ }
235
+ }
236
+ setState(prev => ({ ...prev, isLoading: false }));
237
+ };
238
+ handleCallback();
239
+ }, [exchangeCodeForTokens, fetchUserInfo]);
240
+ const contextValue = {
241
+ isLoading: state.isLoading,
242
+ isAuthenticated: state.isAuthenticated,
243
+ user: state.user,
244
+ error: state.error,
245
+ login,
246
+ logout,
247
+ getAccessToken
248
+ };
249
+ return (_jsx(AuthUContext.Provider, { value: contextValue, children: children }));
250
+ }
@@ -0,0 +1,9 @@
1
+ import type { ReactNode } from 'react';
2
+ interface PrivateRouteProps {
3
+ children: ReactNode;
4
+ fallback?: ReactNode;
5
+ loginOnUnauthenticated?: boolean;
6
+ }
7
+ export declare function PrivateRoute({ children, fallback, loginOnUnauthenticated }: PrivateRouteProps): string | number | bigint | boolean | Iterable<ReactNode> | Promise<string | number | bigint | boolean | import("react").ReactPortal | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>> | Iterable<ReactNode> | null | undefined> | import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=PrivateRoute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PrivateRoute.d.ts","sourceRoot":"","sources":["../src/PrivateRoute.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,OAAO,CAAC;AAGrC,UAAU,iBAAiB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,wBAAgB,YAAY,CAAC,EAC3B,QAAQ,EACR,QAAe,EACf,sBAA6B,EAC9B,EAAE,iBAAiB,+TAenB"}
@@ -0,0 +1,15 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useAuthU } from './useAuthU.js';
3
+ export function PrivateRoute({ children, fallback = null, loginOnUnauthenticated = true }) {
4
+ const { isLoading, isAuthenticated, login } = useAuthU();
5
+ if (isLoading) {
6
+ return fallback;
7
+ }
8
+ if (!isAuthenticated) {
9
+ if (loginOnUnauthenticated) {
10
+ login();
11
+ }
12
+ return fallback;
13
+ }
14
+ return _jsx(_Fragment, { children: children });
15
+ }
@@ -0,0 +1,7 @@
1
+ export { AuthUProvider } from './AuthUProvider.js';
2
+ export { useAuthU } from './useAuthU.js';
3
+ export { useApiToken } from './useApiToken.js';
4
+ export { PrivateRoute } from './PrivateRoute.js';
5
+ export * from './pkce.js';
6
+ export type { AuthUConfig, AuthUUser, AuthUContextValue } from './types.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAC,QAAQ,EAAC,MAAM,eAAe,CAAC;AACvC,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAC,YAAY,EAAC,MAAM,mBAAmB,CAAC;AAC/C,cAAc,WAAW,CAAC;AAC1B,YAAY,EAAC,WAAW,EAAE,SAAS,EAAE,iBAAiB,EAAC,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export { AuthUProvider } from './AuthUProvider.js';
2
+ export { useAuthU } from './useAuthU.js';
3
+ export { useApiToken } from './useApiToken.js';
4
+ export { PrivateRoute } from './PrivateRoute.js';
5
+ export * from './pkce.js';
package/dist/pkce.d.ts ADDED
@@ -0,0 +1,14 @@
1
+ export declare function generateCodeVerifier(): string;
2
+ export declare function generateCodeChallenge(verifier: string): Promise<string>;
3
+ export declare function generateState(): string;
4
+ export declare function generateNonce(): string;
5
+ export declare function storeCodeVerifier(verifier: string): void;
6
+ export declare function getCodeVerifier(): string | null;
7
+ export declare function clearCodeVerifier(): void;
8
+ export declare function storeState(state: string): void;
9
+ export declare function getStoredState(): string | null;
10
+ export declare function clearState(): void;
11
+ export declare function storeNonce(nonce: string): void;
12
+ export declare function getStoredNonce(): string | null;
13
+ export declare function clearNonce(): void;
14
+ //# sourceMappingURL=pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AA2BA,wBAAgB,oBAAoB,IAAI,MAAM,CAE7C;AAED,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAG7E;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAMD,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAED,wBAAgB,eAAe,IAAI,MAAM,GAAG,IAAI,CAE/C;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAE9C;AAED,wBAAgB,UAAU,IAAI,IAAI,CAEjC"}
package/dist/pkce.js ADDED
@@ -0,0 +1,66 @@
1
+ function generateRandomString(length) {
2
+ const charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
3
+ const randomValues = crypto.getRandomValues(new Uint8Array(length));
4
+ return Array.from(randomValues)
5
+ .map(v => charset[v % charset.length])
6
+ .join('');
7
+ }
8
+ async function sha256(plain) {
9
+ const encoder = new TextEncoder();
10
+ const data = encoder.encode(plain);
11
+ return crypto.subtle.digest('SHA-256', data);
12
+ }
13
+ function base64UrlEncode(buffer) {
14
+ const bytes = new Uint8Array(buffer);
15
+ let binary = '';
16
+ for (let i = 0; i < bytes.byteLength; i++) {
17
+ binary += String.fromCharCode(bytes[i]);
18
+ }
19
+ return btoa(binary)
20
+ .replace(/\+/g, '-')
21
+ .replace(/\//g, '_')
22
+ .replace(/=+$/, '');
23
+ }
24
+ export function generateCodeVerifier() {
25
+ return generateRandomString(64);
26
+ }
27
+ export async function generateCodeChallenge(verifier) {
28
+ const hash = await sha256(verifier);
29
+ return base64UrlEncode(hash);
30
+ }
31
+ export function generateState() {
32
+ return generateRandomString(32);
33
+ }
34
+ export function generateNonce() {
35
+ return generateRandomString(32);
36
+ }
37
+ const STORAGE_KEY_VERIFIER = 'authu_code_verifier';
38
+ const STORAGE_KEY_STATE = 'authu_state';
39
+ const STORAGE_KEY_NONCE = 'authu_nonce';
40
+ export function storeCodeVerifier(verifier) {
41
+ sessionStorage.setItem(STORAGE_KEY_VERIFIER, verifier);
42
+ }
43
+ export function getCodeVerifier() {
44
+ return sessionStorage.getItem(STORAGE_KEY_VERIFIER);
45
+ }
46
+ export function clearCodeVerifier() {
47
+ sessionStorage.removeItem(STORAGE_KEY_VERIFIER);
48
+ }
49
+ export function storeState(state) {
50
+ sessionStorage.setItem(STORAGE_KEY_STATE, state);
51
+ }
52
+ export function getStoredState() {
53
+ return sessionStorage.getItem(STORAGE_KEY_STATE);
54
+ }
55
+ export function clearState() {
56
+ sessionStorage.removeItem(STORAGE_KEY_STATE);
57
+ }
58
+ export function storeNonce(nonce) {
59
+ sessionStorage.setItem(STORAGE_KEY_NONCE, nonce);
60
+ }
61
+ export function getStoredNonce() {
62
+ return sessionStorage.getItem(STORAGE_KEY_NONCE);
63
+ }
64
+ export function clearNonce() {
65
+ sessionStorage.removeItem(STORAGE_KEY_NONCE);
66
+ }
@@ -0,0 +1,46 @@
1
+ export interface AuthUConfig {
2
+ domain: string;
3
+ clientId: string;
4
+ redirectUri: string;
5
+ scope?: string;
6
+ audience?: string;
7
+ }
8
+ export interface AuthUUser {
9
+ sub: string;
10
+ email?: string;
11
+ emailVerified?: boolean;
12
+ name?: string;
13
+ picture?: string;
14
+ }
15
+ export interface AuthUContextValue {
16
+ isLoading: boolean;
17
+ isAuthenticated: boolean;
18
+ user: AuthUUser | null;
19
+ error: Error | null;
20
+ login: (options?: LoginOptions) => void;
21
+ logout: (options?: LogoutOptions) => void;
22
+ getAccessToken: () => Promise<string | null>;
23
+ }
24
+ export interface LoginOptions {
25
+ returnTo?: string;
26
+ }
27
+ export interface LogoutOptions {
28
+ returnTo?: string;
29
+ }
30
+ export interface TokenResponse {
31
+ accessToken: string;
32
+ refreshToken?: string;
33
+ idToken?: string;
34
+ expiresIn: number;
35
+ tokenType: string;
36
+ }
37
+ export interface AuthState {
38
+ isLoading: boolean;
39
+ isAuthenticated: boolean;
40
+ user: AuthUUser | null;
41
+ accessToken: string | null;
42
+ refreshToken: string | null;
43
+ expiresAt: number | null;
44
+ error: Error | null;
45
+ }
46
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,YAAY,KAAK,IAAI,CAAC;IACxC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,aAAa,KAAK,IAAI,CAAC;IAC1C,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,OAAO,CAAC;IACnB,eAAe,EAAE,OAAO,CAAC;IACzB,IAAI,EAAE,SAAS,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ interface UseApiTokenReturn {
2
+ getToken: () => Promise<string | null>;
3
+ fetchWithToken: (url: string, options?: RequestInit) => Promise<Response>;
4
+ }
5
+ export declare function useApiToken(): UseApiTokenReturn;
6
+ export {};
7
+ //# sourceMappingURL=useApiToken.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useApiToken.d.ts","sourceRoot":"","sources":["../src/useApiToken.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAiB;IACzB,QAAQ,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACvC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CAC3E;AAED,wBAAgB,WAAW,IAAI,iBAAiB,CA4B/C"}
@@ -0,0 +1,23 @@
1
+ import { useCallback } from 'react';
2
+ import { useAuthU } from './useAuthU.js';
3
+ export function useApiToken() {
4
+ const { getAccessToken, isAuthenticated } = useAuthU();
5
+ const getToken = useCallback(async () => {
6
+ if (!isAuthenticated) {
7
+ return null;
8
+ }
9
+ return getAccessToken();
10
+ }, [isAuthenticated, getAccessToken]);
11
+ const fetchWithToken = useCallback(async (url, options = {}) => {
12
+ const token = await getToken();
13
+ const headers = new Headers(options.headers);
14
+ if (token) {
15
+ headers.set('Authorization', `Bearer ${token}`);
16
+ }
17
+ return fetch(url, {
18
+ ...options,
19
+ headers
20
+ });
21
+ }, [getToken]);
22
+ return { getToken, fetchWithToken };
23
+ }
@@ -0,0 +1,3 @@
1
+ import type { AuthUContextValue } from './types.js';
2
+ export declare function useAuthU(): AuthUContextValue;
3
+ //# sourceMappingURL=useAuthU.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuthU.d.ts","sourceRoot":"","sources":["../src/useAuthU.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,iBAAiB,EAAC,MAAM,YAAY,CAAC;AAElD,wBAAgB,QAAQ,IAAI,iBAAiB,CAQ5C"}
@@ -0,0 +1,9 @@
1
+ import { useContext } from 'react';
2
+ import { AuthUContext } from './AuthUProvider.js';
3
+ export function useAuthU() {
4
+ const context = useContext(AuthUContext);
5
+ if (!context) {
6
+ throw new Error('useAuthU must be used within an AuthUProvider');
7
+ }
8
+ return context;
9
+ }
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@authu/react",
3
+ "version": "0.1.17",
4
+ "description": "React SDK for AuthU - Centralized Multi-Tenant Authentication Service",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "keywords": [
18
+ "authu",
19
+ "auth",
20
+ "authentication",
21
+ "oauth2",
22
+ "oidc",
23
+ "react",
24
+ "hooks"
25
+ ],
26
+ "author": "Uralys",
27
+ "license": "MIT",
28
+ "peerDependencies": {
29
+ "react": ">=18.0.0"
30
+ },
31
+ "devDependencies": {
32
+ "@eslint/js": "^9.36.0",
33
+ "@types/react": "^19.0.4",
34
+ "eslint": "^9.36.0",
35
+ "eslint-plugin-prettier": "^5.5.4",
36
+ "eslint-plugin-react-hooks": "^5.2.0",
37
+ "prettier": "^3.6.2",
38
+ "react": "^19.0.0",
39
+ "typescript": "^5.7.3",
40
+ "typescript-eslint": "^8.44.1"
41
+ },
42
+ "scripts": {
43
+ "build": "tsc",
44
+ "dev": "tsc --watch",
45
+ "eslint": "eslint src --cache",
46
+ "typecheck": "tsc --noEmit",
47
+ "lint": "pnpm run eslint && pnpm run typecheck"
48
+ }
49
+ }