@ccatto/react-auth 1.2.0 → 1.3.0

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/dist/index.cjs CHANGED
@@ -3,6 +3,47 @@
3
3
 
4
4
  var preferences = require('@capacitor/preferences');
5
5
 
6
+ // src/client/native-social-signin.ts
7
+ async function startNativeSocialSignIn(config) {
8
+ let Capacitor;
9
+ try {
10
+ ({ Capacitor } = await import('@capacitor/core'));
11
+ } catch {
12
+ return { error: "not_native" };
13
+ }
14
+ if (!Capacitor.isNativePlatform()) {
15
+ return { error: "not_native" };
16
+ }
17
+ if (!Capacitor.isPluginAvailable("InAppAuth")) {
18
+ return { error: "plugin_unavailable" };
19
+ }
20
+ const base = config.baseURL.replace(/\/$/, "");
21
+ const startPath = config.startPath ?? "/api/mobile-social-start";
22
+ const startUrl = `${base}${startPath}?provider=${encodeURIComponent(
23
+ config.provider
24
+ )}`;
25
+ const pluginSpecifier = "@ccatto/capacitor-inapp-auth";
26
+ try {
27
+ const { InAppAuth } = await import(pluginSpecifier);
28
+ const { url } = await InAppAuth.start({
29
+ url: startUrl,
30
+ callbackScheme: config.callbackScheme
31
+ });
32
+ const params = new URLSearchParams(url.split("?")[1] ?? "");
33
+ const code = params.get("code");
34
+ if (params.get("error") || !code) {
35
+ return { error: "failed" };
36
+ }
37
+ return { code };
38
+ } catch (err) {
39
+ const message = err && typeof err === "object" && "code" in err ? String(err.code) : "";
40
+ if (message === "CANCELED") {
41
+ return { error: "canceled" };
42
+ }
43
+ return { error: "failed" };
44
+ }
45
+ }
46
+
6
47
  // src/client/session-store.ts
7
48
  var currentSession = null;
8
49
  var listeners = /* @__PURE__ */ new Set();
@@ -63,6 +104,27 @@ var _JwtAuthService = class _JwtAuthService {
63
104
  throw error;
64
105
  }
65
106
  }
107
+ /**
108
+ * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.
109
+ * Used by the native deep-link / in-app-browser social sign-in flow after
110
+ * {@link startNativeSocialSignIn} returns a `code`. Requires the app's
111
+ * `IAuthApiService` to implement `exchangeAuthCode`.
112
+ */
113
+ async loginWithExchangeCode(code) {
114
+ if (!this.api.exchangeAuthCode) {
115
+ throw new Error("Exchange-code auth not configured");
116
+ }
117
+ try {
118
+ const data = await this.api.exchangeAuthCode(code);
119
+ await this.storage.setAccessToken(data.accessToken);
120
+ await this.storage.setRefreshToken(data.refreshToken);
121
+ this.log.info("Auth code exchange successful", { userId: data.user.id });
122
+ return data;
123
+ } catch (error) {
124
+ this.log.error("Auth code exchange failed", { error });
125
+ throw error;
126
+ }
127
+ }
66
128
  /** Register new user */
67
129
  async register(data) {
68
130
  try {
@@ -321,5 +383,6 @@ var CapacitorAuthStorage = class {
321
383
  exports.CapacitorAuthStorage = CapacitorAuthStorage;
322
384
  exports.JwtAuthService = JwtAuthService;
323
385
  exports.sessionStore = sessionStore;
386
+ exports.startNativeSocialSignIn = startNativeSocialSignIn;
324
387
  //# sourceMappingURL=index.cjs.map
325
388
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/session-store.ts","../src/services/jwt-auth.service.ts","../src/storage/capacitor-auth-storage.ts"],"names":["Preferences"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @ccatto/react-auth - Session Store\n *\n * Synchronous session state store for sharing auth session with Apollo Client.\n * Avoids network calls in Apollo's authLink.\n *\n * @example\n * // Write (from SessionSync component in React tree)\n * sessionStore.setSession(session);\n *\n * // Read (from Apollo authLink — synchronous, no network call)\n * const session = sessionStore.getSession();\n */\n\nimport type { CompatSession } from '../types/session';\n\nlet currentSession: CompatSession | null = null;\nconst listeners: Set<(session: CompatSession | null) => void> = new Set();\n\nexport const sessionStore = {\n /** Get the current session (synchronous, no network call) */\n getSession(): CompatSession | null {\n return currentSession ? Object.freeze({ ...currentSession }) : null;\n },\n\n /** Update the session (called by SessionSync component) */\n setSession(session: CompatSession | null): void {\n currentSession = session;\n const snapshot = [...listeners];\n snapshot.forEach((listener) => listener(session));\n },\n\n /** Subscribe to session changes */\n subscribe(listener: (session: CompatSession | null) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n /** Get the user ID from the current session (convenience method) */\n getUserId(): string | null {\n return currentSession?.user?.id ?? null;\n },\n};\n","/**\n * @ccatto/react-auth - JWT Auth Service\n *\n * Platform-agnostic JWT authentication service.\n * Handles token storage, login, register, refresh, and passkey auth.\n *\n * Uses IAuthStorage for token persistence and IAuthApiService for API calls.\n *\n * @example\n * ```typescript\n * import { JwtAuthService, CapacitorAuthStorage } from '@ccatto/react-auth';\n *\n * const storage = new CapacitorAuthStorage({ keyPrefix: 'myapp' });\n * const authService = new JwtAuthService(storage, myApiService, undefined, {\n * onSessionExpired: () => router.push('/login'),\n * });\n * await authService.login({ email, password });\n * ```\n */\nimport type { IAuthStorage } from '../storage/auth-storage.interface';\nimport type {\n AuthUser,\n IAuthApiService,\n IAuthLogger,\n LoginCredentials,\n LoginResponse,\n RegisterData,\n} from './auth-api.interface';\n\n// Re-export types for convenience\nexport type {\n AuthUser,\n LoginCredentials,\n RegisterData,\n LoginResponse,\n AuthTokens,\n} from './auth-api.interface';\n\nconst noopLogger: IAuthLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport interface JwtAuthServiceOptions {\n /** Called when session expires (refresh token fails). Use to redirect to login. */\n onSessionExpired?: () => void;\n}\n\nexport class JwtAuthService {\n private log: IAuthLogger;\n private options: JwtAuthServiceOptions;\n\n // Fix 7: Cache token expiry to avoid re-parsing JWT on every getAuthHeaders()\n private cachedTokenExp: { token: string; exp: number } | null = null;\n\n // Fix 5: Refresh deduplication + cooldown after failure\n private refreshPromise: Promise<string> | null = null;\n private lastRefreshFailure: number = 0;\n private static REFRESH_COOLDOWN_MS = 5000; // 5 second cooldown after failure\n\n constructor(\n private storage: IAuthStorage,\n private api: IAuthApiService,\n logger?: IAuthLogger,\n options?: JwtAuthServiceOptions,\n ) {\n this.log = logger || noopLogger;\n this.options = options || {};\n }\n\n /** Login with email and password */\n async login(credentials: LoginCredentials): Promise<LoginResponse> {\n try {\n const data = await this.api.login(credentials);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('User logged in successfully', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Login failed', { error });\n throw error;\n }\n }\n\n /** Register new user */\n async register(data: RegisterData): Promise<LoginResponse> {\n try {\n const result = await this.api.register(data);\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n this.log.info('User registered successfully', { userId: result.user.id });\n return result;\n } catch (error) {\n this.log.error('Registration failed', { error });\n throw error;\n }\n }\n\n /** Logout (clear tokens) */\n async logout(): Promise<void> {\n try {\n const refreshToken = await this.storage.getRefreshToken();\n if (refreshToken) {\n await this.api.logout(refreshToken).catch(() => {});\n }\n } finally {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.info('User logged out');\n }\n }\n\n /** Refresh access token */\n async refreshAccessToken(): Promise<string> {\n const refreshToken = await this.storage.getRefreshToken();\n if (!refreshToken) {\n throw new Error('No refresh token available');\n }\n\n try {\n const result = await this.api.refreshToken(refreshToken);\n await this.storage.setAccessToken(result.accessToken);\n return result.accessToken;\n } catch (error) {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.error('Token refresh failed', { error });\n this.options.onSessionExpired?.();\n throw new Error('Session expired');\n }\n }\n\n /** Get current access token */\n async getAccessToken(): Promise<string | null> {\n return this.storage.getAccessToken();\n }\n\n /** Check if a JWT token is expired or about to expire */\n private isTokenExpiredOrExpiring(\n token: string,\n bufferSeconds = 120,\n ): boolean {\n try {\n let exp: number;\n\n // Fix 7: Use cached expiry if token hasn't changed\n if (this.cachedTokenExp?.token === token) {\n exp = this.cachedTokenExp.exp;\n } else {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n exp = payload.exp;\n if (!exp) return true;\n this.cachedTokenExp = { token, exp };\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds >= exp - bufferSeconds;\n } catch {\n return true;\n }\n }\n\n /**\n * Get auth headers for API requests.\n * Proactively refreshes the access token if it's expired or about to expire.\n * Includes cooldown to prevent repeated refresh attempts after failure.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n let token = await this.getAccessToken();\n if (!token) return {};\n\n if (this.isTokenExpiredOrExpiring(token)) {\n // Fix 5: Skip refresh if we recently failed (cooldown)\n const timeSinceFailure = Date.now() - this.lastRefreshFailure;\n if (\n this.lastRefreshFailure > 0 &&\n timeSinceFailure < JwtAuthService.REFRESH_COOLDOWN_MS\n ) {\n return {};\n }\n\n try {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshAccessToken().finally(() => {\n this.refreshPromise = null;\n });\n }\n token = await this.refreshPromise;\n this.lastRefreshFailure = 0;\n } catch {\n this.lastRefreshFailure = Date.now();\n return {};\n }\n }\n\n return { Authorization: `Bearer ${token}` };\n }\n\n /** Check if user is authenticated */\n async isAuthenticated(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Check if tokens exist in storage */\n async hasTokens(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Decode JWT token (client-side only — for user info, NOT for security) */\n decodeToken(token: string): AuthUser | null {\n try {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n const payload = JSON.parse(jsonPayload);\n return {\n id: payload.sub,\n email: payload.email,\n name: payload.name || null,\n role: payload.role || 'user',\n playerID: payload.playerID,\n organizationId: payload.organizationId,\n };\n } catch {\n return null;\n }\n }\n\n /** Get current user from token (client-side decode) */\n async getCurrentUser(): Promise<AuthUser | null> {\n const token = await this.getAccessToken();\n if (!token) return null;\n return this.decodeToken(token);\n }\n\n /** Login with passkey (WebAuthn/FIDO2) */\n async loginWithPasskey(): Promise<LoginResponse> {\n if (\n !this.api.generatePasskeyAuthenticationOptions ||\n !this.api.verifyPasskeyAuthentication\n ) {\n throw new Error('Passkey authentication not configured');\n }\n\n const { startAuthentication, browserSupportsWebAuthn } =\n await import('@simplewebauthn/browser');\n\n if (!browserSupportsWebAuthn()) {\n throw new Error('WebAuthn is not supported on this device');\n }\n\n try {\n const { options, sessionId } =\n await this.api.generatePasskeyAuthenticationOptions();\n\n const authResponse = await startAuthentication({\n optionsJSON: JSON.parse(options),\n });\n\n const result = await this.api.verifyPasskeyAuthentication(\n sessionId,\n JSON.stringify(authResponse),\n );\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n this.log.info('Passkey auth successful', { userId: result.user.id });\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === 'NotAllowedError') {\n throw new Error('Passkey authentication was cancelled');\n }\n this.log.error('Passkey auth failed', { error });\n throw error;\n }\n }\n\n /** Send OTP to phone number for phone-based login */\n async sendPhoneOtp(\n phoneNumber: string,\n ): Promise<{ success: boolean; message: string; expiresIn: number }> {\n if (!this.api.sendPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.sendPhoneOtp(phoneNumber);\n if (result.success) {\n this.log.info('OTP sent', { phoneNumber: phoneNumber.slice(-4) });\n } else {\n this.log.warn('OTP send failed', {\n phoneNumber: phoneNumber.slice(-4),\n message: result.message,\n });\n }\n return result;\n } catch (error) {\n this.log.error('Failed to send OTP', { error });\n throw error;\n }\n }\n\n /** Verify OTP and login/register user */\n async verifyPhoneOtp(\n phoneNumber: string,\n code: string,\n ): Promise<LoginResponse & { isNewUser: boolean }> {\n if (!this.api.verifyPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.verifyPhoneOtp(phoneNumber, code);\n\n if (!result.success || !result.accessToken || !result.refreshToken) {\n throw new Error(result.message || 'Verification failed');\n }\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n const user = this.decodeToken(result.accessToken);\n if (!user) {\n throw new Error('Failed to decode user token');\n }\n\n this.log.info('Phone auth successful', {\n userId: user.id,\n isNewUser: result.isNewUser,\n });\n\n return {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n user,\n isNewUser: result.isNewUser ?? false,\n };\n } catch (error) {\n this.log.error('Phone OTP verification failed', { error });\n throw error;\n }\n }\n}\n","/**\n * @ccatto/react-auth - Capacitor Auth Storage\n *\n * Capacitor-based auth storage using @capacitor/preferences.\n * Works on BOTH web and mobile (no platform detection needed!).\n *\n * - Web: Uses localStorage\n * - iOS: Uses UserDefaults/Keychain\n * - Android: Uses SharedPreferences\n */\nimport { Preferences } from '@capacitor/preferences';\nimport type { IAuthStorage } from './auth-storage.interface';\n\nexport interface CapacitorAuthStorageOptions {\n /** Key prefix for stored tokens (default: 'catto_auth') */\n keyPrefix?: string;\n}\n\nexport class CapacitorAuthStorage implements IAuthStorage {\n private readonly ACCESS_TOKEN_KEY: string;\n private readonly REFRESH_TOKEN_KEY: string;\n\n constructor(options?: CapacitorAuthStorageOptions) {\n const prefix = options?.keyPrefix ?? 'catto_auth';\n this.ACCESS_TOKEN_KEY = `${prefix}_access_token`;\n this.REFRESH_TOKEN_KEY = `${prefix}_refresh_token`;\n }\n\n async setAccessToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.ACCESS_TOKEN_KEY,\n value: token,\n });\n }\n\n async getAccessToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.ACCESS_TOKEN_KEY });\n return value;\n }\n\n async setRefreshToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.REFRESH_TOKEN_KEY,\n value: token,\n });\n }\n\n async getRefreshToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.REFRESH_TOKEN_KEY });\n return value;\n }\n\n async clearTokens(): Promise<void> {\n await Promise.all([\n Preferences.remove({ key: this.ACCESS_TOKEN_KEY }),\n Preferences.remove({ key: this.REFRESH_TOKEN_KEY }),\n ]);\n }\n\n async hasTokens(): Promise<boolean> {\n const accessToken = await this.getAccessToken();\n return accessToken !== null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/client/native-social-signin.ts","../src/client/session-store.ts","../src/services/jwt-auth.service.ts","../src/storage/capacitor-auth-storage.ts"],"names":["Preferences"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * @ccatto/react-auth - Native social sign-in handoff\n *\n * Drives the in-app-browser OAuth flow on native (Capacitor/iOS) and returns the\n * one-time handoff code. Pair the returned code with\n * `JwtAuthService.loginWithExchangeCode(code)` to mint app JWTs.\n *\n * Why this exists (see @ccatto/capacitor-inapp-auth): OAuth can't run in an\n * embedded webview, and SFSafariViewController can't return to the app via a\n * custom scheme. ASWebAuthenticationSession (this plugin) can. The server flow:\n *\n * start → `${baseURL}${startPath}?provider=…` (initiates OAuth server-side)\n * provider auth → web callback → 302 `${callbackScheme}://auth-callback?code=…`\n * ASWebAuthenticationSession captures the redirect → we parse `code` here.\n *\n * This module hardcodes nothing app-specific — `baseURL` and `callbackScheme`\n * come from config. `@capacitor/core` and `@ccatto/capacitor-inapp-auth` are\n * optional peer deps, imported dynamically so web-only consumers never load them.\n */\n\nexport interface NativeSocialSignInConfig {\n /** Provider key understood by the server route (e.g. 'google', 'apple', 'facebook'). */\n provider: string;\n /** Base URL of the web app that hosts the OAuth start route (no trailing slash needed). */\n baseURL: string;\n /** Custom URL scheme registered in Info.plist, WITHOUT '://' (e.g. 'myapp'). */\n callbackScheme: string;\n /** Server route that initiates OAuth in-session. Default: '/api/mobile-social-start'. */\n startPath?: string;\n}\n\nexport type NativeSocialSignInReason =\n /** Not running in a native Capacitor context — caller should use the web OAuth redirect. */\n | 'not_native'\n /** Native, but the InAppAuth plugin isn't installed (old build) — caller should fall back. */\n | 'plugin_unavailable'\n /** User dismissed the auth sheet. */\n | 'canceled'\n /** The provider/callback returned an error, or no code came back. */\n | 'failed';\n\nexport type NativeSocialSignInResult =\n | { code: string }\n | { code?: undefined; error: NativeSocialSignInReason };\n\n/**\n * Run the native in-app-browser OAuth handoff. Resolves to `{ code }` on success,\n * or `{ error }` describing why it didn't complete (so the caller can fall back\n * to the standard web OAuth redirect when appropriate).\n *\n * Never throws — all failure modes are returned as `{ error }`.\n *\n * @example\n * ```ts\n * const res = await startNativeSocialSignIn({\n * provider: 'google',\n * baseURL: process.env.NEXT_PUBLIC_BASE_URL!,\n * callbackScheme: 'myapp',\n * });\n * if ('code' in res && res.code) {\n * await authService.loginWithExchangeCode(res.code);\n * } else if (res.error === 'not_native' || res.error === 'plugin_unavailable') {\n * await signInSocialOnWeb(provider); // standard redirect\n * }\n * ```\n */\nexport async function startNativeSocialSignIn(\n config: NativeSocialSignInConfig,\n): Promise<NativeSocialSignInResult> {\n let Capacitor: typeof import('@capacitor/core').Capacitor;\n try {\n ({ Capacitor } = await import('@capacitor/core'));\n } catch {\n return { error: 'not_native' };\n }\n\n if (!Capacitor.isNativePlatform()) {\n return { error: 'not_native' };\n }\n if (!Capacitor.isPluginAvailable('InAppAuth')) {\n return { error: 'plugin_unavailable' };\n }\n\n const base = config.baseURL.replace(/\\/$/, '');\n const startPath = config.startPath ?? '/api/mobile-social-start';\n const startUrl = `${base}${startPath}?provider=${encodeURIComponent(\n config.provider,\n )}`;\n\n // Optional peer dep: import via a non-literal specifier with a local type so\n // react-auth's type build never hard-depends on the plugin's .d.ts (it may be\n // unbuilt in a fresh monorepo checkout, or absent entirely in a web-only\n // consumer that doesn't install it).\n type InAppAuthModule = {\n InAppAuth: {\n start(opts: {\n url: string;\n callbackScheme: string;\n }): Promise<{ url: string }>;\n };\n };\n const pluginSpecifier = '@ccatto/capacitor-inapp-auth';\n\n try {\n const { InAppAuth } = (await import(pluginSpecifier)) as InAppAuthModule;\n const { url } = await InAppAuth.start({\n url: startUrl,\n callbackScheme: config.callbackScheme,\n });\n\n const params = new URLSearchParams(url.split('?')[1] ?? '');\n const code = params.get('code');\n if (params.get('error') || !code) {\n return { error: 'failed' };\n }\n return { code };\n } catch (err) {\n // The plugin rejects with code \"CANCELED\" when the user dismisses the sheet.\n const message =\n err && typeof err === 'object' && 'code' in err\n ? String((err as { code?: unknown }).code)\n : '';\n if (message === 'CANCELED') {\n return { error: 'canceled' };\n }\n return { error: 'failed' };\n }\n}\n","/**\n * @ccatto/react-auth - Session Store\n *\n * Synchronous session state store for sharing auth session with Apollo Client.\n * Avoids network calls in Apollo's authLink.\n *\n * @example\n * // Write (from SessionSync component in React tree)\n * sessionStore.setSession(session);\n *\n * // Read (from Apollo authLink — synchronous, no network call)\n * const session = sessionStore.getSession();\n */\n\nimport type { CompatSession } from '../types/session';\n\nlet currentSession: CompatSession | null = null;\nconst listeners: Set<(session: CompatSession | null) => void> = new Set();\n\nexport const sessionStore = {\n /** Get the current session (synchronous, no network call) */\n getSession(): CompatSession | null {\n return currentSession ? Object.freeze({ ...currentSession }) : null;\n },\n\n /** Update the session (called by SessionSync component) */\n setSession(session: CompatSession | null): void {\n currentSession = session;\n const snapshot = [...listeners];\n snapshot.forEach((listener) => listener(session));\n },\n\n /** Subscribe to session changes */\n subscribe(listener: (session: CompatSession | null) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n /** Get the user ID from the current session (convenience method) */\n getUserId(): string | null {\n return currentSession?.user?.id ?? null;\n },\n};\n","/**\n * @ccatto/react-auth - JWT Auth Service\n *\n * Platform-agnostic JWT authentication service.\n * Handles token storage, login, register, refresh, and passkey auth.\n *\n * Uses IAuthStorage for token persistence and IAuthApiService for API calls.\n *\n * @example\n * ```typescript\n * import { JwtAuthService, CapacitorAuthStorage } from '@ccatto/react-auth';\n *\n * const storage = new CapacitorAuthStorage({ keyPrefix: 'myapp' });\n * const authService = new JwtAuthService(storage, myApiService, undefined, {\n * onSessionExpired: () => router.push('/login'),\n * });\n * await authService.login({ email, password });\n * ```\n */\nimport type { IAuthStorage } from '../storage/auth-storage.interface';\nimport type {\n AuthUser,\n IAuthApiService,\n IAuthLogger,\n LoginCredentials,\n LoginResponse,\n RegisterData,\n} from './auth-api.interface';\n\n// Re-export types for convenience\nexport type {\n AuthUser,\n LoginCredentials,\n RegisterData,\n LoginResponse,\n AuthTokens,\n} from './auth-api.interface';\n\nconst noopLogger: IAuthLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport interface JwtAuthServiceOptions {\n /** Called when session expires (refresh token fails). Use to redirect to login. */\n onSessionExpired?: () => void;\n}\n\nexport class JwtAuthService {\n private log: IAuthLogger;\n private options: JwtAuthServiceOptions;\n\n // Fix 7: Cache token expiry to avoid re-parsing JWT on every getAuthHeaders()\n private cachedTokenExp: { token: string; exp: number } | null = null;\n\n // Fix 5: Refresh deduplication + cooldown after failure\n private refreshPromise: Promise<string> | null = null;\n private lastRefreshFailure: number = 0;\n private static REFRESH_COOLDOWN_MS = 5000; // 5 second cooldown after failure\n\n constructor(\n private storage: IAuthStorage,\n private api: IAuthApiService,\n logger?: IAuthLogger,\n options?: JwtAuthServiceOptions,\n ) {\n this.log = logger || noopLogger;\n this.options = options || {};\n }\n\n /** Login with email and password */\n async login(credentials: LoginCredentials): Promise<LoginResponse> {\n try {\n const data = await this.api.login(credentials);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('User logged in successfully', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Login failed', { error });\n throw error;\n }\n }\n\n /**\n * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.\n * Used by the native deep-link / in-app-browser social sign-in flow after\n * {@link startNativeSocialSignIn} returns a `code`. Requires the app's\n * `IAuthApiService` to implement `exchangeAuthCode`.\n */\n async loginWithExchangeCode(code: string): Promise<LoginResponse> {\n if (!this.api.exchangeAuthCode) {\n throw new Error('Exchange-code auth not configured');\n }\n try {\n const data = await this.api.exchangeAuthCode(code);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('Auth code exchange successful', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Auth code exchange failed', { error });\n throw error;\n }\n }\n\n /** Register new user */\n async register(data: RegisterData): Promise<LoginResponse> {\n try {\n const result = await this.api.register(data);\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n this.log.info('User registered successfully', { userId: result.user.id });\n return result;\n } catch (error) {\n this.log.error('Registration failed', { error });\n throw error;\n }\n }\n\n /** Logout (clear tokens) */\n async logout(): Promise<void> {\n try {\n const refreshToken = await this.storage.getRefreshToken();\n if (refreshToken) {\n await this.api.logout(refreshToken).catch(() => {});\n }\n } finally {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.info('User logged out');\n }\n }\n\n /** Refresh access token */\n async refreshAccessToken(): Promise<string> {\n const refreshToken = await this.storage.getRefreshToken();\n if (!refreshToken) {\n throw new Error('No refresh token available');\n }\n\n try {\n const result = await this.api.refreshToken(refreshToken);\n await this.storage.setAccessToken(result.accessToken);\n return result.accessToken;\n } catch (error) {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.error('Token refresh failed', { error });\n this.options.onSessionExpired?.();\n throw new Error('Session expired');\n }\n }\n\n /** Get current access token */\n async getAccessToken(): Promise<string | null> {\n return this.storage.getAccessToken();\n }\n\n /** Check if a JWT token is expired or about to expire */\n private isTokenExpiredOrExpiring(\n token: string,\n bufferSeconds = 120,\n ): boolean {\n try {\n let exp: number;\n\n // Fix 7: Use cached expiry if token hasn't changed\n if (this.cachedTokenExp?.token === token) {\n exp = this.cachedTokenExp.exp;\n } else {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n exp = payload.exp;\n if (!exp) return true;\n this.cachedTokenExp = { token, exp };\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds >= exp - bufferSeconds;\n } catch {\n return true;\n }\n }\n\n /**\n * Get auth headers for API requests.\n * Proactively refreshes the access token if it's expired or about to expire.\n * Includes cooldown to prevent repeated refresh attempts after failure.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n let token = await this.getAccessToken();\n if (!token) return {};\n\n if (this.isTokenExpiredOrExpiring(token)) {\n // Fix 5: Skip refresh if we recently failed (cooldown)\n const timeSinceFailure = Date.now() - this.lastRefreshFailure;\n if (\n this.lastRefreshFailure > 0 &&\n timeSinceFailure < JwtAuthService.REFRESH_COOLDOWN_MS\n ) {\n return {};\n }\n\n try {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshAccessToken().finally(() => {\n this.refreshPromise = null;\n });\n }\n token = await this.refreshPromise;\n this.lastRefreshFailure = 0;\n } catch {\n this.lastRefreshFailure = Date.now();\n return {};\n }\n }\n\n return { Authorization: `Bearer ${token}` };\n }\n\n /** Check if user is authenticated */\n async isAuthenticated(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Check if tokens exist in storage */\n async hasTokens(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Decode JWT token (client-side only — for user info, NOT for security) */\n decodeToken(token: string): AuthUser | null {\n try {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n const payload = JSON.parse(jsonPayload);\n return {\n id: payload.sub,\n email: payload.email,\n name: payload.name || null,\n role: payload.role || 'user',\n playerID: payload.playerID,\n organizationId: payload.organizationId,\n };\n } catch {\n return null;\n }\n }\n\n /** Get current user from token (client-side decode) */\n async getCurrentUser(): Promise<AuthUser | null> {\n const token = await this.getAccessToken();\n if (!token) return null;\n return this.decodeToken(token);\n }\n\n /** Login with passkey (WebAuthn/FIDO2) */\n async loginWithPasskey(): Promise<LoginResponse> {\n if (\n !this.api.generatePasskeyAuthenticationOptions ||\n !this.api.verifyPasskeyAuthentication\n ) {\n throw new Error('Passkey authentication not configured');\n }\n\n const { startAuthentication, browserSupportsWebAuthn } =\n await import('@simplewebauthn/browser');\n\n if (!browserSupportsWebAuthn()) {\n throw new Error('WebAuthn is not supported on this device');\n }\n\n try {\n const { options, sessionId } =\n await this.api.generatePasskeyAuthenticationOptions();\n\n const authResponse = await startAuthentication({\n optionsJSON: JSON.parse(options),\n });\n\n const result = await this.api.verifyPasskeyAuthentication(\n sessionId,\n JSON.stringify(authResponse),\n );\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n this.log.info('Passkey auth successful', { userId: result.user.id });\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === 'NotAllowedError') {\n throw new Error('Passkey authentication was cancelled');\n }\n this.log.error('Passkey auth failed', { error });\n throw error;\n }\n }\n\n /** Send OTP to phone number for phone-based login */\n async sendPhoneOtp(\n phoneNumber: string,\n ): Promise<{ success: boolean; message: string; expiresIn: number }> {\n if (!this.api.sendPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.sendPhoneOtp(phoneNumber);\n if (result.success) {\n this.log.info('OTP sent', { phoneNumber: phoneNumber.slice(-4) });\n } else {\n this.log.warn('OTP send failed', {\n phoneNumber: phoneNumber.slice(-4),\n message: result.message,\n });\n }\n return result;\n } catch (error) {\n this.log.error('Failed to send OTP', { error });\n throw error;\n }\n }\n\n /** Verify OTP and login/register user */\n async verifyPhoneOtp(\n phoneNumber: string,\n code: string,\n ): Promise<LoginResponse & { isNewUser: boolean }> {\n if (!this.api.verifyPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.verifyPhoneOtp(phoneNumber, code);\n\n if (!result.success || !result.accessToken || !result.refreshToken) {\n throw new Error(result.message || 'Verification failed');\n }\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n const user = this.decodeToken(result.accessToken);\n if (!user) {\n throw new Error('Failed to decode user token');\n }\n\n this.log.info('Phone auth successful', {\n userId: user.id,\n isNewUser: result.isNewUser,\n });\n\n return {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n user,\n isNewUser: result.isNewUser ?? false,\n };\n } catch (error) {\n this.log.error('Phone OTP verification failed', { error });\n throw error;\n }\n }\n}\n","/**\n * @ccatto/react-auth - Capacitor Auth Storage\n *\n * Capacitor-based auth storage using @capacitor/preferences.\n * Works on BOTH web and mobile (no platform detection needed!).\n *\n * - Web: Uses localStorage\n * - iOS: Uses UserDefaults/Keychain\n * - Android: Uses SharedPreferences\n */\nimport { Preferences } from '@capacitor/preferences';\nimport type { IAuthStorage } from './auth-storage.interface';\n\nexport interface CapacitorAuthStorageOptions {\n /** Key prefix for stored tokens (default: 'catto_auth') */\n keyPrefix?: string;\n}\n\nexport class CapacitorAuthStorage implements IAuthStorage {\n private readonly ACCESS_TOKEN_KEY: string;\n private readonly REFRESH_TOKEN_KEY: string;\n\n constructor(options?: CapacitorAuthStorageOptions) {\n const prefix = options?.keyPrefix ?? 'catto_auth';\n this.ACCESS_TOKEN_KEY = `${prefix}_access_token`;\n this.REFRESH_TOKEN_KEY = `${prefix}_refresh_token`;\n }\n\n async setAccessToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.ACCESS_TOKEN_KEY,\n value: token,\n });\n }\n\n async getAccessToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.ACCESS_TOKEN_KEY });\n return value;\n }\n\n async setRefreshToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.REFRESH_TOKEN_KEY,\n value: token,\n });\n }\n\n async getRefreshToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.REFRESH_TOKEN_KEY });\n return value;\n }\n\n async clearTokens(): Promise<void> {\n await Promise.all([\n Preferences.remove({ key: this.ACCESS_TOKEN_KEY }),\n Preferences.remove({ key: this.REFRESH_TOKEN_KEY }),\n ]);\n }\n\n async hasTokens(): Promise<boolean> {\n const accessToken = await this.getAccessToken();\n return accessToken !== null;\n }\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -78,6 +78,73 @@ interface AuthStoreUser {
78
78
  }>;
79
79
  }
80
80
 
81
+ /**
82
+ * @ccatto/react-auth - Native social sign-in handoff
83
+ *
84
+ * Drives the in-app-browser OAuth flow on native (Capacitor/iOS) and returns the
85
+ * one-time handoff code. Pair the returned code with
86
+ * `JwtAuthService.loginWithExchangeCode(code)` to mint app JWTs.
87
+ *
88
+ * Why this exists (see @ccatto/capacitor-inapp-auth): OAuth can't run in an
89
+ * embedded webview, and SFSafariViewController can't return to the app via a
90
+ * custom scheme. ASWebAuthenticationSession (this plugin) can. The server flow:
91
+ *
92
+ * start → `${baseURL}${startPath}?provider=…` (initiates OAuth server-side)
93
+ * provider auth → web callback → 302 `${callbackScheme}://auth-callback?code=…`
94
+ * ASWebAuthenticationSession captures the redirect → we parse `code` here.
95
+ *
96
+ * This module hardcodes nothing app-specific — `baseURL` and `callbackScheme`
97
+ * come from config. `@capacitor/core` and `@ccatto/capacitor-inapp-auth` are
98
+ * optional peer deps, imported dynamically so web-only consumers never load them.
99
+ */
100
+ interface NativeSocialSignInConfig {
101
+ /** Provider key understood by the server route (e.g. 'google', 'apple', 'facebook'). */
102
+ provider: string;
103
+ /** Base URL of the web app that hosts the OAuth start route (no trailing slash needed). */
104
+ baseURL: string;
105
+ /** Custom URL scheme registered in Info.plist, WITHOUT '://' (e.g. 'myapp'). */
106
+ callbackScheme: string;
107
+ /** Server route that initiates OAuth in-session. Default: '/api/mobile-social-start'. */
108
+ startPath?: string;
109
+ }
110
+ type NativeSocialSignInReason =
111
+ /** Not running in a native Capacitor context — caller should use the web OAuth redirect. */
112
+ 'not_native'
113
+ /** Native, but the InAppAuth plugin isn't installed (old build) — caller should fall back. */
114
+ | 'plugin_unavailable'
115
+ /** User dismissed the auth sheet. */
116
+ | 'canceled'
117
+ /** The provider/callback returned an error, or no code came back. */
118
+ | 'failed';
119
+ type NativeSocialSignInResult = {
120
+ code: string;
121
+ } | {
122
+ code?: undefined;
123
+ error: NativeSocialSignInReason;
124
+ };
125
+ /**
126
+ * Run the native in-app-browser OAuth handoff. Resolves to `{ code }` on success,
127
+ * or `{ error }` describing why it didn't complete (so the caller can fall back
128
+ * to the standard web OAuth redirect when appropriate).
129
+ *
130
+ * Never throws — all failure modes are returned as `{ error }`.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const res = await startNativeSocialSignIn({
135
+ * provider: 'google',
136
+ * baseURL: process.env.NEXT_PUBLIC_BASE_URL!,
137
+ * callbackScheme: 'myapp',
138
+ * });
139
+ * if ('code' in res && res.code) {
140
+ * await authService.loginWithExchangeCode(res.code);
141
+ * } else if (res.error === 'not_native' || res.error === 'plugin_unavailable') {
142
+ * await signInSocialOnWeb(provider); // standard redirect
143
+ * }
144
+ * ```
145
+ */
146
+ declare function startNativeSocialSignIn(config: NativeSocialSignInConfig): Promise<NativeSocialSignInResult>;
147
+
81
148
  /**
82
149
  * @ccatto/react-auth - Session Store
83
150
  *
@@ -187,6 +254,12 @@ interface IAuthApiService {
187
254
  refreshToken(refreshToken: string): Promise<{
188
255
  accessToken: string;
189
256
  }>;
257
+ /**
258
+ * Exchange a one-time mobile-OAuth handoff code (minted by the web
259
+ * `mobile-auth-callback` route) for app JWTs. Required for native social
260
+ * sign-in via `startNativeSocialSignIn`; optional otherwise.
261
+ */
262
+ exchangeAuthCode?(code: string): Promise<LoginResponse>;
190
263
  forgotPassword?(email: string): Promise<{
191
264
  message: string;
192
265
  resetToken?: string;
@@ -245,6 +318,13 @@ declare class JwtAuthService {
245
318
  constructor(storage: IAuthStorage, api: IAuthApiService, logger?: IAuthLogger, options?: JwtAuthServiceOptions);
246
319
  /** Login with email and password */
247
320
  login(credentials: LoginCredentials): Promise<LoginResponse>;
321
+ /**
322
+ * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.
323
+ * Used by the native deep-link / in-app-browser social sign-in flow after
324
+ * {@link startNativeSocialSignIn} returns a `code`. Requires the app's
325
+ * `IAuthApiService` to implement `exchangeAuthCode`.
326
+ */
327
+ loginWithExchangeCode(code: string): Promise<LoginResponse>;
248
328
  /** Register new user */
249
329
  register(data: RegisterData): Promise<LoginResponse>;
250
330
  /** Logout (clear tokens) */
@@ -299,4 +379,4 @@ declare class CapacitorAuthStorage implements IAuthStorage {
299
379
  hasTokens(): Promise<boolean>;
300
380
  }
301
381
 
302
- export { type AuthStoreUser, type AuthTokens, type AuthUser, CapacitorAuthStorage, type CapacitorAuthStorageOptions, type CompatSession, type CompatSessionUser, type EnrichedSession, type EnrichedUser, type IAuthApiService, type IAuthLogger, type IAuthStorage, JwtAuthService, type LoginCredentials, type LoginResponse, type PasskeyAuthenticationOptions, type RegisterData, type SendOtpResponse, type VerifyOtpFailure, type VerifyOtpResponse, type VerifyOtpSuccess, sessionStore };
382
+ export { type AuthStoreUser, type AuthTokens, type AuthUser, CapacitorAuthStorage, type CapacitorAuthStorageOptions, type CompatSession, type CompatSessionUser, type EnrichedSession, type EnrichedUser, type IAuthApiService, type IAuthLogger, type IAuthStorage, JwtAuthService, type LoginCredentials, type LoginResponse, type NativeSocialSignInConfig, type NativeSocialSignInReason, type NativeSocialSignInResult, type PasskeyAuthenticationOptions, type RegisterData, type SendOtpResponse, type VerifyOtpFailure, type VerifyOtpResponse, type VerifyOtpSuccess, sessionStore, startNativeSocialSignIn };
package/dist/index.d.ts CHANGED
@@ -78,6 +78,73 @@ interface AuthStoreUser {
78
78
  }>;
79
79
  }
80
80
 
81
+ /**
82
+ * @ccatto/react-auth - Native social sign-in handoff
83
+ *
84
+ * Drives the in-app-browser OAuth flow on native (Capacitor/iOS) and returns the
85
+ * one-time handoff code. Pair the returned code with
86
+ * `JwtAuthService.loginWithExchangeCode(code)` to mint app JWTs.
87
+ *
88
+ * Why this exists (see @ccatto/capacitor-inapp-auth): OAuth can't run in an
89
+ * embedded webview, and SFSafariViewController can't return to the app via a
90
+ * custom scheme. ASWebAuthenticationSession (this plugin) can. The server flow:
91
+ *
92
+ * start → `${baseURL}${startPath}?provider=…` (initiates OAuth server-side)
93
+ * provider auth → web callback → 302 `${callbackScheme}://auth-callback?code=…`
94
+ * ASWebAuthenticationSession captures the redirect → we parse `code` here.
95
+ *
96
+ * This module hardcodes nothing app-specific — `baseURL` and `callbackScheme`
97
+ * come from config. `@capacitor/core` and `@ccatto/capacitor-inapp-auth` are
98
+ * optional peer deps, imported dynamically so web-only consumers never load them.
99
+ */
100
+ interface NativeSocialSignInConfig {
101
+ /** Provider key understood by the server route (e.g. 'google', 'apple', 'facebook'). */
102
+ provider: string;
103
+ /** Base URL of the web app that hosts the OAuth start route (no trailing slash needed). */
104
+ baseURL: string;
105
+ /** Custom URL scheme registered in Info.plist, WITHOUT '://' (e.g. 'myapp'). */
106
+ callbackScheme: string;
107
+ /** Server route that initiates OAuth in-session. Default: '/api/mobile-social-start'. */
108
+ startPath?: string;
109
+ }
110
+ type NativeSocialSignInReason =
111
+ /** Not running in a native Capacitor context — caller should use the web OAuth redirect. */
112
+ 'not_native'
113
+ /** Native, but the InAppAuth plugin isn't installed (old build) — caller should fall back. */
114
+ | 'plugin_unavailable'
115
+ /** User dismissed the auth sheet. */
116
+ | 'canceled'
117
+ /** The provider/callback returned an error, or no code came back. */
118
+ | 'failed';
119
+ type NativeSocialSignInResult = {
120
+ code: string;
121
+ } | {
122
+ code?: undefined;
123
+ error: NativeSocialSignInReason;
124
+ };
125
+ /**
126
+ * Run the native in-app-browser OAuth handoff. Resolves to `{ code }` on success,
127
+ * or `{ error }` describing why it didn't complete (so the caller can fall back
128
+ * to the standard web OAuth redirect when appropriate).
129
+ *
130
+ * Never throws — all failure modes are returned as `{ error }`.
131
+ *
132
+ * @example
133
+ * ```ts
134
+ * const res = await startNativeSocialSignIn({
135
+ * provider: 'google',
136
+ * baseURL: process.env.NEXT_PUBLIC_BASE_URL!,
137
+ * callbackScheme: 'myapp',
138
+ * });
139
+ * if ('code' in res && res.code) {
140
+ * await authService.loginWithExchangeCode(res.code);
141
+ * } else if (res.error === 'not_native' || res.error === 'plugin_unavailable') {
142
+ * await signInSocialOnWeb(provider); // standard redirect
143
+ * }
144
+ * ```
145
+ */
146
+ declare function startNativeSocialSignIn(config: NativeSocialSignInConfig): Promise<NativeSocialSignInResult>;
147
+
81
148
  /**
82
149
  * @ccatto/react-auth - Session Store
83
150
  *
@@ -187,6 +254,12 @@ interface IAuthApiService {
187
254
  refreshToken(refreshToken: string): Promise<{
188
255
  accessToken: string;
189
256
  }>;
257
+ /**
258
+ * Exchange a one-time mobile-OAuth handoff code (minted by the web
259
+ * `mobile-auth-callback` route) for app JWTs. Required for native social
260
+ * sign-in via `startNativeSocialSignIn`; optional otherwise.
261
+ */
262
+ exchangeAuthCode?(code: string): Promise<LoginResponse>;
190
263
  forgotPassword?(email: string): Promise<{
191
264
  message: string;
192
265
  resetToken?: string;
@@ -245,6 +318,13 @@ declare class JwtAuthService {
245
318
  constructor(storage: IAuthStorage, api: IAuthApiService, logger?: IAuthLogger, options?: JwtAuthServiceOptions);
246
319
  /** Login with email and password */
247
320
  login(credentials: LoginCredentials): Promise<LoginResponse>;
321
+ /**
322
+ * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.
323
+ * Used by the native deep-link / in-app-browser social sign-in flow after
324
+ * {@link startNativeSocialSignIn} returns a `code`. Requires the app's
325
+ * `IAuthApiService` to implement `exchangeAuthCode`.
326
+ */
327
+ loginWithExchangeCode(code: string): Promise<LoginResponse>;
248
328
  /** Register new user */
249
329
  register(data: RegisterData): Promise<LoginResponse>;
250
330
  /** Logout (clear tokens) */
@@ -299,4 +379,4 @@ declare class CapacitorAuthStorage implements IAuthStorage {
299
379
  hasTokens(): Promise<boolean>;
300
380
  }
301
381
 
302
- export { type AuthStoreUser, type AuthTokens, type AuthUser, CapacitorAuthStorage, type CapacitorAuthStorageOptions, type CompatSession, type CompatSessionUser, type EnrichedSession, type EnrichedUser, type IAuthApiService, type IAuthLogger, type IAuthStorage, JwtAuthService, type LoginCredentials, type LoginResponse, type PasskeyAuthenticationOptions, type RegisterData, type SendOtpResponse, type VerifyOtpFailure, type VerifyOtpResponse, type VerifyOtpSuccess, sessionStore };
382
+ export { type AuthStoreUser, type AuthTokens, type AuthUser, CapacitorAuthStorage, type CapacitorAuthStorageOptions, type CompatSession, type CompatSessionUser, type EnrichedSession, type EnrichedUser, type IAuthApiService, type IAuthLogger, type IAuthStorage, JwtAuthService, type LoginCredentials, type LoginResponse, type NativeSocialSignInConfig, type NativeSocialSignInReason, type NativeSocialSignInResult, type PasskeyAuthenticationOptions, type RegisterData, type SendOtpResponse, type VerifyOtpFailure, type VerifyOtpResponse, type VerifyOtpSuccess, sessionStore, startNativeSocialSignIn };
package/dist/index.js CHANGED
@@ -1,6 +1,47 @@
1
1
  "use client";
2
2
  import { Preferences } from '@capacitor/preferences';
3
3
 
4
+ // src/client/native-social-signin.ts
5
+ async function startNativeSocialSignIn(config) {
6
+ let Capacitor;
7
+ try {
8
+ ({ Capacitor } = await import('@capacitor/core'));
9
+ } catch {
10
+ return { error: "not_native" };
11
+ }
12
+ if (!Capacitor.isNativePlatform()) {
13
+ return { error: "not_native" };
14
+ }
15
+ if (!Capacitor.isPluginAvailable("InAppAuth")) {
16
+ return { error: "plugin_unavailable" };
17
+ }
18
+ const base = config.baseURL.replace(/\/$/, "");
19
+ const startPath = config.startPath ?? "/api/mobile-social-start";
20
+ const startUrl = `${base}${startPath}?provider=${encodeURIComponent(
21
+ config.provider
22
+ )}`;
23
+ const pluginSpecifier = "@ccatto/capacitor-inapp-auth";
24
+ try {
25
+ const { InAppAuth } = await import(pluginSpecifier);
26
+ const { url } = await InAppAuth.start({
27
+ url: startUrl,
28
+ callbackScheme: config.callbackScheme
29
+ });
30
+ const params = new URLSearchParams(url.split("?")[1] ?? "");
31
+ const code = params.get("code");
32
+ if (params.get("error") || !code) {
33
+ return { error: "failed" };
34
+ }
35
+ return { code };
36
+ } catch (err) {
37
+ const message = err && typeof err === "object" && "code" in err ? String(err.code) : "";
38
+ if (message === "CANCELED") {
39
+ return { error: "canceled" };
40
+ }
41
+ return { error: "failed" };
42
+ }
43
+ }
44
+
4
45
  // src/client/session-store.ts
5
46
  var currentSession = null;
6
47
  var listeners = /* @__PURE__ */ new Set();
@@ -61,6 +102,27 @@ var _JwtAuthService = class _JwtAuthService {
61
102
  throw error;
62
103
  }
63
104
  }
105
+ /**
106
+ * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.
107
+ * Used by the native deep-link / in-app-browser social sign-in flow after
108
+ * {@link startNativeSocialSignIn} returns a `code`. Requires the app's
109
+ * `IAuthApiService` to implement `exchangeAuthCode`.
110
+ */
111
+ async loginWithExchangeCode(code) {
112
+ if (!this.api.exchangeAuthCode) {
113
+ throw new Error("Exchange-code auth not configured");
114
+ }
115
+ try {
116
+ const data = await this.api.exchangeAuthCode(code);
117
+ await this.storage.setAccessToken(data.accessToken);
118
+ await this.storage.setRefreshToken(data.refreshToken);
119
+ this.log.info("Auth code exchange successful", { userId: data.user.id });
120
+ return data;
121
+ } catch (error) {
122
+ this.log.error("Auth code exchange failed", { error });
123
+ throw error;
124
+ }
125
+ }
64
126
  /** Register new user */
65
127
  async register(data) {
66
128
  try {
@@ -316,6 +378,6 @@ var CapacitorAuthStorage = class {
316
378
  }
317
379
  };
318
380
 
319
- export { CapacitorAuthStorage, JwtAuthService, sessionStore };
381
+ export { CapacitorAuthStorage, JwtAuthService, sessionStore, startNativeSocialSignIn };
320
382
  //# sourceMappingURL=index.js.map
321
383
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/session-store.ts","../src/services/jwt-auth.service.ts","../src/storage/capacitor-auth-storage.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @ccatto/react-auth - Session Store\n *\n * Synchronous session state store for sharing auth session with Apollo Client.\n * Avoids network calls in Apollo's authLink.\n *\n * @example\n * // Write (from SessionSync component in React tree)\n * sessionStore.setSession(session);\n *\n * // Read (from Apollo authLink — synchronous, no network call)\n * const session = sessionStore.getSession();\n */\n\nimport type { CompatSession } from '../types/session';\n\nlet currentSession: CompatSession | null = null;\nconst listeners: Set<(session: CompatSession | null) => void> = new Set();\n\nexport const sessionStore = {\n /** Get the current session (synchronous, no network call) */\n getSession(): CompatSession | null {\n return currentSession ? Object.freeze({ ...currentSession }) : null;\n },\n\n /** Update the session (called by SessionSync component) */\n setSession(session: CompatSession | null): void {\n currentSession = session;\n const snapshot = [...listeners];\n snapshot.forEach((listener) => listener(session));\n },\n\n /** Subscribe to session changes */\n subscribe(listener: (session: CompatSession | null) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n /** Get the user ID from the current session (convenience method) */\n getUserId(): string | null {\n return currentSession?.user?.id ?? null;\n },\n};\n","/**\n * @ccatto/react-auth - JWT Auth Service\n *\n * Platform-agnostic JWT authentication service.\n * Handles token storage, login, register, refresh, and passkey auth.\n *\n * Uses IAuthStorage for token persistence and IAuthApiService for API calls.\n *\n * @example\n * ```typescript\n * import { JwtAuthService, CapacitorAuthStorage } from '@ccatto/react-auth';\n *\n * const storage = new CapacitorAuthStorage({ keyPrefix: 'myapp' });\n * const authService = new JwtAuthService(storage, myApiService, undefined, {\n * onSessionExpired: () => router.push('/login'),\n * });\n * await authService.login({ email, password });\n * ```\n */\nimport type { IAuthStorage } from '../storage/auth-storage.interface';\nimport type {\n AuthUser,\n IAuthApiService,\n IAuthLogger,\n LoginCredentials,\n LoginResponse,\n RegisterData,\n} from './auth-api.interface';\n\n// Re-export types for convenience\nexport type {\n AuthUser,\n LoginCredentials,\n RegisterData,\n LoginResponse,\n AuthTokens,\n} from './auth-api.interface';\n\nconst noopLogger: IAuthLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport interface JwtAuthServiceOptions {\n /** Called when session expires (refresh token fails). Use to redirect to login. */\n onSessionExpired?: () => void;\n}\n\nexport class JwtAuthService {\n private log: IAuthLogger;\n private options: JwtAuthServiceOptions;\n\n // Fix 7: Cache token expiry to avoid re-parsing JWT on every getAuthHeaders()\n private cachedTokenExp: { token: string; exp: number } | null = null;\n\n // Fix 5: Refresh deduplication + cooldown after failure\n private refreshPromise: Promise<string> | null = null;\n private lastRefreshFailure: number = 0;\n private static REFRESH_COOLDOWN_MS = 5000; // 5 second cooldown after failure\n\n constructor(\n private storage: IAuthStorage,\n private api: IAuthApiService,\n logger?: IAuthLogger,\n options?: JwtAuthServiceOptions,\n ) {\n this.log = logger || noopLogger;\n this.options = options || {};\n }\n\n /** Login with email and password */\n async login(credentials: LoginCredentials): Promise<LoginResponse> {\n try {\n const data = await this.api.login(credentials);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('User logged in successfully', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Login failed', { error });\n throw error;\n }\n }\n\n /** Register new user */\n async register(data: RegisterData): Promise<LoginResponse> {\n try {\n const result = await this.api.register(data);\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n this.log.info('User registered successfully', { userId: result.user.id });\n return result;\n } catch (error) {\n this.log.error('Registration failed', { error });\n throw error;\n }\n }\n\n /** Logout (clear tokens) */\n async logout(): Promise<void> {\n try {\n const refreshToken = await this.storage.getRefreshToken();\n if (refreshToken) {\n await this.api.logout(refreshToken).catch(() => {});\n }\n } finally {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.info('User logged out');\n }\n }\n\n /** Refresh access token */\n async refreshAccessToken(): Promise<string> {\n const refreshToken = await this.storage.getRefreshToken();\n if (!refreshToken) {\n throw new Error('No refresh token available');\n }\n\n try {\n const result = await this.api.refreshToken(refreshToken);\n await this.storage.setAccessToken(result.accessToken);\n return result.accessToken;\n } catch (error) {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.error('Token refresh failed', { error });\n this.options.onSessionExpired?.();\n throw new Error('Session expired');\n }\n }\n\n /** Get current access token */\n async getAccessToken(): Promise<string | null> {\n return this.storage.getAccessToken();\n }\n\n /** Check if a JWT token is expired or about to expire */\n private isTokenExpiredOrExpiring(\n token: string,\n bufferSeconds = 120,\n ): boolean {\n try {\n let exp: number;\n\n // Fix 7: Use cached expiry if token hasn't changed\n if (this.cachedTokenExp?.token === token) {\n exp = this.cachedTokenExp.exp;\n } else {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n exp = payload.exp;\n if (!exp) return true;\n this.cachedTokenExp = { token, exp };\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds >= exp - bufferSeconds;\n } catch {\n return true;\n }\n }\n\n /**\n * Get auth headers for API requests.\n * Proactively refreshes the access token if it's expired or about to expire.\n * Includes cooldown to prevent repeated refresh attempts after failure.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n let token = await this.getAccessToken();\n if (!token) return {};\n\n if (this.isTokenExpiredOrExpiring(token)) {\n // Fix 5: Skip refresh if we recently failed (cooldown)\n const timeSinceFailure = Date.now() - this.lastRefreshFailure;\n if (\n this.lastRefreshFailure > 0 &&\n timeSinceFailure < JwtAuthService.REFRESH_COOLDOWN_MS\n ) {\n return {};\n }\n\n try {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshAccessToken().finally(() => {\n this.refreshPromise = null;\n });\n }\n token = await this.refreshPromise;\n this.lastRefreshFailure = 0;\n } catch {\n this.lastRefreshFailure = Date.now();\n return {};\n }\n }\n\n return { Authorization: `Bearer ${token}` };\n }\n\n /** Check if user is authenticated */\n async isAuthenticated(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Check if tokens exist in storage */\n async hasTokens(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Decode JWT token (client-side only — for user info, NOT for security) */\n decodeToken(token: string): AuthUser | null {\n try {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n const payload = JSON.parse(jsonPayload);\n return {\n id: payload.sub,\n email: payload.email,\n name: payload.name || null,\n role: payload.role || 'user',\n playerID: payload.playerID,\n organizationId: payload.organizationId,\n };\n } catch {\n return null;\n }\n }\n\n /** Get current user from token (client-side decode) */\n async getCurrentUser(): Promise<AuthUser | null> {\n const token = await this.getAccessToken();\n if (!token) return null;\n return this.decodeToken(token);\n }\n\n /** Login with passkey (WebAuthn/FIDO2) */\n async loginWithPasskey(): Promise<LoginResponse> {\n if (\n !this.api.generatePasskeyAuthenticationOptions ||\n !this.api.verifyPasskeyAuthentication\n ) {\n throw new Error('Passkey authentication not configured');\n }\n\n const { startAuthentication, browserSupportsWebAuthn } =\n await import('@simplewebauthn/browser');\n\n if (!browserSupportsWebAuthn()) {\n throw new Error('WebAuthn is not supported on this device');\n }\n\n try {\n const { options, sessionId } =\n await this.api.generatePasskeyAuthenticationOptions();\n\n const authResponse = await startAuthentication({\n optionsJSON: JSON.parse(options),\n });\n\n const result = await this.api.verifyPasskeyAuthentication(\n sessionId,\n JSON.stringify(authResponse),\n );\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n this.log.info('Passkey auth successful', { userId: result.user.id });\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === 'NotAllowedError') {\n throw new Error('Passkey authentication was cancelled');\n }\n this.log.error('Passkey auth failed', { error });\n throw error;\n }\n }\n\n /** Send OTP to phone number for phone-based login */\n async sendPhoneOtp(\n phoneNumber: string,\n ): Promise<{ success: boolean; message: string; expiresIn: number }> {\n if (!this.api.sendPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.sendPhoneOtp(phoneNumber);\n if (result.success) {\n this.log.info('OTP sent', { phoneNumber: phoneNumber.slice(-4) });\n } else {\n this.log.warn('OTP send failed', {\n phoneNumber: phoneNumber.slice(-4),\n message: result.message,\n });\n }\n return result;\n } catch (error) {\n this.log.error('Failed to send OTP', { error });\n throw error;\n }\n }\n\n /** Verify OTP and login/register user */\n async verifyPhoneOtp(\n phoneNumber: string,\n code: string,\n ): Promise<LoginResponse & { isNewUser: boolean }> {\n if (!this.api.verifyPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.verifyPhoneOtp(phoneNumber, code);\n\n if (!result.success || !result.accessToken || !result.refreshToken) {\n throw new Error(result.message || 'Verification failed');\n }\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n const user = this.decodeToken(result.accessToken);\n if (!user) {\n throw new Error('Failed to decode user token');\n }\n\n this.log.info('Phone auth successful', {\n userId: user.id,\n isNewUser: result.isNewUser,\n });\n\n return {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n user,\n isNewUser: result.isNewUser ?? false,\n };\n } catch (error) {\n this.log.error('Phone OTP verification failed', { error });\n throw error;\n }\n }\n}\n","/**\n * @ccatto/react-auth - Capacitor Auth Storage\n *\n * Capacitor-based auth storage using @capacitor/preferences.\n * Works on BOTH web and mobile (no platform detection needed!).\n *\n * - Web: Uses localStorage\n * - iOS: Uses UserDefaults/Keychain\n * - Android: Uses SharedPreferences\n */\nimport { Preferences } from '@capacitor/preferences';\nimport type { IAuthStorage } from './auth-storage.interface';\n\nexport interface CapacitorAuthStorageOptions {\n /** Key prefix for stored tokens (default: 'catto_auth') */\n keyPrefix?: string;\n}\n\nexport class CapacitorAuthStorage implements IAuthStorage {\n private readonly ACCESS_TOKEN_KEY: string;\n private readonly REFRESH_TOKEN_KEY: string;\n\n constructor(options?: CapacitorAuthStorageOptions) {\n const prefix = options?.keyPrefix ?? 'catto_auth';\n this.ACCESS_TOKEN_KEY = `${prefix}_access_token`;\n this.REFRESH_TOKEN_KEY = `${prefix}_refresh_token`;\n }\n\n async setAccessToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.ACCESS_TOKEN_KEY,\n value: token,\n });\n }\n\n async getAccessToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.ACCESS_TOKEN_KEY });\n return value;\n }\n\n async setRefreshToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.REFRESH_TOKEN_KEY,\n value: token,\n });\n }\n\n async getRefreshToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.REFRESH_TOKEN_KEY });\n return value;\n }\n\n async clearTokens(): Promise<void> {\n await Promise.all([\n Preferences.remove({ key: this.ACCESS_TOKEN_KEY }),\n Preferences.remove({ key: this.REFRESH_TOKEN_KEY }),\n ]);\n }\n\n async hasTokens(): Promise<boolean> {\n const accessToken = await this.getAccessToken();\n return accessToken !== null;\n }\n}\n"]}
1
+ {"version":3,"sources":["../src/client/native-social-signin.ts","../src/client/session-store.ts","../src/services/jwt-auth.service.ts","../src/storage/capacitor-auth-storage.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @ccatto/react-auth - Native social sign-in handoff\n *\n * Drives the in-app-browser OAuth flow on native (Capacitor/iOS) and returns the\n * one-time handoff code. Pair the returned code with\n * `JwtAuthService.loginWithExchangeCode(code)` to mint app JWTs.\n *\n * Why this exists (see @ccatto/capacitor-inapp-auth): OAuth can't run in an\n * embedded webview, and SFSafariViewController can't return to the app via a\n * custom scheme. ASWebAuthenticationSession (this plugin) can. The server flow:\n *\n * start → `${baseURL}${startPath}?provider=…` (initiates OAuth server-side)\n * provider auth → web callback → 302 `${callbackScheme}://auth-callback?code=…`\n * ASWebAuthenticationSession captures the redirect → we parse `code` here.\n *\n * This module hardcodes nothing app-specific — `baseURL` and `callbackScheme`\n * come from config. `@capacitor/core` and `@ccatto/capacitor-inapp-auth` are\n * optional peer deps, imported dynamically so web-only consumers never load them.\n */\n\nexport interface NativeSocialSignInConfig {\n /** Provider key understood by the server route (e.g. 'google', 'apple', 'facebook'). */\n provider: string;\n /** Base URL of the web app that hosts the OAuth start route (no trailing slash needed). */\n baseURL: string;\n /** Custom URL scheme registered in Info.plist, WITHOUT '://' (e.g. 'myapp'). */\n callbackScheme: string;\n /** Server route that initiates OAuth in-session. Default: '/api/mobile-social-start'. */\n startPath?: string;\n}\n\nexport type NativeSocialSignInReason =\n /** Not running in a native Capacitor context — caller should use the web OAuth redirect. */\n | 'not_native'\n /** Native, but the InAppAuth plugin isn't installed (old build) — caller should fall back. */\n | 'plugin_unavailable'\n /** User dismissed the auth sheet. */\n | 'canceled'\n /** The provider/callback returned an error, or no code came back. */\n | 'failed';\n\nexport type NativeSocialSignInResult =\n | { code: string }\n | { code?: undefined; error: NativeSocialSignInReason };\n\n/**\n * Run the native in-app-browser OAuth handoff. Resolves to `{ code }` on success,\n * or `{ error }` describing why it didn't complete (so the caller can fall back\n * to the standard web OAuth redirect when appropriate).\n *\n * Never throws — all failure modes are returned as `{ error }`.\n *\n * @example\n * ```ts\n * const res = await startNativeSocialSignIn({\n * provider: 'google',\n * baseURL: process.env.NEXT_PUBLIC_BASE_URL!,\n * callbackScheme: 'myapp',\n * });\n * if ('code' in res && res.code) {\n * await authService.loginWithExchangeCode(res.code);\n * } else if (res.error === 'not_native' || res.error === 'plugin_unavailable') {\n * await signInSocialOnWeb(provider); // standard redirect\n * }\n * ```\n */\nexport async function startNativeSocialSignIn(\n config: NativeSocialSignInConfig,\n): Promise<NativeSocialSignInResult> {\n let Capacitor: typeof import('@capacitor/core').Capacitor;\n try {\n ({ Capacitor } = await import('@capacitor/core'));\n } catch {\n return { error: 'not_native' };\n }\n\n if (!Capacitor.isNativePlatform()) {\n return { error: 'not_native' };\n }\n if (!Capacitor.isPluginAvailable('InAppAuth')) {\n return { error: 'plugin_unavailable' };\n }\n\n const base = config.baseURL.replace(/\\/$/, '');\n const startPath = config.startPath ?? '/api/mobile-social-start';\n const startUrl = `${base}${startPath}?provider=${encodeURIComponent(\n config.provider,\n )}`;\n\n // Optional peer dep: import via a non-literal specifier with a local type so\n // react-auth's type build never hard-depends on the plugin's .d.ts (it may be\n // unbuilt in a fresh monorepo checkout, or absent entirely in a web-only\n // consumer that doesn't install it).\n type InAppAuthModule = {\n InAppAuth: {\n start(opts: {\n url: string;\n callbackScheme: string;\n }): Promise<{ url: string }>;\n };\n };\n const pluginSpecifier = '@ccatto/capacitor-inapp-auth';\n\n try {\n const { InAppAuth } = (await import(pluginSpecifier)) as InAppAuthModule;\n const { url } = await InAppAuth.start({\n url: startUrl,\n callbackScheme: config.callbackScheme,\n });\n\n const params = new URLSearchParams(url.split('?')[1] ?? '');\n const code = params.get('code');\n if (params.get('error') || !code) {\n return { error: 'failed' };\n }\n return { code };\n } catch (err) {\n // The plugin rejects with code \"CANCELED\" when the user dismisses the sheet.\n const message =\n err && typeof err === 'object' && 'code' in err\n ? String((err as { code?: unknown }).code)\n : '';\n if (message === 'CANCELED') {\n return { error: 'canceled' };\n }\n return { error: 'failed' };\n }\n}\n","/**\n * @ccatto/react-auth - Session Store\n *\n * Synchronous session state store for sharing auth session with Apollo Client.\n * Avoids network calls in Apollo's authLink.\n *\n * @example\n * // Write (from SessionSync component in React tree)\n * sessionStore.setSession(session);\n *\n * // Read (from Apollo authLink — synchronous, no network call)\n * const session = sessionStore.getSession();\n */\n\nimport type { CompatSession } from '../types/session';\n\nlet currentSession: CompatSession | null = null;\nconst listeners: Set<(session: CompatSession | null) => void> = new Set();\n\nexport const sessionStore = {\n /** Get the current session (synchronous, no network call) */\n getSession(): CompatSession | null {\n return currentSession ? Object.freeze({ ...currentSession }) : null;\n },\n\n /** Update the session (called by SessionSync component) */\n setSession(session: CompatSession | null): void {\n currentSession = session;\n const snapshot = [...listeners];\n snapshot.forEach((listener) => listener(session));\n },\n\n /** Subscribe to session changes */\n subscribe(listener: (session: CompatSession | null) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n /** Get the user ID from the current session (convenience method) */\n getUserId(): string | null {\n return currentSession?.user?.id ?? null;\n },\n};\n","/**\n * @ccatto/react-auth - JWT Auth Service\n *\n * Platform-agnostic JWT authentication service.\n * Handles token storage, login, register, refresh, and passkey auth.\n *\n * Uses IAuthStorage for token persistence and IAuthApiService for API calls.\n *\n * @example\n * ```typescript\n * import { JwtAuthService, CapacitorAuthStorage } from '@ccatto/react-auth';\n *\n * const storage = new CapacitorAuthStorage({ keyPrefix: 'myapp' });\n * const authService = new JwtAuthService(storage, myApiService, undefined, {\n * onSessionExpired: () => router.push('/login'),\n * });\n * await authService.login({ email, password });\n * ```\n */\nimport type { IAuthStorage } from '../storage/auth-storage.interface';\nimport type {\n AuthUser,\n IAuthApiService,\n IAuthLogger,\n LoginCredentials,\n LoginResponse,\n RegisterData,\n} from './auth-api.interface';\n\n// Re-export types for convenience\nexport type {\n AuthUser,\n LoginCredentials,\n RegisterData,\n LoginResponse,\n AuthTokens,\n} from './auth-api.interface';\n\nconst noopLogger: IAuthLogger = {\n info: () => {},\n warn: () => {},\n error: () => {},\n};\n\nexport interface JwtAuthServiceOptions {\n /** Called when session expires (refresh token fails). Use to redirect to login. */\n onSessionExpired?: () => void;\n}\n\nexport class JwtAuthService {\n private log: IAuthLogger;\n private options: JwtAuthServiceOptions;\n\n // Fix 7: Cache token expiry to avoid re-parsing JWT on every getAuthHeaders()\n private cachedTokenExp: { token: string; exp: number } | null = null;\n\n // Fix 5: Refresh deduplication + cooldown after failure\n private refreshPromise: Promise<string> | null = null;\n private lastRefreshFailure: number = 0;\n private static REFRESH_COOLDOWN_MS = 5000; // 5 second cooldown after failure\n\n constructor(\n private storage: IAuthStorage,\n private api: IAuthApiService,\n logger?: IAuthLogger,\n options?: JwtAuthServiceOptions,\n ) {\n this.log = logger || noopLogger;\n this.options = options || {};\n }\n\n /** Login with email and password */\n async login(credentials: LoginCredentials): Promise<LoginResponse> {\n try {\n const data = await this.api.login(credentials);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('User logged in successfully', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Login failed', { error });\n throw error;\n }\n }\n\n /**\n * Exchange a one-time mobile-OAuth handoff code for app JWTs and store them.\n * Used by the native deep-link / in-app-browser social sign-in flow after\n * {@link startNativeSocialSignIn} returns a `code`. Requires the app's\n * `IAuthApiService` to implement `exchangeAuthCode`.\n */\n async loginWithExchangeCode(code: string): Promise<LoginResponse> {\n if (!this.api.exchangeAuthCode) {\n throw new Error('Exchange-code auth not configured');\n }\n try {\n const data = await this.api.exchangeAuthCode(code);\n await this.storage.setAccessToken(data.accessToken);\n await this.storage.setRefreshToken(data.refreshToken);\n this.log.info('Auth code exchange successful', { userId: data.user.id });\n return data;\n } catch (error) {\n this.log.error('Auth code exchange failed', { error });\n throw error;\n }\n }\n\n /** Register new user */\n async register(data: RegisterData): Promise<LoginResponse> {\n try {\n const result = await this.api.register(data);\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n this.log.info('User registered successfully', { userId: result.user.id });\n return result;\n } catch (error) {\n this.log.error('Registration failed', { error });\n throw error;\n }\n }\n\n /** Logout (clear tokens) */\n async logout(): Promise<void> {\n try {\n const refreshToken = await this.storage.getRefreshToken();\n if (refreshToken) {\n await this.api.logout(refreshToken).catch(() => {});\n }\n } finally {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.info('User logged out');\n }\n }\n\n /** Refresh access token */\n async refreshAccessToken(): Promise<string> {\n const refreshToken = await this.storage.getRefreshToken();\n if (!refreshToken) {\n throw new Error('No refresh token available');\n }\n\n try {\n const result = await this.api.refreshToken(refreshToken);\n await this.storage.setAccessToken(result.accessToken);\n return result.accessToken;\n } catch (error) {\n await this.storage.clearTokens();\n this.cachedTokenExp = null;\n this.log.error('Token refresh failed', { error });\n this.options.onSessionExpired?.();\n throw new Error('Session expired');\n }\n }\n\n /** Get current access token */\n async getAccessToken(): Promise<string | null> {\n return this.storage.getAccessToken();\n }\n\n /** Check if a JWT token is expired or about to expire */\n private isTokenExpiredOrExpiring(\n token: string,\n bufferSeconds = 120,\n ): boolean {\n try {\n let exp: number;\n\n // Fix 7: Use cached expiry if token hasn't changed\n if (this.cachedTokenExp?.token === token) {\n exp = this.cachedTokenExp.exp;\n } else {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const payload = JSON.parse(atob(base64));\n exp = payload.exp;\n if (!exp) return true;\n this.cachedTokenExp = { token, exp };\n }\n\n const nowSeconds = Math.floor(Date.now() / 1000);\n return nowSeconds >= exp - bufferSeconds;\n } catch {\n return true;\n }\n }\n\n /**\n * Get auth headers for API requests.\n * Proactively refreshes the access token if it's expired or about to expire.\n * Includes cooldown to prevent repeated refresh attempts after failure.\n */\n async getAuthHeaders(): Promise<Record<string, string>> {\n let token = await this.getAccessToken();\n if (!token) return {};\n\n if (this.isTokenExpiredOrExpiring(token)) {\n // Fix 5: Skip refresh if we recently failed (cooldown)\n const timeSinceFailure = Date.now() - this.lastRefreshFailure;\n if (\n this.lastRefreshFailure > 0 &&\n timeSinceFailure < JwtAuthService.REFRESH_COOLDOWN_MS\n ) {\n return {};\n }\n\n try {\n if (!this.refreshPromise) {\n this.refreshPromise = this.refreshAccessToken().finally(() => {\n this.refreshPromise = null;\n });\n }\n token = await this.refreshPromise;\n this.lastRefreshFailure = 0;\n } catch {\n this.lastRefreshFailure = Date.now();\n return {};\n }\n }\n\n return { Authorization: `Bearer ${token}` };\n }\n\n /** Check if user is authenticated */\n async isAuthenticated(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Check if tokens exist in storage */\n async hasTokens(): Promise<boolean> {\n return this.storage.hasTokens();\n }\n\n /** Decode JWT token (client-side only — for user info, NOT for security) */\n decodeToken(token: string): AuthUser | null {\n try {\n const base64Url = token.split('.')[1];\n const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');\n const jsonPayload = decodeURIComponent(\n atob(base64)\n .split('')\n .map((c) => '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2))\n .join(''),\n );\n const payload = JSON.parse(jsonPayload);\n return {\n id: payload.sub,\n email: payload.email,\n name: payload.name || null,\n role: payload.role || 'user',\n playerID: payload.playerID,\n organizationId: payload.organizationId,\n };\n } catch {\n return null;\n }\n }\n\n /** Get current user from token (client-side decode) */\n async getCurrentUser(): Promise<AuthUser | null> {\n const token = await this.getAccessToken();\n if (!token) return null;\n return this.decodeToken(token);\n }\n\n /** Login with passkey (WebAuthn/FIDO2) */\n async loginWithPasskey(): Promise<LoginResponse> {\n if (\n !this.api.generatePasskeyAuthenticationOptions ||\n !this.api.verifyPasskeyAuthentication\n ) {\n throw new Error('Passkey authentication not configured');\n }\n\n const { startAuthentication, browserSupportsWebAuthn } =\n await import('@simplewebauthn/browser');\n\n if (!browserSupportsWebAuthn()) {\n throw new Error('WebAuthn is not supported on this device');\n }\n\n try {\n const { options, sessionId } =\n await this.api.generatePasskeyAuthenticationOptions();\n\n const authResponse = await startAuthentication({\n optionsJSON: JSON.parse(options),\n });\n\n const result = await this.api.verifyPasskeyAuthentication(\n sessionId,\n JSON.stringify(authResponse),\n );\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n this.log.info('Passkey auth successful', { userId: result.user.id });\n return result;\n } catch (error) {\n if (error instanceof Error && error.name === 'NotAllowedError') {\n throw new Error('Passkey authentication was cancelled');\n }\n this.log.error('Passkey auth failed', { error });\n throw error;\n }\n }\n\n /** Send OTP to phone number for phone-based login */\n async sendPhoneOtp(\n phoneNumber: string,\n ): Promise<{ success: boolean; message: string; expiresIn: number }> {\n if (!this.api.sendPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.sendPhoneOtp(phoneNumber);\n if (result.success) {\n this.log.info('OTP sent', { phoneNumber: phoneNumber.slice(-4) });\n } else {\n this.log.warn('OTP send failed', {\n phoneNumber: phoneNumber.slice(-4),\n message: result.message,\n });\n }\n return result;\n } catch (error) {\n this.log.error('Failed to send OTP', { error });\n throw error;\n }\n }\n\n /** Verify OTP and login/register user */\n async verifyPhoneOtp(\n phoneNumber: string,\n code: string,\n ): Promise<LoginResponse & { isNewUser: boolean }> {\n if (!this.api.verifyPhoneOtp) {\n throw new Error('Phone OTP not configured');\n }\n\n try {\n const result = await this.api.verifyPhoneOtp(phoneNumber, code);\n\n if (!result.success || !result.accessToken || !result.refreshToken) {\n throw new Error(result.message || 'Verification failed');\n }\n\n await this.storage.setAccessToken(result.accessToken);\n await this.storage.setRefreshToken(result.refreshToken);\n\n const user = this.decodeToken(result.accessToken);\n if (!user) {\n throw new Error('Failed to decode user token');\n }\n\n this.log.info('Phone auth successful', {\n userId: user.id,\n isNewUser: result.isNewUser,\n });\n\n return {\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n user,\n isNewUser: result.isNewUser ?? false,\n };\n } catch (error) {\n this.log.error('Phone OTP verification failed', { error });\n throw error;\n }\n }\n}\n","/**\n * @ccatto/react-auth - Capacitor Auth Storage\n *\n * Capacitor-based auth storage using @capacitor/preferences.\n * Works on BOTH web and mobile (no platform detection needed!).\n *\n * - Web: Uses localStorage\n * - iOS: Uses UserDefaults/Keychain\n * - Android: Uses SharedPreferences\n */\nimport { Preferences } from '@capacitor/preferences';\nimport type { IAuthStorage } from './auth-storage.interface';\n\nexport interface CapacitorAuthStorageOptions {\n /** Key prefix for stored tokens (default: 'catto_auth') */\n keyPrefix?: string;\n}\n\nexport class CapacitorAuthStorage implements IAuthStorage {\n private readonly ACCESS_TOKEN_KEY: string;\n private readonly REFRESH_TOKEN_KEY: string;\n\n constructor(options?: CapacitorAuthStorageOptions) {\n const prefix = options?.keyPrefix ?? 'catto_auth';\n this.ACCESS_TOKEN_KEY = `${prefix}_access_token`;\n this.REFRESH_TOKEN_KEY = `${prefix}_refresh_token`;\n }\n\n async setAccessToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.ACCESS_TOKEN_KEY,\n value: token,\n });\n }\n\n async getAccessToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.ACCESS_TOKEN_KEY });\n return value;\n }\n\n async setRefreshToken(token: string): Promise<void> {\n await Preferences.set({\n key: this.REFRESH_TOKEN_KEY,\n value: token,\n });\n }\n\n async getRefreshToken(): Promise<string | null> {\n const { value } = await Preferences.get({ key: this.REFRESH_TOKEN_KEY });\n return value;\n }\n\n async clearTokens(): Promise<void> {\n await Promise.all([\n Preferences.remove({ key: this.ACCESS_TOKEN_KEY }),\n Preferences.remove({ key: this.REFRESH_TOKEN_KEY }),\n ]);\n }\n\n async hasTokens(): Promise<boolean> {\n const accessToken = await this.getAccessToken();\n return accessToken !== null;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ccatto/react-auth",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "Catto Auth - React/Next.js authentication with Better Auth, JWT, and mobile support",
5
5
  "license": "MIT",
6
6
  "author": "Chris Catto",
@@ -50,6 +50,7 @@
50
50
  "typecheck": "tsc --noEmit"
51
51
  },
52
52
  "devDependencies": {
53
+ "@ccatto/capacitor-inapp-auth": "^1.0.0",
53
54
  "@types/react": "^19.0.0",
54
55
  "better-auth": "^1.2.8",
55
56
  "glob": "^13.0.0",
@@ -68,6 +69,7 @@
68
69
  "@apollo/client": ">=3.0.0",
69
70
  "@capacitor/core": ">=5.0.0",
70
71
  "@capacitor/preferences": ">=5.0.0",
72
+ "@ccatto/capacitor-inapp-auth": ">=1.0.0",
71
73
  "@simplewebauthn/browser": ">=13.0.0",
72
74
  "graphql": ">=16.0.0"
73
75
  },
@@ -75,6 +77,9 @@
75
77
  "@apollo/client": {
76
78
  "optional": true
77
79
  },
80
+ "@ccatto/capacitor-inapp-auth": {
81
+ "optional": true
82
+ },
78
83
  "@capacitor/core": {
79
84
  "optional": true
80
85
  },