@smarthivelabs-devs/auth-expo 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -16
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -38,9 +38,9 @@ import { SmartHiveAuthProvider, buildRedirectUri } from "@smarthivelabs-devs/aut
|
|
|
38
38
|
export default function Layout() {
|
|
39
39
|
return (
|
|
40
40
|
<SmartHiveAuthProvider
|
|
41
|
-
projectId={process.env.
|
|
42
|
-
publishableKey={process.env.
|
|
43
|
-
baseUrl={process.env.
|
|
41
|
+
projectId={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_PROJECT_ID!}
|
|
42
|
+
publishableKey={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_PUBLISHABLE_KEY!}
|
|
43
|
+
baseUrl={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_BASE_URL!}
|
|
44
44
|
redirectUri={buildRedirectUri("myapp")}
|
|
45
45
|
>
|
|
46
46
|
<Stack />
|
|
@@ -51,9 +51,9 @@ export default function Layout() {
|
|
|
51
51
|
|
|
52
52
|
```bash
|
|
53
53
|
# .env
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
EXPO_PUBLIC_SMARTHIVE_AUTH_PROJECT_ID=proj_abc123
|
|
55
|
+
EXPO_PUBLIC_SMARTHIVE_AUTH_PUBLISHABLE_KEY=pk_prod_abc123
|
|
56
|
+
EXPO_PUBLIC_SMARTHIVE_AUTH_BASE_URL=https://auth.myapp.com
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
---
|
|
@@ -402,9 +402,9 @@ import { SmartHiveAuthProvider, buildRedirectUri } from "@smarthivelabs-devs/aut
|
|
|
402
402
|
export default function Layout() {
|
|
403
403
|
return (
|
|
404
404
|
<SmartHiveAuthProvider
|
|
405
|
-
projectId={process.env.
|
|
406
|
-
publishableKey={process.env.
|
|
407
|
-
baseUrl={process.env.
|
|
405
|
+
projectId={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_PROJECT_ID!}
|
|
406
|
+
publishableKey={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_PUBLISHABLE_KEY!}
|
|
407
|
+
baseUrl={process.env.EXPO_PUBLIC_SMARTHIVE_AUTH_BASE_URL!}
|
|
408
408
|
redirectUri={buildRedirectUri("myapp")}
|
|
409
409
|
>
|
|
410
410
|
<Stack />
|
|
@@ -563,7 +563,7 @@ const router = express.Router();
|
|
|
563
563
|
|
|
564
564
|
const SMARTHIVE_BASE = process.env.SMARTHIVE_AUTH_BASE_URL!; // https://authcore.smarthivelabs.dev
|
|
565
565
|
const SMARTHIVE_ENV = process.env.SMARTHIVE_AUTH_ENVIRONMENT!; // prod
|
|
566
|
-
const SMARTHIVE_PID = process.env.
|
|
566
|
+
const SMARTHIVE_PID = process.env.SMARTHIVE_AUTH_PROJECT_ID!;
|
|
567
567
|
const APP_BASE_URL = process.env.APP_BASE_URL!; // https://api.yourapp.com
|
|
568
568
|
|
|
569
569
|
// Initiation — app calls this, proxy forwards to SmartHive
|
|
@@ -618,13 +618,139 @@ That's it. `signIn.social("google")` now opens `api.yourapp.com/api/auth/social/
|
|
|
618
618
|
|
|
619
619
|
---
|
|
620
620
|
|
|
621
|
-
##
|
|
621
|
+
## Backend Integration
|
|
622
622
|
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
623
|
+
Most apps need a backend that the Expo app calls for data. Here is the complete flow for how the Expo SDK, the developer's backend, and the SmartHive server SDK work together.
|
|
624
|
+
|
|
625
|
+
### Architecture overview
|
|
626
|
+
|
|
627
|
+
```
|
|
628
|
+
Expo App Developer Backend SmartHive Auth
|
|
629
|
+
──────────────────────────────────────────────────────────────────────────────
|
|
630
|
+
1. signIn.email / signIn.social ──── (direct to SmartHive) ────► headless endpoint
|
|
631
|
+
◄── access_token + refresh_token
|
|
632
|
+
SecureStore ◄── tokens saved
|
|
633
|
+
|
|
634
|
+
2. authFetch("https://api.myapp.com/me") ──► requireAuth() ──► JWKS endpoint
|
|
635
|
+
(adds Authorization: Bearer <token>) verifies JWT (once per key rotation)
|
|
636
|
+
◄── req.auth.userId
|
|
637
|
+
your API logic runs
|
|
638
|
+
◄── JSON response
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
**No tokens are sent to your backend** — only the short-lived access token (JWT) in the `Authorization` header. Your backend never sees or stores the refresh token.
|
|
642
|
+
|
|
643
|
+
### Step 1 — Expo app calls your backend
|
|
644
|
+
|
|
645
|
+
Every method from `useAuthFetch()` or `authFetch` automatically adds the `Authorization: Bearer <token>` header and transparently refreshes the access token when it is near expiry.
|
|
646
|
+
|
|
647
|
+
```tsx
|
|
648
|
+
// In your Expo screen
|
|
649
|
+
import { useAuthFetch } from "@smarthivelabs-devs/auth-expo";
|
|
650
|
+
|
|
651
|
+
const authFetch = useAuthFetch();
|
|
652
|
+
|
|
653
|
+
const res = await authFetch("https://api.myapp.com/profile");
|
|
654
|
+
const data = await res.json();
|
|
655
|
+
```
|
|
656
|
+
|
|
657
|
+
Or using `client.fetch` directly:
|
|
658
|
+
|
|
659
|
+
```tsx
|
|
660
|
+
const { client } = useAuth();
|
|
661
|
+
const res = await client.fetch("https://api.myapp.com/orders");
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
### Step 2 — Backend verifies the JWT
|
|
665
|
+
|
|
666
|
+
Install the server SDK on your backend:
|
|
667
|
+
|
|
668
|
+
```bash
|
|
669
|
+
npm install @smarthivelabs-devs/auth-server
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
// Express — src/middleware/auth.ts
|
|
674
|
+
import { requireAuth } from "@smarthivelabs-devs/auth-server";
|
|
675
|
+
|
|
676
|
+
export const protect = requireAuth({
|
|
677
|
+
issuer: process.env.SMARTHIVE_AUTH_ISSUER!, // e.g. https://authcore.smarthivelabs.dev
|
|
678
|
+
projectId: process.env.SMARTHIVE_AUTH_PROJECT_ID!, // optional — rejects tokens from other projects
|
|
679
|
+
});
|
|
680
|
+
```
|
|
681
|
+
|
|
682
|
+
```env
|
|
683
|
+
SMARTHIVE_AUTH_ISSUER=https://authcore.smarthivelabs.dev
|
|
684
|
+
SMARTHIVE_AUTH_PROJECT_ID=proj_abc123
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
```ts
|
|
688
|
+
// Express — src/routes/profile.ts
|
|
689
|
+
import { protect } from "../middleware/auth";
|
|
690
|
+
|
|
691
|
+
router.get("/profile", protect, (req, res) => {
|
|
692
|
+
// req.auth is typed as AuthContext
|
|
693
|
+
res.json({ userId: req.auth!.userId, email: req.auth!.email });
|
|
694
|
+
});
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Step 3 — Sign-up flow
|
|
698
|
+
|
|
699
|
+
Sign-up goes directly to SmartHive Auth — your backend does not need a sign-up endpoint.
|
|
700
|
+
|
|
701
|
+
```tsx
|
|
702
|
+
// Expo — sign-up screen
|
|
703
|
+
const { signUp } = useAuth();
|
|
704
|
+
|
|
705
|
+
const result = await signUp.email({
|
|
706
|
+
email: "user@example.com",
|
|
707
|
+
password: "secret123",
|
|
708
|
+
name: "Jane Doe",
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
if (result.requiresVerification) {
|
|
712
|
+
// Show "check your inbox" screen
|
|
713
|
+
// No session yet — user must verify email before signing in
|
|
714
|
+
} else {
|
|
715
|
+
// Session saved automatically — user is now signed in
|
|
716
|
+
// Next authFetch call to your backend will include a valid token
|
|
717
|
+
}
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
Once signed in, calls to `authFetch` on your backend will immediately work — no extra step needed.
|
|
721
|
+
|
|
722
|
+
### Step 4 — Admin operations (optional)
|
|
723
|
+
|
|
724
|
+
If your backend needs to look up user details, ban a user, or list sessions, use `createAdminClient` from the server SDK with your `shai_*` key from the dashboard.
|
|
725
|
+
|
|
726
|
+
```ts
|
|
727
|
+
import { requireAuth, createAdminClient } from "@smarthivelabs-devs/auth-server";
|
|
728
|
+
|
|
729
|
+
const smarthive = createAdminClient({
|
|
730
|
+
secretKey: process.env.SMARTHIVE_AUTH_ADMIN_KEY!, // shai_*
|
|
731
|
+
baseUrl: process.env.SMARTHIVE_AUTH_ISSUER!,
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
// After verifying the JWT, look up the full user record
|
|
735
|
+
router.get("/profile", protect, async (req, res) => {
|
|
736
|
+
const { user } = await smarthive.users.get(req.auth!.userId);
|
|
737
|
+
res.json(user);
|
|
738
|
+
});
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
### Full sign-in flow reference
|
|
742
|
+
|
|
743
|
+
| Step | Who | What happens |
|
|
744
|
+
|---|---|---|
|
|
745
|
+
| 1 | Expo app | `signIn.email()` or `signIn.social()` — tokens returned from SmartHive |
|
|
746
|
+
| 2 | Expo app | Tokens stored in SecureStore (never leaves the device in plaintext) |
|
|
747
|
+
| 3 | Expo app | `authFetch(backendUrl)` — sends `Authorization: Bearer <access_token>` |
|
|
748
|
+
| 4 | Backend | `requireAuth()` fetches SmartHive JWKS once, verifies JWT signature + expiry |
|
|
749
|
+
| 5 | Backend | `req.auth.userId` is available — your business logic runs |
|
|
750
|
+
| 6 | Backend | (optional) `smarthive.users.get(userId)` for full user profile |
|
|
751
|
+
| Token refresh | Expo SDK | When access token is ≤ 30 s from expiry, automatically refreshed before the next `authFetch` — your backend never sees an expired token |
|
|
752
|
+
|
|
753
|
+
---
|
|
628
754
|
|
|
629
755
|
---
|
|
630
756
|
|
package/dist/index.js
CHANGED
|
@@ -80,6 +80,9 @@ function initExpoAuth(config) {
|
|
|
80
80
|
if (result.type === "success") {
|
|
81
81
|
await base.handleSocialCallback({ url: result.url });
|
|
82
82
|
}
|
|
83
|
+
},
|
|
84
|
+
async logout() {
|
|
85
|
+
await base.headless.signOut();
|
|
83
86
|
}
|
|
84
87
|
};
|
|
85
88
|
}
|
|
@@ -108,7 +111,7 @@ function SmartHiveAuthProvider({ children, ...config }) {
|
|
|
108
111
|
if (parsed.searchParams.has("access_token") || parsed.searchParams.has("error")) {
|
|
109
112
|
client.handleSocialCallback({ url }).then((s) => setSession(s)).catch(() => {
|
|
110
113
|
});
|
|
111
|
-
} else {
|
|
114
|
+
} else if (parsed.searchParams.has("code")) {
|
|
112
115
|
client.handleCallback({ url }).then((s) => setSession(s)).catch(() => {
|
|
113
116
|
});
|
|
114
117
|
}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Linking } from \"react-native\";\nimport { AuthRequest } from \"expo-auth-session\";\nimport * as WebBrowser from \"expo-web-browser\";\nimport * as SecureStore from \"expo-secure-store\";\nimport {\n initAuth,\n envFromPublishableKey,\n type AuthSession,\n type AuthStorage,\n type HeadlessClient,\n type HeadlessSignInResult,\n type HeadlessSignUpResult,\n type SmartHiveAuthClient,\n type SmartHiveAuthConfig,\n type SocialProvider,\n} from \"@smarthivelabs-devs/auth-sdk\";\n\n// ── Secure storage adapter ────────────────────────────────────────────────────\n\nconst secureStorage: AuthStorage = {\n getItem: (key) => SecureStore.getItemAsync(key),\n setItem: (key, value) => SecureStore.setItemAsync(key, value),\n removeItem: (key) => SecureStore.deleteItemAsync(key),\n};\n\nconst storageKeys = {\n pkceVerifier: \"smarthive.auth.pkce_verifier\",\n pkceState: \"smarthive.auth.pkce_state\"\n} as const;\n\n// ── Config ────────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveExpoConfig extends Omit<SmartHiveAuthConfig, \"storage\"> {\n /** Deep link redirect URI, e.g. \"myapp://auth/callback\" */\n redirectUri: string;\n /**\n * URL of your own backend's social auth proxy (e.g. \"https://api.myapp.com\").\n * When set, social OAuth routes through your domain — Google/Apple consent screens\n * show your domain instead of SmartHive's. Your backend must implement\n * GET /api/auth/social/:provider and GET /api/auth/social/:provider/callback.\n */\n socialProxyUrl?: string;\n}\n\n/**\n * Build a deep link redirect URI from your Expo app scheme.\n * @example buildRedirectUri(\"myapp\") → \"myapp://auth/callback\"\n */\nexport function buildRedirectUri(scheme: string, path = \"auth/callback\"): string {\n return `${scheme}://${path}`;\n}\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\");\n}\n\n// ── Auth client (Expo flavour) ─────────────────────────────────────────────────\n\n/**\n * Creates an auth client that uses SecureStore for token storage and\n * Linking.openURL for the OAuth redirect (instead of location.assign).\n */\nexport function initExpoAuth(config: SmartHiveExpoConfig): SmartHiveAuthClient {\n const base = initAuth({\n ...config,\n storage: secureStorage,\n temporaryStorage: secureStorage\n } as SmartHiveAuthConfig);\n\n return {\n ...base,\n async login(options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri;\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const request = new AuthRequest({\n clientId: config.publishableKey,\n redirectUri,\n responseType: \"code\",\n usePKCE: true,\n state: options?.state,\n extraParams: {\n project_id: config.projectId,\n publishable_key: config.publishableKey\n }\n });\n const url = await request.makeAuthUrlAsync({\n authorizationEndpoint: `${authBase}/api/auth/oauth2/authorize`\n });\n\n if (request.codeVerifier) {\n await secureStorage.setItem(storageKeys.pkceVerifier, request.codeVerifier);\n }\n await secureStorage.setItem(storageKeys.pkceState, request.state);\n await Linking.openURL(url);\n },\n\n async loginSocial(provider, options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri;\n let socialUrl: URL;\n\n if (config.socialProxyUrl) {\n // Route through the app's own backend — Google/Apple show the app's domain\n socialUrl = new URL(`${normalizeBaseUrl(config.socialProxyUrl)}/api/auth/social/${provider}`);\n socialUrl.searchParams.set(\"redirect_uri\", redirectUri);\n // project_id is injected server-side by the backend proxy\n } else {\n // Direct SmartHive route\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const env = envFromPublishableKey(config.publishableKey);\n socialUrl = new URL(`${authBase}/${env}/api/auth/social/${provider}`);\n socialUrl.searchParams.set(\"project_id\", config.projectId);\n socialUrl.searchParams.set(\"redirect_uri\", redirectUri);\n }\n\n // In-app browser sheet — auto-closes when redirectUri fires\n const result = await WebBrowser.openAuthSessionAsync(socialUrl.toString(), redirectUri);\n if (result.type === \"success\") {\n await base.handleSocialCallback({ url: result.url });\n }\n },\n };\n}\n\n// ── Context ────────────────────────────────────────────────────────────────────\n\ninterface AuthContextValue {\n client: SmartHiveAuthClient;\n session: AuthSession | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n /** OAuth 2.0 + PKCE browser redirect sign-in (unchanged). */\n login(options?: { redirectUri?: string }): Promise<void>;\n logout(): Promise<void>;\n refreshSession(): Promise<void>;\n getAuthorizationHeader(): Promise<Record<string, string>>;\n authFetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n /**\n * Headless (no browser) sign-in methods for custom login screens.\n * Tokens are stored in SecureStore automatically on success.\n */\n signIn: {\n email(params: { email: string; password: string }): Promise<HeadlessSignInResult>;\n phone: {\n sendOtp(params: { phoneNumber: string }): Promise<void>;\n verify(params: { phoneNumber: string; code: string }): Promise<HeadlessSignInResult>;\n };\n emailOtp: {\n send(params: { email: string }): Promise<void>;\n verify(params: { email: string; code: string }): Promise<HeadlessSignInResult>;\n };\n magicLink: {\n send(params: { email: string; callbackURL?: string }): Promise<void>;\n };\n /** Initiate a social OAuth flow — opens an in-app browser sheet (expo-web-browser). Auto-closes on redirect and updates session state. */\n social(provider: SocialProvider, options?: { redirectUri?: string }): Promise<void>;\n };\n signUp: {\n email(params: { email: string; password: string; name?: string }): Promise<HeadlessSignUpResult>;\n resendVerificationEmail(params: { email: string }): Promise<void>;\n };\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ── Provider ───────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveAuthProviderProps extends SmartHiveExpoConfig {\n children: React.ReactNode;\n}\n\n/** @deprecated Use SmartHiveAuthProviderProps */\nexport type SmartHiveProviderProps = SmartHiveAuthProviderProps;\n\n/**\n * Wraps your Expo app and provides auth state to all child components.\n * Tokens are stored in SecureStore. Supports both:\n * - OAuth 2.0 + PKCE browser redirect via `login()`\n * - Headless direct sign-in via `signIn.*` (no browser, custom login screens)\n */\nexport function SmartHiveAuthProvider({ children, ...config }: SmartHiveAuthProviderProps) {\n const configRef = useRef(config);\n const client = useMemo(() => initExpoAuth(configRef.current), []);\n\n const [session, setSession] = useState<AuthSession | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n // Initial session load from SecureStore\n useEffect(() => {\n let cancelled = false;\n client\n .initialize()\n .then(() => client.getSession())\n .then((s) => { if (!cancelled) setSession(s); })\n .catch(() => {})\n .finally(() => { if (!cancelled) setIsLoaded(true); });\n return () => { cancelled = true; };\n }, [client]);\n\n // Deep link listener — catches both PKCE and social OAuth callbacks\n useEffect(() => {\n function handleUrl({ url }: { url: string }) {\n try {\n const parsed = new URL(url);\n if (parsed.searchParams.has(\"access_token\") || parsed.searchParams.has(\"error\")) {\n // Social auth callback — tokens already in URL params\n client\n .handleSocialCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\n } else {\n // PKCE OAuth callback — exchange code for tokens\n client\n .handleCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\n }\n } catch {\n // Unparseable URL — ignore\n }\n }\n\n const sub = Linking.addEventListener(\"url\", handleUrl);\n\n // Handle cold-start: app opened directly from the OAuth redirect URL\n Linking.getInitialURL().then((url) => {\n if (url) handleUrl({ url });\n });\n\n return () => sub.remove();\n }, [client]);\n\n const login = useCallback(\n (options?: { redirectUri?: string }) => client.login(options),\n [client]\n );\n\n const logout = useCallback(async () => {\n await client.logout();\n setSession(null);\n }, [client]);\n\n const refreshSession = useCallback(async () => {\n setSession(await client.refreshSession());\n }, [client]);\n\n const getAuthorizationHeader = useCallback(\n () => client.getAuthorizationHeader(),\n [client]\n );\n\n const authFetch = useCallback(\n (input: string | URL | Request, init?: RequestInit) => client.fetch(input, init),\n [client]\n );\n\n // ── Headless sign-in wrappers — save session + update state ──────────────────\n\n function wrapHeadlessSignIn(\n fn: (p: never) => Promise<HeadlessSignInResult>\n ) {\n return async (params: never) => {\n const result = await fn(params);\n setSession({\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: result.expiresAt,\n user: result.user,\n });\n return result;\n };\n }\n\n const signIn = useMemo<AuthContextValue[\"signIn\"]>(() => {\n const h: HeadlessClient = client.headless;\n return {\n email: wrapHeadlessSignIn(h.signIn.email.bind(h.signIn) as never),\n phone: {\n sendOtp: (p) => h.signIn.phone.sendOtp(p),\n verify: wrapHeadlessSignIn(h.signIn.phone.verify.bind(h.signIn.phone) as never),\n },\n emailOtp: {\n send: (p) => h.signIn.emailOtp.send(p),\n verify: wrapHeadlessSignIn(h.signIn.emailOtp.verify.bind(h.signIn.emailOtp) as never),\n },\n magicLink: {\n send: (p) => h.signIn.magicLink.send(p),\n },\n social: async (provider, options) => {\n await client.loginSocial(provider, options);\n // loginSocial used openAuthSessionAsync + handleSocialCallback — session is now in SecureStore\n const s = await client.getSession();\n if (s) setSession(s);\n },\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client]);\n\n const signUp = useMemo<AuthContextValue[\"signUp\"]>(() => {\n const h: HeadlessClient = client.headless;\n return {\n email: async (params) => {\n const result = await h.signUp.email(params);\n if (!result.requiresVerification) {\n setSession({\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: result.expiresAt,\n user: result.user,\n });\n }\n return result;\n },\n resendVerificationEmail: (params) => h.signUp.resendVerificationEmail(params),\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client]);\n\n const value = useMemo<AuthContextValue>(\n () => ({\n client,\n session,\n isLoaded,\n isSignedIn: !!session,\n login,\n logout,\n refreshSession,\n getAuthorizationHeader,\n authFetch,\n signIn,\n signUp,\n }),\n [client, session, isLoaded, login, logout, refreshSession, getAuthorizationHeader, authFetch, signIn, signUp]\n );\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n// ── Hooks ──────────────────────────────────────────────────────────────────────\n\nfunction useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error(\"useAuth must be used inside <SmartHiveAuthProvider>.\");\n return ctx;\n}\n\nexport function useAuth() { return useAuthContext(); }\nexport function useSession() { return useAuthContext().session; }\nexport function useUser() { return useAuthContext().session?.user ?? null; }\nexport function useIsLoaded() { return useAuthContext().isLoaded; }\n\nexport function useIsSignedIn() {\n const { isLoaded, isSignedIn } = useAuthContext();\n return isLoaded ? isSignedIn : null;\n}\n\nexport function useAuthFetch() {\n return useAuthContext().authFetch;\n}\n\nexport function useAuthorizationHeader() {\n return useAuthContext().getAuthorizationHeader;\n}\n\n/** @deprecated Use SmartHiveAuthProvider */\nexport const SmartHiveProvider = SmartHiveAuthProvider;\n\n// ── Render helpers ─────────────────────────────────────────────────────────────\n\nexport function SignedIn({ children }: { children: React.ReactNode }) {\n const { isLoaded, isSignedIn } = useAuthContext();\n if (!isLoaded || !isSignedIn) return null;\n return <>{children}</>;\n}\n\nexport function SignedOut({ children }: { children: React.ReactNode }) {\n const { isLoaded, isSignedIn } = useAuthContext();\n if (!isLoaded || isSignedIn) return null;\n return <>{children}</>;\n}\n\nexport function AuthLoading({ children }: { children: React.ReactNode }) {\n const { isLoaded } = useAuthContext();\n return isLoaded ? null : <>{children}</>;\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,eAAe;AACxB,SAAS,mBAAmB;AAC5B,YAAY,gBAAgB;AAC5B,YAAY,iBAAiB;AAC7B;AAAA,EACE;AAAA,EACA;AAAA,OASK;AA+TE,SAqCA,UArCA;AA3TT,IAAM,gBAA6B;AAAA,EACjC,SAAS,CAAC,QAAoB,yBAAa,GAAG;AAAA,EAC9C,SAAS,CAAC,KAAK,UAAsB,yBAAa,KAAK,KAAK;AAAA,EAC5D,YAAY,CAAC,QAAoB,4BAAgB,GAAG;AACtD;AAEA,IAAM,cAAc;AAAA,EAClB,cAAc;AAAA,EACd,WAAW;AACb;AAoBO,SAAS,iBAAiB,QAAgB,OAAO,iBAAyB;AAC/E,SAAO,GAAG,MAAM,MAAM,IAAI;AAC5B;AAEA,SAAS,iBAAiB,SAAiB;AACzC,SAAO,QAAQ,QAAQ,OAAO,EAAE;AAClC;AAQO,SAAS,aAAa,QAAkD;AAC7E,QAAM,OAAO,SAAS;AAAA,IACpB,GAAG;AAAA,IACH,SAAS;AAAA,IACT,kBAAkB;AAAA,EACpB,CAAwB;AAExB,SAAO;AAAA,IACL,GAAG;AAAA,IACH,MAAM,MAAM,SAAS;AACnB,YAAM,cAAc,SAAS,eAAe,OAAO;AACnD,YAAM,WAAW,iBAAiB,OAAO,cAAc,OAAO,OAAO;AACrE,YAAM,UAAU,IAAI,YAAY;AAAA,QAC9B,UAAU,OAAO;AAAA,QACjB;AAAA,QACA,cAAc;AAAA,QACd,SAAS;AAAA,QACT,OAAO,SAAS;AAAA,QAChB,aAAa;AAAA,UACX,YAAY,OAAO;AAAA,UACnB,iBAAiB,OAAO;AAAA,QAC1B;AAAA,MACF,CAAC;AACD,YAAM,MAAM,MAAM,QAAQ,iBAAiB;AAAA,QACzC,uBAAuB,GAAG,QAAQ;AAAA,MACpC,CAAC;AAED,UAAI,QAAQ,cAAc;AACxB,cAAM,cAAc,QAAQ,YAAY,cAAc,QAAQ,YAAY;AAAA,MAC5E;AACA,YAAM,cAAc,QAAQ,YAAY,WAAW,QAAQ,KAAK;AAChE,YAAM,QAAQ,QAAQ,GAAG;AAAA,IAC3B;AAAA,IAEA,MAAM,YAAY,UAAU,SAAS;AACnC,YAAM,cAAc,SAAS,eAAe,OAAO;AACnD,UAAI;AAEJ,UAAI,OAAO,gBAAgB;AAEzB,oBAAY,IAAI,IAAI,GAAG,iBAAiB,OAAO,cAAc,CAAC,oBAAoB,QAAQ,EAAE;AAC5F,kBAAU,aAAa,IAAI,gBAAgB,WAAW;AAAA,MAExD,OAAO;AAEL,cAAM,WAAW,iBAAiB,OAAO,cAAc,OAAO,OAAO;AACrE,cAAM,MAAM,sBAAsB,OAAO,cAAc;AACvD,oBAAY,IAAI,IAAI,GAAG,QAAQ,IAAI,GAAG,oBAAoB,QAAQ,EAAE;AACpE,kBAAU,aAAa,IAAI,cAAc,OAAO,SAAS;AACzD,kBAAU,aAAa,IAAI,gBAAgB,WAAW;AAAA,MACxD;AAGA,YAAM,SAAS,MAAiB,gCAAqB,UAAU,SAAS,GAAG,WAAW;AACtF,UAAI,OAAO,SAAS,WAAW;AAC7B,cAAM,KAAK,qBAAqB,EAAE,KAAK,OAAO,IAAI,CAAC;AAAA,MACrD;AAAA,IACF;AAAA,EACF;AACF;AAyCA,IAAM,cAAc,cAAuC,IAAI;AAiBxD,SAAS,sBAAsB,EAAE,UAAU,GAAG,OAAO,GAA+B;AACzF,QAAM,YAAY,OAAO,MAAM;AAC/B,QAAM,SAAS,QAAQ,MAAM,aAAa,UAAU,OAAO,GAAG,CAAC,CAAC;AAEhE,QAAM,CAAC,SAAS,UAAU,IAAI,SAA6B,IAAI;AAC/D,QAAM,CAAC,UAAU,WAAW,IAAI,SAAS,KAAK;AAG9C,YAAU,MAAM;AACd,QAAI,YAAY;AAChB,WACG,WAAW,EACX,KAAK,MAAM,OAAO,WAAW,CAAC,EAC9B,KAAK,CAAC,MAAM;AAAE,UAAI,CAAC,UAAW,YAAW,CAAC;AAAA,IAAG,CAAC,EAC9C,MAAM,MAAM;AAAA,IAAC,CAAC,EACd,QAAQ,MAAM;AAAE,UAAI,CAAC,UAAW,aAAY,IAAI;AAAA,IAAG,CAAC;AACvD,WAAO,MAAM;AAAE,kBAAY;AAAA,IAAM;AAAA,EACnC,GAAG,CAAC,MAAM,CAAC;AAGX,YAAU,MAAM;AACd,aAAS,UAAU,EAAE,IAAI,GAAoB;AAC3C,UAAI;AACF,cAAM,SAAS,IAAI,IAAI,GAAG;AAC1B,YAAI,OAAO,aAAa,IAAI,cAAc,KAAK,OAAO,aAAa,IAAI,OAAO,GAAG;AAE/E,iBACG,qBAAqB,EAAE,IAAI,CAAC,EAC5B,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC,EACzB,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACnB,OAAO;AAEL,iBACG,eAAe,EAAE,IAAI,CAAC,EACtB,KAAK,CAAC,MAAM,WAAW,CAAC,CAAC,EACzB,MAAM,MAAM;AAAA,UAAC,CAAC;AAAA,QACnB;AAAA,MACF,QAAQ;AAAA,MAER;AAAA,IACF;AAEA,UAAM,MAAM,QAAQ,iBAAiB,OAAO,SAAS;AAGrD,YAAQ,cAAc,EAAE,KAAK,CAAC,QAAQ;AACpC,UAAI,IAAK,WAAU,EAAE,IAAI,CAAC;AAAA,IAC5B,CAAC;AAED,WAAO,MAAM,IAAI,OAAO;AAAA,EAC1B,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ;AAAA,IACZ,CAAC,YAAuC,OAAO,MAAM,OAAO;AAAA,IAC5D,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,SAAS,YAAY,YAAY;AACrC,UAAM,OAAO,OAAO;AACpB,eAAW,IAAI;AAAA,EACjB,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,iBAAiB,YAAY,YAAY;AAC7C,eAAW,MAAM,OAAO,eAAe,CAAC;AAAA,EAC1C,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,yBAAyB;AAAA,IAC7B,MAAM,OAAO,uBAAuB;AAAA,IACpC,CAAC,MAAM;AAAA,EACT;AAEA,QAAM,YAAY;AAAA,IAChB,CAAC,OAA+B,SAAuB,OAAO,MAAM,OAAO,IAAI;AAAA,IAC/E,CAAC,MAAM;AAAA,EACT;AAIA,WAAS,mBACP,IACA;AACA,WAAO,OAAO,WAAkB;AAC9B,YAAM,SAAS,MAAM,GAAG,MAAM;AAC9B,iBAAW;AAAA,QACT,aAAa,OAAO;AAAA,QACpB,cAAc,OAAO;AAAA,QACrB,WAAW,OAAO;AAAA,QAClB,MAAM,OAAO;AAAA,MACf,CAAC;AACD,aAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,SAAS,QAAoC,MAAM;AACvD,UAAM,IAAoB,OAAO;AACjC,WAAO;AAAA,MACL,OAAO,mBAAmB,EAAE,OAAO,MAAM,KAAK,EAAE,MAAM,CAAU;AAAA,MAChE,OAAO;AAAA,QACL,SAAS,CAAC,MAAM,EAAE,OAAO,MAAM,QAAQ,CAAC;AAAA,QACxC,QAAQ,mBAAmB,EAAE,OAAO,MAAM,OAAO,KAAK,EAAE,OAAO,KAAK,CAAU;AAAA,MAChF;AAAA,MACA,UAAU;AAAA,QACR,MAAM,CAAC,MAAM,EAAE,OAAO,SAAS,KAAK,CAAC;AAAA,QACrC,QAAQ,mBAAmB,EAAE,OAAO,SAAS,OAAO,KAAK,EAAE,OAAO,QAAQ,CAAU;AAAA,MACtF;AAAA,MACA,WAAW;AAAA,QACT,MAAM,CAAC,MAAM,EAAE,OAAO,UAAU,KAAK,CAAC;AAAA,MACxC;AAAA,MACA,QAAQ,OAAO,UAAU,YAAY;AACnC,cAAM,OAAO,YAAY,UAAU,OAAO;AAE1C,cAAM,IAAI,MAAM,OAAO,WAAW;AAClC,YAAI,EAAG,YAAW,CAAC;AAAA,MACrB;AAAA,IACF;AAAA,EAEF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,SAAS,QAAoC,MAAM;AACvD,UAAM,IAAoB,OAAO;AACjC,WAAO;AAAA,MACL,OAAO,OAAO,WAAW;AACvB,cAAM,SAAS,MAAM,EAAE,OAAO,MAAM,MAAM;AAC1C,YAAI,CAAC,OAAO,sBAAsB;AAChC,qBAAW;AAAA,YACT,aAAa,OAAO;AAAA,YACpB,cAAc,OAAO;AAAA,YACrB,WAAW,OAAO;AAAA,YAClB,MAAM,OAAO;AAAA,UACf,CAAC;AAAA,QACH;AACA,eAAO;AAAA,MACT;AAAA,MACA,yBAAyB,CAAC,WAAW,EAAE,OAAO,wBAAwB,MAAM;AAAA,IAC9E;AAAA,EAEF,GAAG,CAAC,MAAM,CAAC;AAEX,QAAM,QAAQ;AAAA,IACZ,OAAO;AAAA,MACL;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAY,CAAC,CAAC;AAAA,MACd;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,IACA,CAAC,QAAQ,SAAS,UAAU,OAAO,QAAQ,gBAAgB,wBAAwB,WAAW,QAAQ,MAAM;AAAA,EAC9G;AAEA,SAAO,oBAAC,YAAY,UAAZ,EAAqB,OAAe,UAAS;AACvD;AAIA,SAAS,iBAAmC;AAC1C,QAAM,MAAM,WAAW,WAAW;AAClC,MAAI,CAAC,IAAK,OAAM,IAAI,MAAM,sDAAsD;AAChF,SAAO;AACT;AAEO,SAAS,UAAU;AAAE,SAAO,eAAe;AAAG;AAC9C,SAAS,aAAa;AAAE,SAAO,eAAe,EAAE;AAAS;AACzD,SAAS,UAAU;AAAE,SAAO,eAAe,EAAE,SAAS,QAAQ;AAAM;AACpE,SAAS,cAAc;AAAE,SAAO,eAAe,EAAE;AAAU;AAE3D,SAAS,gBAAgB;AAC9B,QAAM,EAAE,UAAU,WAAW,IAAI,eAAe;AAChD,SAAO,WAAW,aAAa;AACjC;AAEO,SAAS,eAAe;AAC7B,SAAO,eAAe,EAAE;AAC1B;AAEO,SAAS,yBAAyB;AACvC,SAAO,eAAe,EAAE;AAC1B;AAGO,IAAM,oBAAoB;AAI1B,SAAS,SAAS,EAAE,SAAS,GAAkC;AACpE,QAAM,EAAE,UAAU,WAAW,IAAI,eAAe;AAChD,MAAI,CAAC,YAAY,CAAC,WAAY,QAAO;AACrC,SAAO,gCAAG,UAAS;AACrB;AAEO,SAAS,UAAU,EAAE,SAAS,GAAkC;AACrE,QAAM,EAAE,UAAU,WAAW,IAAI,eAAe;AAChD,MAAI,CAAC,YAAY,WAAY,QAAO;AACpC,SAAO,gCAAG,UAAS;AACrB;AAEO,SAAS,YAAY,EAAE,SAAS,GAAkC;AACvE,QAAM,EAAE,SAAS,IAAI,eAAe;AACpC,SAAO,WAAW,OAAO,gCAAG,UAAS;AACvC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useRef,\n useState,\n} from \"react\";\nimport { Linking } from \"react-native\";\nimport { AuthRequest } from \"expo-auth-session\";\nimport * as WebBrowser from \"expo-web-browser\";\nimport * as SecureStore from \"expo-secure-store\";\nimport {\n initAuth,\n envFromPublishableKey,\n type AuthSession,\n type AuthStorage,\n type HeadlessClient,\n type HeadlessSignInResult,\n type HeadlessSignUpResult,\n type SmartHiveAuthClient,\n type SmartHiveAuthConfig,\n type SocialProvider,\n} from \"@smarthivelabs-devs/auth-sdk\";\n\n// ── Secure storage adapter ────────────────────────────────────────────────────\n\nconst secureStorage: AuthStorage = {\n getItem: (key) => SecureStore.getItemAsync(key),\n setItem: (key, value) => SecureStore.setItemAsync(key, value),\n removeItem: (key) => SecureStore.deleteItemAsync(key),\n};\n\nconst storageKeys = {\n pkceVerifier: \"smarthive.auth.pkce_verifier\",\n pkceState: \"smarthive.auth.pkce_state\"\n} as const;\n\n// ── Config ────────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveExpoConfig extends Omit<SmartHiveAuthConfig, \"storage\"> {\n /** Deep link redirect URI, e.g. \"myapp://auth/callback\" */\n redirectUri: string;\n /**\n * URL of your own backend's social auth proxy (e.g. \"https://api.myapp.com\").\n * When set, social OAuth routes through your domain — Google/Apple consent screens\n * show your domain instead of SmartHive's. Your backend must implement\n * GET /api/auth/social/:provider and GET /api/auth/social/:provider/callback.\n */\n socialProxyUrl?: string;\n}\n\n/**\n * Build a deep link redirect URI from your Expo app scheme.\n * @example buildRedirectUri(\"myapp\") → \"myapp://auth/callback\"\n */\nexport function buildRedirectUri(scheme: string, path = \"auth/callback\"): string {\n return `${scheme}://${path}`;\n}\n\nfunction normalizeBaseUrl(baseUrl: string) {\n return baseUrl.replace(/\\/$/, \"\");\n}\n\n// ── Auth client (Expo flavour) ─────────────────────────────────────────────────\n\n/**\n * Creates an auth client that uses SecureStore for token storage and\n * Linking.openURL for the OAuth redirect (instead of location.assign).\n */\nexport function initExpoAuth(config: SmartHiveExpoConfig): SmartHiveAuthClient {\n const base = initAuth({\n ...config,\n storage: secureStorage,\n temporaryStorage: secureStorage\n } as SmartHiveAuthConfig);\n\n return {\n ...base,\n async login(options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri;\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const request = new AuthRequest({\n clientId: config.publishableKey,\n redirectUri,\n responseType: \"code\",\n usePKCE: true,\n state: options?.state,\n extraParams: {\n project_id: config.projectId,\n publishable_key: config.publishableKey\n }\n });\n const url = await request.makeAuthUrlAsync({\n authorizationEndpoint: `${authBase}/api/auth/oauth2/authorize`\n });\n\n if (request.codeVerifier) {\n await secureStorage.setItem(storageKeys.pkceVerifier, request.codeVerifier);\n }\n await secureStorage.setItem(storageKeys.pkceState, request.state);\n await Linking.openURL(url);\n },\n\n async loginSocial(provider, options) {\n const redirectUri = options?.redirectUri ?? config.redirectUri;\n let socialUrl: URL;\n\n if (config.socialProxyUrl) {\n // Route through the app's own backend — Google/Apple show the app's domain\n socialUrl = new URL(`${normalizeBaseUrl(config.socialProxyUrl)}/api/auth/social/${provider}`);\n socialUrl.searchParams.set(\"redirect_uri\", redirectUri);\n // project_id is injected server-side by the backend proxy\n } else {\n // Direct SmartHive route\n const authBase = normalizeBaseUrl(config.authDomain ?? config.baseUrl);\n const env = envFromPublishableKey(config.publishableKey);\n socialUrl = new URL(`${authBase}/${env}/api/auth/social/${provider}`);\n socialUrl.searchParams.set(\"project_id\", config.projectId);\n socialUrl.searchParams.set(\"redirect_uri\", redirectUri);\n }\n\n // In-app browser sheet — auto-closes when redirectUri fires\n const result = await WebBrowser.openAuthSessionAsync(socialUrl.toString(), redirectUri);\n if (result.type === \"success\") {\n await base.handleSocialCallback({ url: result.url });\n }\n },\n\n async logout() {\n // React Native has no cookie jar, so the base SDK's credentials:\"include\" sign-out\n // endpoint never reaches the session. Use the headless signOut which sends the\n // refresh_token in the request body so the server can revoke the session.\n await base.headless.signOut();\n },\n };\n}\n\n// ── Context ────────────────────────────────────────────────────────────────────\n\ninterface AuthContextValue {\n client: SmartHiveAuthClient;\n session: AuthSession | null;\n isLoaded: boolean;\n isSignedIn: boolean;\n /** OAuth 2.0 + PKCE browser redirect sign-in (unchanged). */\n login(options?: { redirectUri?: string }): Promise<void>;\n logout(): Promise<void>;\n refreshSession(): Promise<void>;\n getAuthorizationHeader(): Promise<Record<string, string>>;\n authFetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;\n /**\n * Headless (no browser) sign-in methods for custom login screens.\n * Tokens are stored in SecureStore automatically on success.\n */\n signIn: {\n email(params: { email: string; password: string }): Promise<HeadlessSignInResult>;\n phone: {\n sendOtp(params: { phoneNumber: string }): Promise<void>;\n verify(params: { phoneNumber: string; code: string }): Promise<HeadlessSignInResult>;\n };\n emailOtp: {\n send(params: { email: string }): Promise<void>;\n verify(params: { email: string; code: string }): Promise<HeadlessSignInResult>;\n };\n magicLink: {\n send(params: { email: string; callbackURL?: string }): Promise<void>;\n };\n /** Initiate a social OAuth flow — opens an in-app browser sheet (expo-web-browser). Auto-closes on redirect and updates session state. */\n social(provider: SocialProvider, options?: { redirectUri?: string }): Promise<void>;\n };\n signUp: {\n email(params: { email: string; password: string; name?: string }): Promise<HeadlessSignUpResult>;\n resendVerificationEmail(params: { email: string }): Promise<void>;\n };\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ── Provider ───────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveAuthProviderProps extends SmartHiveExpoConfig {\n children: React.ReactNode;\n}\n\n/** @deprecated Use SmartHiveAuthProviderProps */\nexport type SmartHiveProviderProps = SmartHiveAuthProviderProps;\n\n/**\n * Wraps your Expo app and provides auth state to all child components.\n * Tokens are stored in SecureStore. Supports both:\n * - OAuth 2.0 + PKCE browser redirect via `login()`\n * - Headless direct sign-in via `signIn.*` (no browser, custom login screens)\n */\nexport function SmartHiveAuthProvider({ children, ...config }: SmartHiveAuthProviderProps) {\n const configRef = useRef(config);\n const client = useMemo(() => initExpoAuth(configRef.current), []);\n\n const [session, setSession] = useState<AuthSession | null>(null);\n const [isLoaded, setIsLoaded] = useState(false);\n\n // Initial session load from SecureStore\n useEffect(() => {\n let cancelled = false;\n client\n .initialize()\n .then(() => client.getSession())\n .then((s) => { if (!cancelled) setSession(s); })\n .catch(() => {})\n .finally(() => { if (!cancelled) setIsLoaded(true); });\n return () => { cancelled = true; };\n }, [client]);\n\n // Deep link listener — catches both PKCE and social OAuth callbacks\n useEffect(() => {\n function handleUrl({ url }: { url: string }) {\n try {\n const parsed = new URL(url);\n if (parsed.searchParams.has(\"access_token\") || parsed.searchParams.has(\"error\")) {\n // Social auth callback — tokens already in URL params\n client\n .handleSocialCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\n } else if (parsed.searchParams.has(\"code\")) {\n // PKCE OAuth callback — exchange code for tokens\n client\n .handleCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\n }\n } catch {\n // Unparseable URL — ignore\n }\n }\n\n const sub = Linking.addEventListener(\"url\", handleUrl);\n\n // Handle cold-start: app opened directly from the OAuth redirect URL\n Linking.getInitialURL().then((url) => {\n if (url) handleUrl({ url });\n });\n\n return () => sub.remove();\n }, [client]);\n\n const login = useCallback(\n (options?: { redirectUri?: string }) => client.login(options),\n [client]\n );\n\n const logout = useCallback(async () => {\n await client.logout();\n setSession(null);\n }, [client]);\n\n const refreshSession = useCallback(async () => {\n setSession(await client.refreshSession());\n }, [client]);\n\n const getAuthorizationHeader = useCallback(\n () => client.getAuthorizationHeader(),\n [client]\n );\n\n const authFetch = useCallback(\n (input: string | URL | Request, init?: RequestInit) => client.fetch(input, init),\n [client]\n );\n\n // ── Headless sign-in wrappers — save session + update state ──────────────────\n\n function wrapHeadlessSignIn(\n fn: (p: never) => Promise<HeadlessSignInResult>\n ) {\n return async (params: never) => {\n const result = await fn(params);\n setSession({\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: result.expiresAt,\n user: result.user,\n });\n return result;\n };\n }\n\n const signIn = useMemo<AuthContextValue[\"signIn\"]>(() => {\n const h: HeadlessClient = client.headless;\n return {\n email: wrapHeadlessSignIn(h.signIn.email.bind(h.signIn) as never),\n phone: {\n sendOtp: (p) => h.signIn.phone.sendOtp(p),\n verify: wrapHeadlessSignIn(h.signIn.phone.verify.bind(h.signIn.phone) as never),\n },\n emailOtp: {\n send: (p) => h.signIn.emailOtp.send(p),\n verify: wrapHeadlessSignIn(h.signIn.emailOtp.verify.bind(h.signIn.emailOtp) as never),\n },\n magicLink: {\n send: (p) => h.signIn.magicLink.send(p),\n },\n social: async (provider, options) => {\n await client.loginSocial(provider, options);\n // loginSocial used openAuthSessionAsync + handleSocialCallback — session is now in SecureStore\n const s = await client.getSession();\n if (s) setSession(s);\n },\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client]);\n\n const signUp = useMemo<AuthContextValue[\"signUp\"]>(() => {\n const h: HeadlessClient = client.headless;\n return {\n email: async (params) => {\n const result = await h.signUp.email(params);\n if (!result.requiresVerification) {\n setSession({\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n expiresAt: result.expiresAt,\n user: result.user,\n });\n }\n return result;\n },\n resendVerificationEmail: (params) => h.signUp.resendVerificationEmail(params),\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [client]);\n\n const value = useMemo<AuthContextValue>(\n () => ({\n client,\n session,\n isLoaded,\n isSignedIn: !!session,\n login,\n logout,\n refreshSession,\n getAuthorizationHeader,\n authFetch,\n signIn,\n signUp,\n }),\n [client, session, isLoaded, login, logout, refreshSession, getAuthorizationHeader, authFetch, signIn, signUp]\n );\n\n return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;\n}\n\n// ── Hooks ──────────────────────────────────────────────────────────────────────\n\nfunction useAuthContext(): AuthContextValue {\n const ctx = useContext(AuthContext);\n if (!ctx) throw new Error(\"useAuth must be used inside <SmartHiveAuthProvider>.\");\n return ctx;\n}\n\nexport function useAuth() { return useAuthContext(); }\nexport function useSession() { return useAuthContext().session; }\nexport function useUser() { return useAuthContext().session?.user ?? null; }\nexport function useIsLoaded() { return useAuthContext().isLoaded; }\n\nexport function useIsSignedIn() {\n const { isLoaded, isSignedIn } = useAuthContext();\n return isLoaded ? isSignedIn : null;\n}\n\nexport function useAuthFetch() {\n return useAuthContext().authFetch;\n}\n\nexport function useAuthorizationHeader() {\n return useAuthContext().getAuthorizationHeader;\n}\n\n/** @deprecated Use SmartHiveAuthProvider */\nexport const SmartHiveProvider = SmartHiveAuthProvider;\n\n// ── Render helpers ─────────────────────────────────────────────────────────────\n\nexport function SignedIn({ children }: { children: React.ReactNode }) {\n const { isLoaded, isSignedIn } = useAuthContext();\n if (!isLoaded || !isSignedIn) return null;\n return <>{children}</>;\n}\n\nexport function SignedOut({ children }: { children: React.ReactNode }) {\n const { isLoaded, isSignedIn } = useAuthContext();\n if (!isLoaded || isSignedIn) return null;\n return <>{children}</>;\n}\n\nexport function AuthLoading({ children }: { children: React.ReactNode }) {\n const { isLoaded } = useAuthContext();\n return isLoaded ? null : <>{children}</>;\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smarthivelabs-devs/auth-expo",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.1",
|
|
4
4
|
"description": "SmartHive Auth provider, hooks, and SecureStore integration for React Native / Expo",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"expo-web-browser": "^55.0.15",
|
|
40
40
|
"tsup": "^8.5.1",
|
|
41
41
|
"typescript": "^6.0.3",
|
|
42
|
-
"@smarthivelabs-devs/auth-sdk": "^1.5.
|
|
42
|
+
"@smarthivelabs-devs/auth-sdk": "^1.5.1"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "tsup",
|