@avantmedia/id-react 0.0.1 → 0.0.2

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,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,CAK7E;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,71 @@
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 function generateCodeVerifier() {
10
+ const array = new Uint8Array(32);
11
+ crypto.getRandomValues(array);
12
+ return base64UrlEncode(array);
13
+ }
14
+ /**
15
+ * Generate code challenge from verifier using S256 method
16
+ * SHA256 hash of verifier, base64url encoded
17
+ */
18
+ export async function generateCodeChallenge(verifier) {
19
+ const encoder = new TextEncoder();
20
+ const data = encoder.encode(verifier);
21
+ const hash = await crypto.subtle.digest("SHA-256", data);
22
+ return base64UrlEncode(new Uint8Array(hash));
23
+ }
24
+ /**
25
+ * Base64url encode (URL-safe base64 without padding)
26
+ */
27
+ function base64UrlEncode(buffer) {
28
+ const base64 = btoa(String.fromCharCode(...buffer));
29
+ return base64
30
+ .replace(/\+/g, "-")
31
+ .replace(/\//g, "_")
32
+ .replace(/=+$/, "");
33
+ }
34
+ /**
35
+ * Storage keys for PKCE state
36
+ */
37
+ const VERIFIER_KEY = "avant_id_pkce_verifier";
38
+ const STATE_KEY = "avant_id_oauth_state";
39
+ /**
40
+ * Store code verifier in sessionStorage for later retrieval
41
+ */
42
+ export function storeCodeVerifier(verifier) {
43
+ sessionStorage.setItem(VERIFIER_KEY, verifier);
44
+ }
45
+ /**
46
+ * Retrieve and clear stored code verifier
47
+ */
48
+ export function retrieveCodeVerifier() {
49
+ const verifier = sessionStorage.getItem(VERIFIER_KEY);
50
+ sessionStorage.removeItem(VERIFIER_KEY);
51
+ return verifier;
52
+ }
53
+ /**
54
+ * Generate and store OAuth state parameter for CSRF protection
55
+ */
56
+ export function generateState() {
57
+ const array = new Uint8Array(16);
58
+ crypto.getRandomValues(array);
59
+ const state = base64UrlEncode(array);
60
+ sessionStorage.setItem(STATE_KEY, state);
61
+ return state;
62
+ }
63
+ /**
64
+ * Verify state parameter matches stored value
65
+ */
66
+ export function verifyState(state) {
67
+ const storedState = sessionStorage.getItem(STATE_KEY);
68
+ sessionStorage.removeItem(STATE_KEY);
69
+ return storedState === state;
70
+ }
71
+ //# sourceMappingURL=pkce.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pkce.js","sourceRoot":"","sources":["../src/pkce.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;IACjC,MAAM,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAC9B,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,QAAgB;IAC1D,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACzD,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;AAiBpF,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,SAAS,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;CACvB;AAED,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,oBAAoB,2CA0PzE;AAED,wBAAgB,cAAc,IAAI,gBAAgB,CAMjD"}
@@ -0,0 +1,233 @@
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
+ const AuthContext = createContext(null);
5
+ // Token storage keys
6
+ const ACCESS_TOKEN_KEY = "avant_id_access_token";
7
+ const REFRESH_TOKEN_KEY = "avant_id_refresh_token";
8
+ const TOKEN_EXPIRY_KEY = "avant_id_token_expiry";
9
+ export function AvantIdProvider({ children, config }) {
10
+ const [user, setUser] = useState(null);
11
+ const [isLoading, setIsLoading] = useState(true);
12
+ const [accessToken, setAccessToken] = useState(null);
13
+ const [refreshToken, setRefreshToken] = useState(null);
14
+ const [tokenExpiry, setTokenExpiry] = useState(null);
15
+ // Normalize domain to ensure it has protocol
16
+ const baseUrl = config.domain.startsWith("http")
17
+ ? config.domain
18
+ : `https://${config.domain}`;
19
+ // Default redirect URI
20
+ const redirectUri = config.redirectUri || `${window.location.origin}/callback`;
21
+ // Store tokens
22
+ const storeTokens = useCallback((tokens) => {
23
+ const expiry = Date.now() + tokens.expires_in * 1000;
24
+ setAccessToken(tokens.access_token);
25
+ setRefreshToken(tokens.refresh_token);
26
+ setTokenExpiry(expiry);
27
+ // Always store access token in memory, optionally persist refresh token
28
+ if (config.persistSession) {
29
+ localStorage.setItem(REFRESH_TOKEN_KEY, tokens.refresh_token);
30
+ localStorage.setItem(TOKEN_EXPIRY_KEY, expiry.toString());
31
+ }
32
+ }, [config.persistSession]);
33
+ // Clear tokens
34
+ const clearTokens = useCallback(() => {
35
+ setAccessToken(null);
36
+ setRefreshToken(null);
37
+ setTokenExpiry(null);
38
+ setUser(null);
39
+ localStorage.removeItem(ACCESS_TOKEN_KEY);
40
+ localStorage.removeItem(REFRESH_TOKEN_KEY);
41
+ localStorage.removeItem(TOKEN_EXPIRY_KEY);
42
+ }, []);
43
+ // Fetch user profile with access token
44
+ const fetchUser = useCallback(async (token) => {
45
+ try {
46
+ const response = await fetch(`${baseUrl}/auth/me`, {
47
+ headers: { Authorization: `Bearer ${token}` },
48
+ });
49
+ if (!response.ok) {
50
+ return null;
51
+ }
52
+ return await response.json();
53
+ }
54
+ catch {
55
+ return null;
56
+ }
57
+ }, [baseUrl]);
58
+ // Refresh access token using refresh token
59
+ const refreshAccessToken = useCallback(async (token) => {
60
+ try {
61
+ const response = await fetch(`${baseUrl}/auth/refresh`, {
62
+ method: "POST",
63
+ headers: { "Content-Type": "application/json" },
64
+ body: JSON.stringify({ refreshToken: token }),
65
+ });
66
+ if (!response.ok) {
67
+ return null;
68
+ }
69
+ return await response.json();
70
+ }
71
+ catch {
72
+ return null;
73
+ }
74
+ }, [baseUrl]);
75
+ // Get access token, refreshing if needed
76
+ const getAccessToken = useCallback(async () => {
77
+ // Check if current token is still valid (with 30s buffer)
78
+ if (accessToken && tokenExpiry && Date.now() < tokenExpiry - 30000) {
79
+ return accessToken;
80
+ }
81
+ // Try to refresh
82
+ if (refreshToken) {
83
+ const tokens = await refreshAccessToken(refreshToken);
84
+ if (tokens) {
85
+ storeTokens(tokens);
86
+ return tokens.access_token;
87
+ }
88
+ }
89
+ // No valid token
90
+ clearTokens();
91
+ return null;
92
+ }, [accessToken, tokenExpiry, refreshToken, refreshAccessToken, storeTokens, clearTokens]);
93
+ // Exchange authorization code for tokens
94
+ const exchangeCode = useCallback(async (code, codeVerifier) => {
95
+ try {
96
+ const response = await fetch(`${baseUrl}/oauth/token`, {
97
+ method: "POST",
98
+ headers: { "Content-Type": "application/json" },
99
+ body: JSON.stringify({
100
+ grant_type: "authorization_code",
101
+ code,
102
+ code_verifier: codeVerifier,
103
+ client_id: config.clientId,
104
+ redirect_uri: redirectUri,
105
+ }),
106
+ });
107
+ if (!response.ok) {
108
+ console.error("Token exchange failed:", await response.text());
109
+ return false;
110
+ }
111
+ const tokens = await response.json();
112
+ storeTokens(tokens);
113
+ // Fetch user profile
114
+ const userProfile = await fetchUser(tokens.access_token);
115
+ if (userProfile) {
116
+ setUser(userProfile);
117
+ }
118
+ return true;
119
+ }
120
+ catch (err) {
121
+ console.error("Token exchange error:", err);
122
+ return false;
123
+ }
124
+ }, [baseUrl, config.clientId, redirectUri, storeTokens, fetchUser]);
125
+ // Login - redirect to Avant ID
126
+ const login = useCallback(async () => {
127
+ const verifier = generateCodeVerifier();
128
+ const challenge = await generateCodeChallenge(verifier);
129
+ const state = generateState();
130
+ // Store verifier for later
131
+ storeCodeVerifier(verifier);
132
+ // Build authorization URL
133
+ const params = new URLSearchParams({
134
+ client_id: config.clientId,
135
+ redirect_uri: redirectUri,
136
+ response_type: "code",
137
+ code_challenge: challenge,
138
+ code_challenge_method: "S256",
139
+ state,
140
+ });
141
+ window.location.href = `${baseUrl}/oauth/authorize?${params.toString()}`;
142
+ }, [config.clientId, redirectUri, baseUrl]);
143
+ // Logout
144
+ const logout = useCallback(async () => {
145
+ // Optionally call logout endpoint to revoke refresh token
146
+ if (refreshToken) {
147
+ try {
148
+ await fetch(`${baseUrl}/auth/logout`, {
149
+ method: "POST",
150
+ headers: { "Content-Type": "application/json" },
151
+ body: JSON.stringify({ refreshToken }),
152
+ });
153
+ }
154
+ catch {
155
+ // Ignore errors, still clear local state
156
+ }
157
+ }
158
+ clearTokens();
159
+ }, [refreshToken, baseUrl, clearTokens]);
160
+ // Handle OAuth callback on mount
161
+ useEffect(() => {
162
+ const handleCallback = async () => {
163
+ const params = new URLSearchParams(window.location.search);
164
+ const code = params.get("code");
165
+ const state = params.get("state");
166
+ const error = params.get("error");
167
+ if (error) {
168
+ console.error("OAuth error:", error, params.get("error_description"));
169
+ setIsLoading(false);
170
+ return;
171
+ }
172
+ if (code && state) {
173
+ // Verify state
174
+ if (!verifyState(state)) {
175
+ console.error("State mismatch - possible CSRF attack");
176
+ setIsLoading(false);
177
+ return;
178
+ }
179
+ // Get stored code verifier
180
+ const codeVerifier = retrieveCodeVerifier();
181
+ if (!codeVerifier) {
182
+ console.error("No code verifier found");
183
+ setIsLoading(false);
184
+ return;
185
+ }
186
+ // Exchange code for tokens
187
+ const success = await exchangeCode(code, codeVerifier);
188
+ // Clean up URL
189
+ window.history.replaceState({}, "", window.location.pathname);
190
+ setIsLoading(false);
191
+ return;
192
+ }
193
+ // No callback params - check for existing session
194
+ if (config.persistSession) {
195
+ const storedRefresh = localStorage.getItem(REFRESH_TOKEN_KEY);
196
+ if (storedRefresh) {
197
+ setRefreshToken(storedRefresh);
198
+ const tokens = await refreshAccessToken(storedRefresh);
199
+ if (tokens) {
200
+ storeTokens(tokens);
201
+ const userProfile = await fetchUser(tokens.access_token);
202
+ if (userProfile) {
203
+ setUser(userProfile);
204
+ }
205
+ }
206
+ else {
207
+ clearTokens();
208
+ }
209
+ }
210
+ }
211
+ setIsLoading(false);
212
+ };
213
+ handleCallback();
214
+ }, [config.persistSession, exchangeCode, refreshAccessToken, storeTokens, fetchUser, clearTokens]);
215
+ const value = {
216
+ user,
217
+ isLoading,
218
+ isAuthenticated: !!user,
219
+ login,
220
+ logout,
221
+ getAccessToken,
222
+ config,
223
+ };
224
+ return _jsx(AuthContext.Provider, { value: value, children: children });
225
+ }
226
+ export function useAuthContext() {
227
+ const context = useContext(AuthContext);
228
+ if (!context) {
229
+ throw new Error("useAuth must be used within an AvantIdProvider");
230
+ }
231
+ return context;
232
+ }
233
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAkB,MAAM,OAAO,CAAC;AAEpG,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,iBAAiB,EACjB,oBAAoB,EACpB,aAAa,EACb,WAAW,GACZ,MAAM,QAAQ,CAAC;AAEhB,MAAM,WAAW,GAAG,aAAa,CAA0B,IAAI,CAAC,CAAC;AAEjE,qBAAqB;AACrB,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AACjD,MAAM,iBAAiB,GAAG,wBAAwB,CAAC;AACnD,MAAM,gBAAgB,GAAG,uBAAuB,CAAC;AAOjD,MAAM,UAAU,eAAe,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAwB;IACxE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAc,IAAI,CAAC,CAAC;IACpD,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IACjD,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IACtE,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAgB,IAAI,CAAC,CAAC;IAEpE,6CAA6C;IAC7C,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC;QAC9C,CAAC,CAAC,MAAM,CAAC,MAAM;QACf,CAAC,CAAC,WAAW,MAAM,CAAC,MAAM,EAAE,CAAC;IAE/B,uBAAuB;IACvB,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,WAAW,CAAC;IAE/E,eAAe;IACf,MAAM,WAAW,GAAG,WAAW,CAAC,CAAC,MAAqB,EAAE,EAAE;QACxD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,CAAC,UAAU,GAAG,IAAI,CAAC;QAErD,cAAc,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QACpC,eAAe,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;QACtC,cAAc,CAAC,MAAM,CAAC,CAAC;QAEvB,wEAAwE;QACxE,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;YAC1B,YAAY,CAAC,OAAO,CAAC,iBAAiB,EAAE,MAAM,CAAC,aAAa,CAAC,CAAC;YAC9D,YAAY,CAAC,OAAO,CAAC,gBAAgB,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC5D,CAAC;IACH,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAE5B,eAAe;IACf,MAAM,WAAW,GAAG,WAAW,CAAC,GAAG,EAAE;QACnC,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,eAAe,CAAC,IAAI,CAAC,CAAC;QACtB,cAAc,CAAC,IAAI,CAAC,CAAC;QACrB,OAAO,CAAC,IAAI,CAAC,CAAC;QAEd,YAAY,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;QAC1C,YAAY,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;QAC3C,YAAY,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC;IAC5C,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,uCAAuC;IACvC,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,EAAE,KAAa,EAAwB,EAAE;QAC1E,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,UAAU,EAAE;gBACjD,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE;aAC9C,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,2CAA2C;IAC3C,MAAM,kBAAkB,GAAG,WAAW,CAAC,KAAK,EAAE,KAAa,EAAiC,EAAE;QAC5F,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,eAAe,EAAE;gBACtD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,KAAK,EAAE,CAAC;aAC9C,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,yCAAyC;IACzC,MAAM,cAAc,GAAG,WAAW,CAAC,KAAK,IAA4B,EAAE;QACpE,0DAA0D;QAC1D,IAAI,WAAW,IAAI,WAAW,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,GAAG,KAAK,EAAE,CAAC;YACnE,OAAO,WAAW,CAAC;QACrB,CAAC;QAED,iBAAiB;QACjB,IAAI,YAAY,EAAE,CAAC;YACjB,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,YAAY,CAAC,CAAC;YACtD,IAAI,MAAM,EAAE,CAAC;gBACX,WAAW,CAAC,MAAM,CAAC,CAAC;gBACpB,OAAO,MAAM,CAAC,YAAY,CAAC;YAC7B,CAAC;QACH,CAAC;QAED,iBAAiB;QACjB,WAAW,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC,EAAE,CAAC,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;IAE3F,yCAAyC;IACzC,MAAM,YAAY,GAAG,WAAW,CAAC,KAAK,EAAE,IAAY,EAAE,YAAoB,EAAoB,EAAE;QAC9F,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;gBACrD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;gBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,UAAU,EAAE,oBAAoB;oBAChC,IAAI;oBACJ,aAAa,EAAE,YAAY;oBAC3B,SAAS,EAAE,MAAM,CAAC,QAAQ;oBAC1B,YAAY,EAAE,WAAW;iBAC1B,CAAC;aACH,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;gBAC/D,OAAO,KAAK,CAAC;YACf,CAAC;YAED,MAAM,MAAM,GAAkB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACpD,WAAW,CAAC,MAAM,CAAC,CAAC;YAEpB,qBAAqB;YACrB,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;YACzD,IAAI,WAAW,EAAE,CAAC;gBAChB,OAAO,CAAC,WAAW,CAAC,CAAC;YACvB,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,uBAAuB,EAAE,GAAG,CAAC,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC,CAAC;IAEpE,+BAA+B;IAC/B,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACnC,MAAM,QAAQ,GAAG,oBAAoB,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,MAAM,qBAAqB,CAAC,QAAQ,CAAC,CAAC;QACxD,MAAM,KAAK,GAAG,aAAa,EAAE,CAAC;QAE9B,2BAA2B;QAC3B,iBAAiB,CAAC,QAAQ,CAAC,CAAC;QAE5B,0BAA0B;QAC1B,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;YACjC,SAAS,EAAE,MAAM,CAAC,QAAQ;YAC1B,YAAY,EAAE,WAAW;YACzB,aAAa,EAAE,MAAM;YACrB,cAAc,EAAE,SAAS;YACzB,qBAAqB,EAAE,MAAM;YAC7B,KAAK;SACN,CAAC,CAAC;QAEH,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,GAAG,OAAO,oBAAoB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAC3E,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC,CAAC;IAE5C,SAAS;IACT,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,0DAA0D;QAC1D,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC;gBACH,MAAM,KAAK,CAAC,GAAG,OAAO,cAAc,EAAE;oBACpC,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,yCAAyC;YAC3C,CAAC;QACH,CAAC;QAED,WAAW,EAAE,CAAC;IAChB,CAAC,EAAE,CAAC,YAAY,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC;IAEzC,iCAAiC;IACjC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,cAAc,GAAG,KAAK,IAAI,EAAE;YAChC,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;YAC3D,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAChC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAClC,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAElC,IAAI,KAAK,EAAE,CAAC;gBACV,OAAO,CAAC,KAAK,CAAC,cAAc,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC;gBACtE,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO;YACT,CAAC;YAED,IAAI,IAAI,IAAI,KAAK,EAAE,CAAC;gBAClB,eAAe;gBACf,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxB,OAAO,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;oBACvD,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO;gBACT,CAAC;gBAED,2BAA2B;gBAC3B,MAAM,YAAY,GAAG,oBAAoB,EAAE,CAAC;gBAC5C,IAAI,CAAC,YAAY,EAAE,CAAC;oBAClB,OAAO,CAAC,KAAK,CAAC,wBAAwB,CAAC,CAAC;oBACxC,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO;gBACT,CAAC;gBAED,2BAA2B;gBAC3B,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;gBAEvD,eAAe;gBACf,MAAM,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;gBAE9D,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO;YACT,CAAC;YAED,kDAAkD;YAClD,IAAI,MAAM,CAAC,cAAc,EAAE,CAAC;gBAC1B,MAAM,aAAa,GAAG,YAAY,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC;gBAC9D,IAAI,aAAa,EAAE,CAAC;oBAClB,eAAe,CAAC,aAAa,CAAC,CAAC;oBAC/B,MAAM,MAAM,GAAG,MAAM,kBAAkB,CAAC,aAAa,CAAC,CAAC;oBACvD,IAAI,MAAM,EAAE,CAAC;wBACX,WAAW,CAAC,MAAM,CAAC,CAAC;wBACpB,MAAM,WAAW,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;wBACzD,IAAI,WAAW,EAAE,CAAC;4BAChB,OAAO,CAAC,WAAW,CAAC,CAAC;wBACvB,CAAC;oBACH,CAAC;yBAAM,CAAC;wBACN,WAAW,EAAE,CAAC;oBAChB,CAAC;gBACH,CAAC;YACH,CAAC;YAED,YAAY,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC,CAAC;QAEF,cAAc,EAAE,CAAC;IACnB,CAAC,EAAE,CAAC,MAAM,CAAC,cAAc,EAAE,YAAY,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC,CAAC;IAEnG,MAAM,KAAK,GAAqB;QAC9B,IAAI;QACJ,SAAS;QACT,eAAe,EAAE,CAAC,CAAC,IAAI;QACvB,KAAK;QACL,MAAM;QACN,cAAc;QACd,MAAM;KACP,CAAC;IAEF,OAAO,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,KAAK,YAAG,QAAQ,GAAwB,CAAC;AAC/E,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -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.2",
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
- }