@avantmedia/id-react 0.0.1 → 0.0.3

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.
@@ -0,0 +1,3 @@
1
+ declare function assert<T>(condition: T, msg?: string): asserts condition;
2
+ export default assert;
3
+ //# sourceMappingURL=assert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA,iBAAS,MAAM,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,GAAG,SAAqB,GAAG,OAAO,CAAC,SAAS,CAI5E;AAED,eAAe,MAAM,CAAC"}
package/dist/assert.js ADDED
@@ -0,0 +1,7 @@
1
+ function assert(condition, msg = 'Assertion failed') {
2
+ if (!condition) {
3
+ throw new Error(msg);
4
+ }
5
+ }
6
+ export default assert;
7
+ //# sourceMappingURL=assert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../src/assert.ts"],"names":[],"mappings":"AAAA,SAAS,MAAM,CAAI,SAAY,EAAE,GAAG,GAAG,kBAAkB;IACrD,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;IACzB,CAAC;AACL,CAAC;AAED,eAAe,MAAM,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=assert.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.test.d.ts","sourceRoot":"","sources":["../src/assert.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,88 @@
1
+ import { describe, expect, test } from 'vitest';
2
+ import assert from './assert';
3
+ describe('assert', () => {
4
+ test('should not throw when condition is truthy', () => {
5
+ expect(() => assert(true)).not.toThrow();
6
+ expect(() => assert(1)).not.toThrow();
7
+ expect(() => assert('non-empty string')).not.toThrow();
8
+ expect(() => assert({})).not.toThrow();
9
+ expect(() => assert([])).not.toThrow();
10
+ expect(() => assert(() => true)).not.toThrow();
11
+ });
12
+ test('should throw when condition is falsy', () => {
13
+ expect(() => assert(false)).toThrow('Assertion failed');
14
+ expect(() => assert(0)).toThrow('Assertion failed');
15
+ expect(() => assert('')).toThrow('Assertion failed');
16
+ expect(() => assert(null)).toThrow('Assertion failed');
17
+ expect(() => assert(undefined)).toThrow('Assertion failed');
18
+ expect(() => assert(NaN)).toThrow('Assertion failed');
19
+ });
20
+ test('should throw Error instance', () => {
21
+ try {
22
+ assert(false);
23
+ }
24
+ catch (error) {
25
+ expect(error).toBeInstanceOf(Error);
26
+ }
27
+ });
28
+ test('should use default error message when no message provided', () => {
29
+ expect(() => assert(false)).toThrow('Assertion failed');
30
+ });
31
+ test('should use custom error message when provided', () => {
32
+ const customMessage = 'Custom assertion error';
33
+ expect(() => assert(false, customMessage)).toThrow(customMessage);
34
+ });
35
+ test('should work with complex conditions', () => {
36
+ const obj = { prop: 'value' };
37
+ const arr = [1, 2, 3];
38
+ expect(() => assert(obj.prop === 'value')).not.toThrow();
39
+ expect(() => assert(arr.length > 0)).not.toThrow();
40
+ expect(() => assert(arr.includes(2))).not.toThrow();
41
+ expect(() => assert(obj.prop === 'wrong')).toThrow();
42
+ expect(() => assert(arr.length === 0)).toThrow();
43
+ expect(() => assert(arr.includes(999))).toThrow();
44
+ });
45
+ test('should work as type guard', () => {
46
+ // Use a deterministic value to avoid non-determinism in tests
47
+ const value = 'test';
48
+ if (value !== null) {
49
+ assert(value, 'Value should not be null');
50
+ // After assertion, TypeScript should know that value is string
51
+ expect(typeof value).toBe('string');
52
+ expect(value.toLowerCase()).toBe(value.toLowerCase()); // This wouldn't compile if value could be null
53
+ }
54
+ });
55
+ test('should work with function conditions', () => {
56
+ const isValid = (val) => val > 0;
57
+ expect(() => assert(isValid(5))).not.toThrow();
58
+ expect(() => assert(isValid(-1))).toThrow();
59
+ });
60
+ test('should preserve original condition value in type narrowing', () => {
61
+ function testFunction(input) {
62
+ assert(typeof input === 'string', 'Input must be a string');
63
+ // TypeScript should now know input is a string
64
+ return input.toUpperCase();
65
+ }
66
+ expect(testFunction('hello')).toBe('HELLO');
67
+ expect(() => testFunction(123)).toThrow('Input must be a string');
68
+ });
69
+ test('should work with array type narrowing', () => {
70
+ function processArray(input) {
71
+ assert(Array.isArray(input), 'Input must be an array');
72
+ // TypeScript should now know input is an array
73
+ return input.length;
74
+ }
75
+ expect(processArray([1, 2, 3])).toBe(3);
76
+ expect(() => processArray('not an array')).toThrow('Input must be an array');
77
+ });
78
+ test('should work with object property existence', () => {
79
+ function getUserName(user) {
80
+ assert(user.name, 'User must have a name');
81
+ // TypeScript should now know user.name is defined
82
+ return user.name.toUpperCase();
83
+ }
84
+ expect(getUserName({ id: 1, name: 'John' })).toBe('JOHN');
85
+ expect(() => getUserName({ id: 1 })).toThrow('User must have a name');
86
+ });
87
+ });
88
+ //# sourceMappingURL=assert.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.test.js","sourceRoot":"","sources":["../src/assert.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import type { AuthState } from "./types";
2
+ /**
3
+ * Hook to access auth state and actions
4
+ *
5
+ * @example
6
+ * ```tsx
7
+ * function App() {
8
+ * const { user, isLoading, login, logout } = useAuth();
9
+ *
10
+ * if (isLoading) return <div>Loading...</div>;
11
+ * if (!user) return <button onClick={login}>Login</button>;
12
+ *
13
+ * return (
14
+ * <div>
15
+ * Hello {user.email}!
16
+ * <button onClick={logout}>Logout</button>
17
+ * </div>
18
+ * );
19
+ * }
20
+ * ```
21
+ */
22
+ export declare function useAuth(): AuthState;
23
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,OAAO,IAAI,SAAS,CAGnC"}
@@ -1,6 +1,4 @@
1
1
  import { useAuthContext } from "./provider";
2
- import type { AuthState } from "./types";
3
-
4
2
  /**
5
3
  * Hook to access auth state and actions
6
4
  *
@@ -21,7 +19,8 @@ import type { AuthState } from "./types";
21
19
  * }
22
20
  * ```
23
21
  */
24
- export function useAuth(): AuthState {
25
- const { config, ...authState } = useAuthContext();
26
- return authState;
22
+ export function useAuth() {
23
+ const { config, ...authState } = useAuthContext();
24
+ return authState;
27
25
  }
26
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,cAAc,EAAE,CAAC;IAClD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { AvantIdProvider } from "./provider";
2
+ export { useAuth } from "./hooks";
3
+ export type { AvantIdConfig, User, TokenResponse, AuthState, } from "./types";
4
+ export { generateCodeVerifier, generateCodeChallenge, } from "./pkce";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG7C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAGlC,YAAY,EACV,aAAa,EACb,IAAI,EACJ,aAAa,EACb,SAAS,GACV,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,QAAQ,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ // Components
2
+ export { AvantIdProvider } from "./provider";
3
+ // Hooks
4
+ export { useAuth } from "./hooks";
5
+ // PKCE utilities (for advanced use cases)
6
+ export { generateCodeVerifier, generateCodeChallenge, } from "./pkce";
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,QAAQ;AACR,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAUlC,0CAA0C;AAC1C,OAAO,EACL,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,QAAQ,CAAC"}
package/dist/pkce.d.ts ADDED
@@ -0,0 +1,31 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) utilities
3
+ * Used to secure the OAuth2 authorization code flow for SPAs
4
+ */
5
+ /**
6
+ * Generate a cryptographically random code verifier
7
+ * Must be between 43 and 128 characters
8
+ */
9
+ export declare function generateCodeVerifier(): string;
10
+ /**
11
+ * Generate code challenge from verifier using S256 method
12
+ * SHA256 hash of verifier, base64url encoded
13
+ */
14
+ export declare function generateCodeChallenge(verifier: string): Promise<string>;
15
+ /**
16
+ * Store code verifier in sessionStorage for later retrieval
17
+ */
18
+ export declare function storeCodeVerifier(verifier: string): void;
19
+ /**
20
+ * Retrieve and clear stored code verifier
21
+ */
22
+ export declare function retrieveCodeVerifier(): string | null;
23
+ /**
24
+ * Generate and store OAuth state parameter for CSRF protection
25
+ */
26
+ export declare function generateState(): string;
27
+ /**
28
+ * Verify state parameter matches stored value
29
+ */
30
+ export declare function verifyState(state: string): boolean;
31
+ //# sourceMappingURL=pkce.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.d.ts","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAeH;;;GAGG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAI7C;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc7E;AAmBD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAExD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAIpD;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAMtC;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAIlD"}
package/dist/pkce.js ADDED
@@ -0,0 +1,88 @@
1
+ /**
2
+ * PKCE (Proof Key for Code Exchange) utilities
3
+ * Used to secure the OAuth2 authorization code flow for SPAs
4
+ */
5
+ /**
6
+ * Get the crypto object, checking for availability
7
+ */
8
+ function getCrypto() {
9
+ if (typeof globalThis !== "undefined" && globalThis.crypto) {
10
+ return globalThis.crypto;
11
+ }
12
+ if (typeof window !== "undefined" && window.crypto) {
13
+ return window.crypto;
14
+ }
15
+ throw new Error("Web Crypto API is not available");
16
+ }
17
+ /**
18
+ * Generate a cryptographically random code verifier
19
+ * Must be between 43 and 128 characters
20
+ */
21
+ export function generateCodeVerifier() {
22
+ const array = new Uint8Array(32);
23
+ getCrypto().getRandomValues(array);
24
+ return base64UrlEncode(array);
25
+ }
26
+ /**
27
+ * Generate code challenge from verifier using S256 method
28
+ * SHA256 hash of verifier, base64url encoded
29
+ */
30
+ export async function generateCodeChallenge(verifier) {
31
+ const cryptoObj = getCrypto();
32
+ if (!cryptoObj.subtle) {
33
+ throw new Error("crypto.subtle is not available. This usually means you are not using HTTPS. " +
34
+ "The Web Crypto API requires a secure context (HTTPS or localhost).");
35
+ }
36
+ const encoder = new TextEncoder();
37
+ const data = encoder.encode(verifier);
38
+ const hash = await cryptoObj.subtle.digest("SHA-256", data);
39
+ return base64UrlEncode(new Uint8Array(hash));
40
+ }
41
+ /**
42
+ * Base64url encode (URL-safe base64 without padding)
43
+ */
44
+ function base64UrlEncode(buffer) {
45
+ const base64 = btoa(String.fromCharCode(...buffer));
46
+ return base64
47
+ .replace(/\+/g, "-")
48
+ .replace(/\//g, "_")
49
+ .replace(/=+$/, "");
50
+ }
51
+ /**
52
+ * Storage keys for PKCE state
53
+ */
54
+ const VERIFIER_KEY = "avant_id_pkce_verifier";
55
+ const STATE_KEY = "avant_id_oauth_state";
56
+ /**
57
+ * Store code verifier in sessionStorage for later retrieval
58
+ */
59
+ export function storeCodeVerifier(verifier) {
60
+ sessionStorage.setItem(VERIFIER_KEY, verifier);
61
+ }
62
+ /**
63
+ * Retrieve and clear stored code verifier
64
+ */
65
+ export function retrieveCodeVerifier() {
66
+ const verifier = sessionStorage.getItem(VERIFIER_KEY);
67
+ sessionStorage.removeItem(VERIFIER_KEY);
68
+ return verifier;
69
+ }
70
+ /**
71
+ * Generate and store OAuth state parameter for CSRF protection
72
+ */
73
+ export function generateState() {
74
+ const array = new Uint8Array(16);
75
+ crypto.getRandomValues(array);
76
+ const state = base64UrlEncode(array);
77
+ sessionStorage.setItem(STATE_KEY, state);
78
+ return state;
79
+ }
80
+ /**
81
+ * Verify state parameter matches stored value
82
+ */
83
+ export function verifyState(state) {
84
+ const storedState = sessionStorage.getItem(STATE_KEY);
85
+ sessionStorage.removeItem(STATE_KEY);
86
+ return storedState === state;
87
+ }
88
+ //# sourceMappingURL=pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;GAEG;AACH,SAAS,SAAS;IAChB,IAAI,OAAO,UAAU,KAAK,WAAW,IAAI,UAAU,CAAC,MAAM,EAAE,CAAC;QAC3D,OAAO,UAAU,CAAC,MAAM,CAAC;IAC3B,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QACnD,OAAO,MAAM,CAAC,MAAM,CAAC;IACvB,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,SAAS,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IACnC,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAgB;IAC1D,MAAM,SAAS,GAAG,SAAS,EAAE,CAAC;IAE9B,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,8EAA8E;YAC9E,oEAAoE,CACrE,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IAC5D,OAAO,eAAe,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED;;GAEG;AACH,SAAS,eAAe,CAAC,MAAkB;IACzC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;IACpD,OAAO,MAAM;SACV,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;SACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,YAAY,GAAG,wBAAwB,CAAC;AAC9C,MAAM,SAAS,GAAG,sBAAsB,CAAC;AAEzC;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB;IAChD,cAAc,CAAC,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IACtD,cAAc,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,KAAK,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IACrC,cAAc,CAAC,OAAO,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC;IACzC,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,KAAa;IACvC,MAAM,WAAW,GAAG,cAAc,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IACtD,cAAc,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACrC,OAAO,WAAW,KAAK,KAAK,CAAC;AAC/B,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { type ReactNode } from "react";
2
+ import type { AvantIdConfig, AuthContextState } from "./types";
3
+ interface AvantIdProviderProps {
4
+ children: ReactNode;
5
+ config: AvantIdConfig;
6
+ }
7
+ export declare function AvantIdProvider({ children, config }: AvantIdProviderProps): import("react/jsx-runtime").JSX.Element;
8
+ export declare function useAuthContext(): AuthContextState;
9
+ export {};
10
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA+D,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACpG,OAAO,KAAK,EAAE,aAAa,EAAuB,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAkBpF,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,oBAAoB,2CAmPzE;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAIjD"}
@@ -0,0 +1,225 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useState, useEffect, useCallback } from "react";
3
+ import { generateCodeVerifier, generateCodeChallenge, storeCodeVerifier, retrieveCodeVerifier, generateState, verifyState, } from "./pkce";
4
+ import assert from "./assert";
5
+ const AuthContext = createContext(null);
6
+ // Token storage keys
7
+ const ACCESS_TOKEN_KEY = "avant_id_access_token";
8
+ const REFRESH_TOKEN_KEY = "avant_id_refresh_token";
9
+ const TOKEN_EXPIRY_KEY = "avant_id_token_expiry";
10
+ export function AvantIdProvider({ children, config }) {
11
+ const [user, setUser] = useState(null);
12
+ const [isLoading, setIsLoading] = useState(true);
13
+ const [accessToken, setAccessToken] = useState(null);
14
+ const [refreshToken, setRefreshToken] = useState(null);
15
+ const [tokenExpiry, setTokenExpiry] = useState(null);
16
+ // Normalize domain to ensure it has protocol
17
+ const baseUrl = config.domain.startsWith("http")
18
+ ? config.domain
19
+ : `https://${config.domain}`;
20
+ // Default redirect URI
21
+ const redirectUri = config.redirectUri || `${window.location.origin}/callback`;
22
+ // Store tokens
23
+ const storeTokens = useCallback((tokens) => {
24
+ const expiry = Date.now() + tokens.expires_in * 1000;
25
+ setAccessToken(tokens.access_token);
26
+ setRefreshToken(tokens.refresh_token ?? null);
27
+ setTokenExpiry(expiry);
28
+ // Always store access token in memory, optionally persist refresh token
29
+ if (config.persistSession && tokens.refresh_token) {
30
+ localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
31
+ localStorage.setItem(TOKEN_EXPIRY_KEY, expiry.toString());
32
+ }
33
+ }, [config.persistSession]);
34
+ // Clear tokens
35
+ const clearTokens = useCallback(() => {
36
+ setAccessToken(null);
37
+ setRefreshToken(null);
38
+ setTokenExpiry(null);
39
+ setUser(null);
40
+ localStorage.removeItem(ACCESS_TOKEN_KEY);
41
+ localStorage.removeItem(REFRESH_TOKEN_KEY);
42
+ localStorage.removeItem(TOKEN_EXPIRY_KEY);
43
+ }, []);
44
+ // Fetch user profile with access token
45
+ const fetchUser = useCallback(async (token) => {
46
+ try {
47
+ const response = await fetch(`${baseUrl}/auth/me`, {
48
+ headers: { Authorization: `Bearer ${token}` },
49
+ });
50
+ if (!response.ok) {
51
+ return null;
52
+ }
53
+ return await response.json();
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ }, [baseUrl]);
59
+ // Refresh access token using refresh token
60
+ const refreshAccessToken = useCallback(async (token) => {
61
+ try {
62
+ const response = await fetch(`${baseUrl}/auth/refresh`, {
63
+ method: "POST",
64
+ headers: { "Content-Type": "application/json" },
65
+ body: JSON.stringify({ refreshToken: token }),
66
+ });
67
+ if (!response.ok) {
68
+ return null;
69
+ }
70
+ return await response.json();
71
+ }
72
+ catch {
73
+ return null;
74
+ }
75
+ }, [baseUrl]);
76
+ // Get access token, refreshing if needed
77
+ const getAccessToken = useCallback(async () => {
78
+ // Check if current token is still valid (with 30s buffer)
79
+ if (accessToken && tokenExpiry && Date.now() < tokenExpiry - 30000) {
80
+ return accessToken;
81
+ }
82
+ // Try to refresh
83
+ if (refreshToken) {
84
+ const tokens = await refreshAccessToken(refreshToken);
85
+ if (tokens) {
86
+ storeTokens(tokens);
87
+ return tokens.access_token;
88
+ }
89
+ }
90
+ // No valid token
91
+ clearTokens();
92
+ return null;
93
+ }, [accessToken, tokenExpiry, refreshToken, refreshAccessToken, storeTokens, clearTokens]);
94
+ // Exchange authorization code for tokens
95
+ const exchangeCode = useCallback(async (code, codeVerifier) => {
96
+ try {
97
+ const response = await fetch(`${baseUrl}/oauth/token`, {
98
+ method: "POST",
99
+ headers: { "Content-Type": "application/json" },
100
+ body: JSON.stringify({
101
+ grant_type: "authorization_code",
102
+ code,
103
+ code_verifier: codeVerifier,
104
+ client_id: config.clientId,
105
+ redirect_uri: redirectUri,
106
+ }),
107
+ });
108
+ if (!response.ok) {
109
+ console.error("Token exchange failed:", await response.text());
110
+ return false;
111
+ }
112
+ const tokens = await response.json();
113
+ storeTokens(tokens);
114
+ // Fetch user profile
115
+ const userProfile = await fetchUser(tokens.access_token);
116
+ if (userProfile) {
117
+ setUser(userProfile);
118
+ }
119
+ return true;
120
+ }
121
+ catch (err) {
122
+ console.error("Token exchange error:", err);
123
+ return false;
124
+ }
125
+ }, [baseUrl, config.clientId, redirectUri, storeTokens, fetchUser]);
126
+ // Login - redirect to Avant ID
127
+ const login = useCallback(async () => {
128
+ const verifier = generateCodeVerifier();
129
+ const challenge = await generateCodeChallenge(verifier);
130
+ const state = generateState();
131
+ // Store verifier for later
132
+ storeCodeVerifier(verifier);
133
+ // Build authorization URL
134
+ const params = new URLSearchParams({
135
+ client_id: config.clientId,
136
+ redirect_uri: redirectUri,
137
+ response_type: "code",
138
+ code_challenge: challenge,
139
+ code_challenge_method: "S256",
140
+ state,
141
+ });
142
+ window.location.href = `${baseUrl}/oauth/authorize?${params.toString()}`;
143
+ }, [config.clientId, redirectUri, baseUrl]);
144
+ // Logout
145
+ const logout = useCallback(async () => {
146
+ // Optionally call logout endpoint to revoke refresh token
147
+ if (refreshToken) {
148
+ try {
149
+ await fetch(`${baseUrl}/auth/logout`, {
150
+ method: "POST",
151
+ headers: { "Content-Type": "application/json" },
152
+ body: JSON.stringify({ refreshToken }),
153
+ });
154
+ }
155
+ catch {
156
+ // Ignore errors, still clear local state
157
+ }
158
+ }
159
+ clearTokens();
160
+ }, [refreshToken, baseUrl, clearTokens]);
161
+ // Handle OAuth callback on mount
162
+ useEffect(() => {
163
+ const handleCallback = async () => {
164
+ const params = new URLSearchParams(window.location.search);
165
+ const code = params.get("code");
166
+ const state = params.get("state");
167
+ const error = params.get("error");
168
+ if (error) {
169
+ console.error("OAuth error:", error, params.get("error_description"));
170
+ setIsLoading(false);
171
+ return;
172
+ }
173
+ if (code && state) {
174
+ // Verify state
175
+ assert(verifyState(state), "State mismatch - possible CSRF attack");
176
+ // Get stored code verifier
177
+ const codeVerifier = retrieveCodeVerifier();
178
+ assert(codeVerifier, "No code verifier found");
179
+ // Exchange code for tokens
180
+ await exchangeCode(code, codeVerifier);
181
+ // Clean up URL
182
+ window.history.replaceState({}, "", window.location.pathname);
183
+ setIsLoading(false);
184
+ return;
185
+ }
186
+ // No callback params - check for existing session
187
+ if (config.persistSession) {
188
+ const storedRefresh = localStorage.getItem(REFRESH_TOKEN_KEY);
189
+ // Check for valid refresh token (not empty, not the string "undefined" or "null")
190
+ if (storedRefresh && storedRefresh !== "undefined" && storedRefresh !== "null") {
191
+ setRefreshToken(storedRefresh);
192
+ const tokens = await refreshAccessToken(storedRefresh);
193
+ if (tokens) {
194
+ storeTokens(tokens);
195
+ const userProfile = await fetchUser(tokens.access_token);
196
+ if (userProfile) {
197
+ setUser(userProfile);
198
+ }
199
+ }
200
+ else {
201
+ clearTokens();
202
+ }
203
+ }
204
+ }
205
+ setIsLoading(false);
206
+ };
207
+ handleCallback();
208
+ }, [config.persistSession, exchangeCode, refreshAccessToken, storeTokens, fetchUser, clearTokens]);
209
+ const value = {
210
+ user,
211
+ isLoading,
212
+ isAuthenticated: !!user,
213
+ login,
214
+ logout,
215
+ getAccessToken,
216
+ config,
217
+ };
218
+ return _jsx(AuthContext.Provider, { value: value, children: children });
219
+ }
220
+ export function useAuthContext() {
221
+ const context = useContext(AuthContext);
222
+ assert(context, "useAuth must be used within an AvantIdProvider");
223
+ return context;
224
+ }
225
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Configuration for the AvantIdProvider
3
+ */
4
+ export interface AvantIdConfig {
5
+ /** The domain of the Avant ID server (e.g., "auth.example.com" or "http://localhost:16000") */
6
+ domain: string;
7
+ /** The client ID of your application */
8
+ clientId: string;
9
+ /** The redirect URI for OAuth callbacks (defaults to current origin + /callback) */
10
+ redirectUri?: string;
11
+ /** Whether to persist refresh token in localStorage (default: false) */
12
+ persistSession?: boolean;
13
+ }
14
+ /**
15
+ * User object returned by the auth context
16
+ */
17
+ export interface User {
18
+ id: string;
19
+ email: string;
20
+ name?: string;
21
+ emailVerified?: boolean;
22
+ permissions?: Record<string, string>;
23
+ }
24
+ /**
25
+ * Token response from the OAuth token endpoint
26
+ */
27
+ export interface TokenResponse {
28
+ access_token: string;
29
+ refresh_token: string;
30
+ token_type: string;
31
+ expires_in: number;
32
+ }
33
+ /**
34
+ * Auth state exposed by useAuth hook
35
+ */
36
+ export interface AuthState {
37
+ /** Current user or null if not authenticated */
38
+ user: User | null;
39
+ /** Whether the SDK is initializing/checking auth state */
40
+ isLoading: boolean;
41
+ /** Whether the user is authenticated */
42
+ isAuthenticated: boolean;
43
+ /** Redirect to Avant ID login page */
44
+ login: () => Promise<void>;
45
+ /** Clear local auth state and optionally revoke tokens */
46
+ logout: () => Promise<void>;
47
+ /** Get the current access token (refreshes if expired) */
48
+ getAccessToken: () => Promise<string | null>;
49
+ }
50
+ /**
51
+ * Internal auth context state
52
+ */
53
+ export interface AuthContextState extends AuthState {
54
+ config: AvantIdConfig;
55
+ }
56
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,+FAA+F;IAC/F,MAAM,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,QAAQ,EAAE,MAAM,CAAC;IACjB,oFAAoF;IACpF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wEAAwE;IACxE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,gDAAgD;IAChD,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,0DAA0D;IAC1D,SAAS,EAAE,OAAO,CAAC;IACnB,wCAAwC;IACxC,eAAe,EAAE,OAAO,CAAC;IACzB,sCAAsC;IACtC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,0DAA0D;IAC1D,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,0DAA0D;IAC1D,cAAc,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED;;GAEG;AACH,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,MAAM,EAAE,aAAa,CAAC;CACvB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,8 +1,9 @@
1
1
  {
2
2
  "name": "@avantmedia/id-react",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",
7
8
  "private": false,
8
9
  "publishConfig": {
@@ -10,10 +11,14 @@
10
11
  },
11
12
  "exports": {
12
13
  ".": {
14
+ "types": "./dist/index.d.ts",
13
15
  "import": "./dist/index.js",
14
- "types": "./dist/index.d.ts"
16
+ "default": "./dist/index.js"
15
17
  }
16
18
  },
19
+ "files": [
20
+ "dist"
21
+ ],
17
22
  "scripts": {
18
23
  "build": "tsc",
19
24
  "dev": "tsc --watch"
package/src/index.ts DELETED
@@ -1,19 +0,0 @@
1
- // Components
2
- export { AvantIdProvider } from "./provider";
3
-
4
- // Hooks
5
- export { useAuth } from "./hooks";
6
-
7
- // Types
8
- export type {
9
- AvantIdConfig,
10
- User,
11
- TokenResponse,
12
- AuthState,
13
- } from "./types";
14
-
15
- // PKCE utilities (for advanced use cases)
16
- export {
17
- generateCodeVerifier,
18
- generateCodeChallenge,
19
- } from "./pkce";
package/src/pkce.ts DELETED
@@ -1,78 +0,0 @@
1
- /**
2
- * PKCE (Proof Key for Code Exchange) utilities
3
- * Used to secure the OAuth2 authorization code flow for SPAs
4
- */
5
-
6
- /**
7
- * Generate a cryptographically random code verifier
8
- * Must be between 43 and 128 characters
9
- */
10
- export function generateCodeVerifier(): string {
11
- const array = new Uint8Array(32);
12
- crypto.getRandomValues(array);
13
- return base64UrlEncode(array);
14
- }
15
-
16
- /**
17
- * Generate code challenge from verifier using S256 method
18
- * SHA256 hash of verifier, base64url encoded
19
- */
20
- export async function generateCodeChallenge(verifier: string): Promise<string> {
21
- const encoder = new TextEncoder();
22
- const data = encoder.encode(verifier);
23
- const hash = await crypto.subtle.digest("SHA-256", data);
24
- return base64UrlEncode(new Uint8Array(hash));
25
- }
26
-
27
- /**
28
- * Base64url encode (URL-safe base64 without padding)
29
- */
30
- function base64UrlEncode(buffer: Uint8Array): string {
31
- const base64 = btoa(String.fromCharCode(...buffer));
32
- return base64
33
- .replace(/\+/g, "-")
34
- .replace(/\//g, "_")
35
- .replace(/=+$/, "");
36
- }
37
-
38
- /**
39
- * Storage keys for PKCE state
40
- */
41
- const VERIFIER_KEY = "avant_id_pkce_verifier";
42
- const STATE_KEY = "avant_id_oauth_state";
43
-
44
- /**
45
- * Store code verifier in sessionStorage for later retrieval
46
- */
47
- export function storeCodeVerifier(verifier: string): void {
48
- sessionStorage.setItem(VERIFIER_KEY, verifier);
49
- }
50
-
51
- /**
52
- * Retrieve and clear stored code verifier
53
- */
54
- export function retrieveCodeVerifier(): string | null {
55
- const verifier = sessionStorage.getItem(VERIFIER_KEY);
56
- sessionStorage.removeItem(VERIFIER_KEY);
57
- return verifier;
58
- }
59
-
60
- /**
61
- * Generate and store OAuth state parameter for CSRF protection
62
- */
63
- export function generateState(): string {
64
- const array = new Uint8Array(16);
65
- crypto.getRandomValues(array);
66
- const state = base64UrlEncode(array);
67
- sessionStorage.setItem(STATE_KEY, state);
68
- return state;
69
- }
70
-
71
- /**
72
- * Verify state parameter matches stored value
73
- */
74
- export function verifyState(state: string): boolean {
75
- const storedState = sessionStorage.getItem(STATE_KEY);
76
- sessionStorage.removeItem(STATE_KEY);
77
- return storedState === state;
78
- }
package/src/provider.tsx DELETED
@@ -1,282 +0,0 @@
1
- import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from "react";
2
- import type { AvantIdConfig, User, TokenResponse, AuthContextState } from "./types";
3
- import {
4
- generateCodeVerifier,
5
- generateCodeChallenge,
6
- storeCodeVerifier,
7
- retrieveCodeVerifier,
8
- generateState,
9
- verifyState,
10
- } from "./pkce";
11
-
12
- const AuthContext = createContext<AuthContextState | null>(null);
13
-
14
- // Token storage keys
15
- const ACCESS_TOKEN_KEY = "avant_id_access_token";
16
- const REFRESH_TOKEN_KEY = "avant_id_refresh_token";
17
- const TOKEN_EXPIRY_KEY = "avant_id_token_expiry";
18
-
19
- interface AvantIdProviderProps {
20
- children: ReactNode;
21
- config: AvantIdConfig;
22
- }
23
-
24
- export function AvantIdProvider({ children, config }: AvantIdProviderProps) {
25
- const [user, setUser] = useState<User | null>(null);
26
- const [isLoading, setIsLoading] = useState(true);
27
- const [accessToken, setAccessToken] = useState<string | null>(null);
28
- const [refreshToken, setRefreshToken] = useState<string | null>(null);
29
- const [tokenExpiry, setTokenExpiry] = useState<number | null>(null);
30
-
31
- // Normalize domain to ensure it has protocol
32
- const baseUrl = config.domain.startsWith("http")
33
- ? config.domain
34
- : `https://${config.domain}`;
35
-
36
- // Default redirect URI
37
- const redirectUri = config.redirectUri || `${window.location.origin}/callback`;
38
-
39
- // Store tokens
40
- const storeTokens = useCallback((tokens: TokenResponse) => {
41
- const expiry = Date.now() + tokens.expires_in * 1000;
42
-
43
- setAccessToken(tokens.access_token);
44
- setRefreshToken(tokens.refresh_token);
45
- setTokenExpiry(expiry);
46
-
47
- // Always store access token in memory, optionally persist refresh token
48
- if (config.persistSession) {
49
- localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
50
- localStorage.setItem(TOKEN_EXPIRY_KEY, expiry.toString());
51
- }
52
- }, [config.persistSession]);
53
-
54
- // Clear tokens
55
- const clearTokens = useCallback(() => {
56
- setAccessToken(null);
57
- setRefreshToken(null);
58
- setTokenExpiry(null);
59
- setUser(null);
60
-
61
- localStorage.removeItem(ACCESS_TOKEN_KEY);
62
- localStorage.removeItem(REFRESH_TOKEN_KEY);
63
- localStorage.removeItem(TOKEN_EXPIRY_KEY);
64
- }, []);
65
-
66
- // Fetch user profile with access token
67
- const fetchUser = useCallback(async (token: string): Promise<User | null> => {
68
- try {
69
- const response = await fetch(`${baseUrl}/auth/me`, {
70
- headers: { Authorization: `Bearer ${token}` },
71
- });
72
-
73
- if (!response.ok) {
74
- return null;
75
- }
76
-
77
- return await response.json();
78
- } catch {
79
- return null;
80
- }
81
- }, [baseUrl]);
82
-
83
- // Refresh access token using refresh token
84
- const refreshAccessToken = useCallback(async (token: string): Promise<TokenResponse | null> => {
85
- try {
86
- const response = await fetch(`${baseUrl}/auth/refresh`, {
87
- method: "POST",
88
- headers: { "Content-Type": "application/json" },
89
- body: JSON.stringify({ refreshToken: token }),
90
- });
91
-
92
- if (!response.ok) {
93
- return null;
94
- }
95
-
96
- return await response.json();
97
- } catch {
98
- return null;
99
- }
100
- }, [baseUrl]);
101
-
102
- // Get access token, refreshing if needed
103
- const getAccessToken = useCallback(async (): Promise<string | null> => {
104
- // Check if current token is still valid (with 30s buffer)
105
- if (accessToken && tokenExpiry && Date.now() < tokenExpiry - 30000) {
106
- return accessToken;
107
- }
108
-
109
- // Try to refresh
110
- if (refreshToken) {
111
- const tokens = await refreshAccessToken(refreshToken);
112
- if (tokens) {
113
- storeTokens(tokens);
114
- return tokens.access_token;
115
- }
116
- }
117
-
118
- // No valid token
119
- clearTokens();
120
- return null;
121
- }, [accessToken, tokenExpiry, refreshToken, refreshAccessToken, storeTokens, clearTokens]);
122
-
123
- // Exchange authorization code for tokens
124
- const exchangeCode = useCallback(async (code: string, codeVerifier: string): Promise<boolean> => {
125
- try {
126
- const response = await fetch(`${baseUrl}/oauth/token`, {
127
- method: "POST",
128
- headers: { "Content-Type": "application/json" },
129
- body: JSON.stringify({
130
- grant_type: "authorization_code",
131
- code,
132
- code_verifier: codeVerifier,
133
- client_id: config.clientId,
134
- redirect_uri: redirectUri,
135
- }),
136
- });
137
-
138
- if (!response.ok) {
139
- console.error("Token exchange failed:", await response.text());
140
- return false;
141
- }
142
-
143
- const tokens: TokenResponse = await response.json();
144
- storeTokens(tokens);
145
-
146
- // Fetch user profile
147
- const userProfile = await fetchUser(tokens.access_token);
148
- if (userProfile) {
149
- setUser(userProfile);
150
- }
151
-
152
- return true;
153
- } catch (err) {
154
- console.error("Token exchange error:", err);
155
- return false;
156
- }
157
- }, [baseUrl, config.clientId, redirectUri, storeTokens, fetchUser]);
158
-
159
- // Login - redirect to Avant ID
160
- const login = useCallback(async () => {
161
- const verifier = generateCodeVerifier();
162
- const challenge = await generateCodeChallenge(verifier);
163
- const state = generateState();
164
-
165
- // Store verifier for later
166
- storeCodeVerifier(verifier);
167
-
168
- // Build authorization URL
169
- const params = new URLSearchParams({
170
- client_id: config.clientId,
171
- redirect_uri: redirectUri,
172
- response_type: "code",
173
- code_challenge: challenge,
174
- code_challenge_method: "S256",
175
- state,
176
- });
177
-
178
- window.location.href = `${baseUrl}/oauth/authorize?${params.toString()}`;
179
- }, [config.clientId, redirectUri, baseUrl]);
180
-
181
- // Logout
182
- const logout = useCallback(async () => {
183
- // Optionally call logout endpoint to revoke refresh token
184
- if (refreshToken) {
185
- try {
186
- await fetch(`${baseUrl}/auth/logout`, {
187
- method: "POST",
188
- headers: { "Content-Type": "application/json" },
189
- body: JSON.stringify({ refreshToken }),
190
- });
191
- } catch {
192
- // Ignore errors, still clear local state
193
- }
194
- }
195
-
196
- clearTokens();
197
- }, [refreshToken, baseUrl, clearTokens]);
198
-
199
- // Handle OAuth callback on mount
200
- useEffect(() => {
201
- const handleCallback = async () => {
202
- const params = new URLSearchParams(window.location.search);
203
- const code = params.get("code");
204
- const state = params.get("state");
205
- const error = params.get("error");
206
-
207
- if (error) {
208
- console.error("OAuth error:", error, params.get("error_description"));
209
- setIsLoading(false);
210
- return;
211
- }
212
-
213
- if (code && state) {
214
- // Verify state
215
- if (!verifyState(state)) {
216
- console.error("State mismatch - possible CSRF attack");
217
- setIsLoading(false);
218
- return;
219
- }
220
-
221
- // Get stored code verifier
222
- const codeVerifier = retrieveCodeVerifier();
223
- if (!codeVerifier) {
224
- console.error("No code verifier found");
225
- setIsLoading(false);
226
- return;
227
- }
228
-
229
- // Exchange code for tokens
230
- const success = await exchangeCode(code, codeVerifier);
231
-
232
- // Clean up URL
233
- window.history.replaceState({}, "", window.location.pathname);
234
-
235
- setIsLoading(false);
236
- return;
237
- }
238
-
239
- // No callback params - check for existing session
240
- if (config.persistSession) {
241
- const storedRefresh = localStorage.getItem(REFRESH_TOKEN_KEY);
242
- if (storedRefresh) {
243
- setRefreshToken(storedRefresh);
244
- const tokens = await refreshAccessToken(storedRefresh);
245
- if (tokens) {
246
- storeTokens(tokens);
247
- const userProfile = await fetchUser(tokens.access_token);
248
- if (userProfile) {
249
- setUser(userProfile);
250
- }
251
- } else {
252
- clearTokens();
253
- }
254
- }
255
- }
256
-
257
- setIsLoading(false);
258
- };
259
-
260
- handleCallback();
261
- }, [config.persistSession, exchangeCode, refreshAccessToken, storeTokens, fetchUser, clearTokens]);
262
-
263
- const value: AuthContextState = {
264
- user,
265
- isLoading,
266
- isAuthenticated: !!user,
267
- login,
268
- logout,
269
- getAccessToken,
270
- config,
271
- };
272
-
273
- return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
274
- }
275
-
276
- export function useAuthContext(): AuthContextState {
277
- const context = useContext(AuthContext);
278
- if (!context) {
279
- throw new Error("useAuth must be used within an AvantIdProvider");
280
- }
281
- return context;
282
- }
package/src/types.ts DELETED
@@ -1,59 +0,0 @@
1
- /**
2
- * Configuration for the AvantIdProvider
3
- */
4
- export interface AvantIdConfig {
5
- /** The domain of the Avant ID server (e.g., "auth.example.com" or "http://localhost:16000") */
6
- domain: string;
7
- /** The client ID of your application */
8
- clientId: string;
9
- /** The redirect URI for OAuth callbacks (defaults to current origin + /callback) */
10
- redirectUri?: string;
11
- /** Whether to persist refresh token in localStorage (default: false) */
12
- persistSession?: boolean;
13
- }
14
-
15
- /**
16
- * User object returned by the auth context
17
- */
18
- export interface User {
19
- id: string;
20
- email: string;
21
- name?: string;
22
- emailVerified?: boolean;
23
- permissions?: Record<string, string>;
24
- }
25
-
26
- /**
27
- * Token response from the OAuth token endpoint
28
- */
29
- export interface TokenResponse {
30
- access_token: string;
31
- refresh_token: string;
32
- token_type: string;
33
- expires_in: number;
34
- }
35
-
36
- /**
37
- * Auth state exposed by useAuth hook
38
- */
39
- export interface AuthState {
40
- /** Current user or null if not authenticated */
41
- user: User | null;
42
- /** Whether the SDK is initializing/checking auth state */
43
- isLoading: boolean;
44
- /** Whether the user is authenticated */
45
- isAuthenticated: boolean;
46
- /** Redirect to Avant ID login page */
47
- login: () => Promise<void>;
48
- /** Clear local auth state and optionally revoke tokens */
49
- logout: () => Promise<void>;
50
- /** Get the current access token (refreshes if expired) */
51
- getAccessToken: () => Promise<string | null>;
52
- }
53
-
54
- /**
55
- * Internal auth context state
56
- */
57
- export interface AuthContextState extends AuthState {
58
- config: AvantIdConfig;
59
- }
package/tsconfig.json DELETED
@@ -1,10 +0,0 @@
1
- {
2
- "extends": "../../tsconfig.json",
3
- "compilerOptions": {
4
- "outDir": "./dist",
5
- "rootDir": "./src",
6
- "jsx": "react-jsx",
7
- "lib": ["ES2020", "DOM", "DOM.Iterable"]
8
- },
9
- "include": ["src/**/*"]
10
- }