@smarthivelabs-devs/auth-expo 0.1.0 → 1.1.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/README.md ADDED
@@ -0,0 +1,505 @@
1
+ # @smarthivelabs-devs/auth-expo
2
+
3
+ SmartHive Auth for React Native and Expo. Provides a provider, hooks, and components with SecureStore-backed token storage.
4
+
5
+ Supports two sign-in modes — both in the same package, zero config difference:
6
+
7
+ | Mode | How it works | Good for |
8
+ |---|---|---|
9
+ | **Headless** | Call `signIn.*` directly — no browser, custom UI | Native mobile apps with branded login screens |
10
+ | **OAuth redirect** | `login()` opens system browser, deep-links back | Social login, SSO, or when you want the hosted UI |
11
+
12
+ ---
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npx expo install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
18
+ ```
19
+
20
+ ```bash
21
+ # npm / pnpm
22
+ npm install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
23
+ pnpm add @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
24
+ ```
25
+
26
+ > **Peer dependencies:** `expo-auth-session>=5`, `expo-secure-store>=12`, `react>=18`, `react-native>=0.73`
27
+
28
+ ---
29
+
30
+ ## Setup
31
+
32
+ Wrap your root component with `SmartHiveProvider`. The `redirectUri` is only needed for the OAuth redirect flow — you can still set it even if you only use headless.
33
+
34
+ ```tsx
35
+ // app/_layout.tsx
36
+ import { SmartHiveProvider, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
37
+
38
+ export default function Layout() {
39
+ return (
40
+ <SmartHiveProvider
41
+ projectId={process.env.EXPO_PUBLIC_AUTH_PROJECT_ID!}
42
+ publishableKey={process.env.EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY!}
43
+ baseUrl={process.env.EXPO_PUBLIC_AUTH_BASE_URL!}
44
+ redirectUri={buildRedirectUri("myapp")}
45
+ >
46
+ <Stack />
47
+ </SmartHiveProvider>
48
+ );
49
+ }
50
+ ```
51
+
52
+ ```bash
53
+ # .env
54
+ EXPO_PUBLIC_AUTH_PROJECT_ID=proj_abc123
55
+ EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY=pk_prod_abc123
56
+ EXPO_PUBLIC_AUTH_BASE_URL=https://auth.myapp.com
57
+ ```
58
+
59
+ ---
60
+
61
+ ## Headless Sign-in (Custom Login Screen)
62
+
63
+ No browser, no redirect. Call the method, get tokens. Full control of your UI.
64
+
65
+ ### Email + Password
66
+
67
+ ```tsx
68
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
69
+ import { useState } from "react";
70
+ import { Button, TextInput, View, Text } from "react-native";
71
+
72
+ export default function LoginScreen() {
73
+ const { signIn } = useAuth();
74
+ const [email, setEmail] = useState("");
75
+ const [password, setPassword] = useState("");
76
+ const [error, setError] = useState("");
77
+
78
+ async function handleSignIn() {
79
+ try {
80
+ await signIn.email({ email, password });
81
+ // Session is saved automatically — user is now signed in
82
+ } catch (e: any) {
83
+ setError(e.message);
84
+ }
85
+ }
86
+
87
+ return (
88
+ <View>
89
+ <TextInput value={email} onChangeText={setEmail} placeholder="Email" />
90
+ <TextInput value={password} onChangeText={setPassword} placeholder="Password" secureTextEntry />
91
+ {error ? <Text>{error}</Text> : null}
92
+ <Button title="Sign in" onPress={handleSignIn} />
93
+ </View>
94
+ );
95
+ }
96
+ ```
97
+
98
+ ### Phone OTP
99
+
100
+ ```tsx
101
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
102
+ import { useState } from "react";
103
+ import { Button, TextInput, View } from "react-native";
104
+
105
+ export default function PhoneLoginScreen() {
106
+ const { signIn } = useAuth();
107
+ const [phone, setPhone] = useState("");
108
+ const [code, setCode] = useState("");
109
+ const [step, setStep] = useState<"phone" | "code">("phone");
110
+
111
+ async function sendOtp() {
112
+ await signIn.phone.sendOtp({ phoneNumber: phone });
113
+ setStep("code");
114
+ }
115
+
116
+ async function verifyOtp() {
117
+ await signIn.phone.verify({ phoneNumber: phone, code });
118
+ // Signed in — session saved automatically
119
+ }
120
+
121
+ if (step === "phone") {
122
+ return (
123
+ <View>
124
+ <TextInput value={phone} onChangeText={setPhone} placeholder="+1234567890" keyboardType="phone-pad" />
125
+ <Button title="Send code" onPress={sendOtp} />
126
+ </View>
127
+ );
128
+ }
129
+
130
+ return (
131
+ <View>
132
+ <TextInput value={code} onChangeText={setCode} placeholder="Enter code" keyboardType="number-pad" />
133
+ <Button title="Verify" onPress={verifyOtp} />
134
+ </View>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ### Email OTP
140
+
141
+ ```tsx
142
+ const { signIn } = useAuth();
143
+
144
+ // Step 1 — send the code
145
+ await signIn.emailOtp.send({ email: "user@example.com" });
146
+
147
+ // Step 2 — verify (returns session, user is now signed in)
148
+ await signIn.emailOtp.verify({ email: "user@example.com", code: "123456" });
149
+ ```
150
+
151
+ ### Magic Link
152
+
153
+ ```tsx
154
+ const { signIn } = useAuth();
155
+
156
+ // Sends an email — user clicks the link to sign in (no token returned here)
157
+ await signIn.magicLink.send({ email: "user@example.com" });
158
+ ```
159
+
160
+ ---
161
+
162
+ ## Headless Sign-up
163
+
164
+ ```tsx
165
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
166
+
167
+ const { signUp } = useAuth();
168
+
169
+ const result = await signUp.email({
170
+ email: "user@example.com",
171
+ password: "secret123",
172
+ name: "Jane Doe", // optional
173
+ });
174
+
175
+ if (result.requiresVerification) {
176
+ // Email verification required — show "check your inbox" screen
177
+ // No session yet, tokens are empty
178
+ } else {
179
+ // Account created and signed in immediately
180
+ }
181
+ ```
182
+
183
+ ---
184
+
185
+ ## OAuth Redirect Sign-in (Browser)
186
+
187
+ The original flow is completely unchanged. Use it for social login or when you want the hosted login page.
188
+
189
+ ```tsx
190
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
191
+ import { Button } from "react-native";
192
+
193
+ export default function LoginScreen() {
194
+ const { login } = useAuth();
195
+ return <Button title="Sign in with SmartHive" onPress={() => login()} />;
196
+ }
197
+ ```
198
+
199
+ ### Deep Link Setup (required for OAuth redirect only)
200
+
201
+ If you use `login()`, register a custom scheme in `app.json` so the browser can redirect back:
202
+
203
+ ```json
204
+ {
205
+ "expo": {
206
+ "scheme": "myapp",
207
+ "android": {
208
+ "intentFilters": [
209
+ {
210
+ "action": "VIEW",
211
+ "autoVerify": true,
212
+ "data": [{ "scheme": "myapp" }],
213
+ "category": ["BROWSABLE", "DEFAULT"]
214
+ }
215
+ ]
216
+ }
217
+ }
218
+ }
219
+ ```
220
+
221
+ Rebuild after changing `app.json`:
222
+
223
+ ```bash
224
+ npx expo prebuild
225
+ ```
226
+
227
+ No extra setup needed for headless sign-in — it works without a deep link.
228
+
229
+ ---
230
+
231
+ ## Sign-out
232
+
233
+ ```tsx
234
+ const { logout } = useAuth();
235
+
236
+ // Clears SecureStore + invalidates session on the server
237
+ await logout();
238
+ ```
239
+
240
+ ---
241
+
242
+ ## Hooks
243
+
244
+ ### `useAuth()`
245
+
246
+ Returns the full auth context.
247
+
248
+ ```tsx
249
+ const {
250
+ session, // AuthSession | null
251
+ isLoaded, // true once initial SecureStore read is done
252
+ isSignedIn, // boolean
253
+ login, // OAuth redirect sign-in
254
+ logout, // sign out
255
+ signIn, // headless sign-in methods
256
+ signUp, // headless sign-up methods
257
+ refreshSession, // force a token refresh
258
+ authFetch, // authenticated fetch wrapper
259
+ getAuthorizationHeader, // () => Promise<{ authorization: string }>
260
+ } = useAuth();
261
+ ```
262
+
263
+ ### `useSession()`
264
+
265
+ ```tsx
266
+ const session = useSession(); // AuthSession | null
267
+ // session.accessToken, session.refreshToken, session.expiresAt, session.user
268
+ ```
269
+
270
+ ### `useUser()`
271
+
272
+ ```tsx
273
+ const user = useUser(); // unknown | null
274
+ ```
275
+
276
+ ### `useIsLoaded()`
277
+
278
+ `true` once the initial SecureStore check is complete. Use this to avoid a flash of unauthenticated state on startup.
279
+
280
+ ```tsx
281
+ const isLoaded = useIsLoaded();
282
+ if (!isLoaded) return <SplashScreen />;
283
+ ```
284
+
285
+ ### `useIsSignedIn()`
286
+
287
+ Returns `true` (signed in), `false` (signed out), or `null` (still loading).
288
+
289
+ ```tsx
290
+ const isSignedIn = useIsSignedIn();
291
+
292
+ useEffect(() => {
293
+ if (isSignedIn === false) router.replace("/login");
294
+ }, [isSignedIn]);
295
+ ```
296
+
297
+ ### `useAuthFetch()`
298
+
299
+ Authenticated `fetch` wrapper. Bearer token is injected automatically and refreshed transparently when near expiry.
300
+
301
+ ```tsx
302
+ const authFetch = useAuthFetch();
303
+ const res = await authFetch("https://api.myapp.com/protected");
304
+ ```
305
+
306
+ ### `useAuthorizationHeader()`
307
+
308
+ Resolves to `{ authorization: "Bearer <token>" }`. Useful for GraphQL clients or custom SDK setup.
309
+
310
+ ```tsx
311
+ const getAuthorizationHeader = useAuthorizationHeader();
312
+ const headers = await getAuthorizationHeader();
313
+ ```
314
+
315
+ ---
316
+
317
+ ## Render Helpers
318
+
319
+ ```tsx
320
+ import { SignedIn, SignedOut, AuthLoading } from "@smarthivelabs-devs/auth-expo";
321
+
322
+ // Shown only when loaded + authenticated
323
+ <SignedIn><Dashboard /></SignedIn>
324
+
325
+ // Shown only when loaded + not authenticated
326
+ <SignedOut><LoginScreen /></SignedOut>
327
+
328
+ // Shown while the initial SecureStore check is running
329
+ <AuthLoading><ActivityIndicator /></AuthLoading>
330
+ ```
331
+
332
+ ---
333
+
334
+ ## Expo Router Integration
335
+
336
+ ```
337
+ app/
338
+ ├── _layout.tsx ← SmartHiveProvider here
339
+ ├── index.tsx ← SignedIn / SignedOut routing
340
+ ├── login.tsx ← your custom login screen using signIn.*
341
+ └── (protected)/
342
+ └── dashboard.tsx
343
+ ```
344
+
345
+ ```tsx
346
+ // app/_layout.tsx
347
+ import { Stack } from "expo-router";
348
+ import { SmartHiveProvider, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
349
+
350
+ export default function Layout() {
351
+ return (
352
+ <SmartHiveProvider
353
+ projectId={process.env.EXPO_PUBLIC_AUTH_PROJECT_ID!}
354
+ publishableKey={process.env.EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY!}
355
+ baseUrl={process.env.EXPO_PUBLIC_AUTH_BASE_URL!}
356
+ redirectUri={buildRedirectUri("myapp")}
357
+ >
358
+ <Stack />
359
+ </SmartHiveProvider>
360
+ );
361
+ }
362
+ ```
363
+
364
+ ```tsx
365
+ // app/index.tsx
366
+ import { SignedIn, SignedOut, AuthLoading } from "@smarthivelabs-devs/auth-expo";
367
+ import { Redirect } from "expo-router";
368
+ import { ActivityIndicator } from "react-native";
369
+
370
+ export default function Index() {
371
+ return (
372
+ <>
373
+ <AuthLoading><ActivityIndicator /></AuthLoading>
374
+ <SignedIn><Redirect href="/dashboard" /></SignedIn>
375
+ <SignedOut><Redirect href="/login" /></SignedOut>
376
+ </>
377
+ );
378
+ }
379
+ ```
380
+
381
+ ```tsx
382
+ // app/login.tsx — custom screen, no browser redirect
383
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
384
+ import { useState } from "react";
385
+ import { Button, TextInput, View, Text, StyleSheet } from "react-native";
386
+
387
+ export default function LoginScreen() {
388
+ const { signIn } = useAuth();
389
+ const [email, setEmail] = useState("");
390
+ const [password, setPassword] = useState("");
391
+ const [loading, setLoading] = useState(false);
392
+ const [error, setError] = useState("");
393
+
394
+ async function handleSignIn() {
395
+ setLoading(true);
396
+ setError("");
397
+ try {
398
+ await signIn.email({ email, password });
399
+ } catch (e: any) {
400
+ setError(e.message ?? "Sign in failed.");
401
+ } finally {
402
+ setLoading(false);
403
+ }
404
+ }
405
+
406
+ return (
407
+ <View style={styles.container}>
408
+ <TextInput style={styles.input} value={email} onChangeText={setEmail} placeholder="Email" autoCapitalize="none" />
409
+ <TextInput style={styles.input} value={password} onChangeText={setPassword} placeholder="Password" secureTextEntry />
410
+ {error ? <Text style={styles.error}>{error}</Text> : null}
411
+ <Button title={loading ? "Signing in…" : "Sign in"} onPress={handleSignIn} disabled={loading} />
412
+ </View>
413
+ );
414
+ }
415
+
416
+ const styles = StyleSheet.create({
417
+ container: { flex: 1, justifyContent: "center", padding: 24 },
418
+ input: { borderWidth: 1, borderColor: "#ccc", borderRadius: 8, padding: 12, marginBottom: 12 },
419
+ error: { color: "red", marginBottom: 12 },
420
+ });
421
+ ```
422
+
423
+ ---
424
+
425
+ ## Token Storage
426
+
427
+ All tokens are stored in `expo-secure-store`:
428
+
429
+ - **iOS**: Keychain Services
430
+ - **Android**: Android Keystore (AES encryption)
431
+
432
+ PKCE verifier and state (used during OAuth redirect flow) are also stored in SecureStore and deleted after the code exchange completes.
433
+
434
+ ---
435
+
436
+ ## Provider Props
437
+
438
+ | Prop | Type | Required | Description |
439
+ |---|---|---|---|
440
+ | `projectId` | `string` | Yes | Your SmartHive project ID |
441
+ | `publishableKey` | `string` | Yes | Your publishable key (`pk_prod_*`) |
442
+ | `baseUrl` | `string` | Yes | URL of your SmartHive Auth service |
443
+ | `redirectUri` | `string` | Yes | Deep link callback URI — used only for OAuth redirect flow |
444
+ | `authDomain` | `string` | No | Custom branded auth domain |
445
+ | `children` | `ReactNode` | Yes | Your app tree |
446
+
447
+ ---
448
+
449
+ ## TypeScript Types
450
+
451
+ ```ts
452
+ import type {
453
+ SmartHiveExpoConfig,
454
+ SmartHiveProviderProps,
455
+ } from "@smarthivelabs-devs/auth-expo";
456
+
457
+ import type {
458
+ AuthSession,
459
+ HeadlessClient,
460
+ HeadlessSignInResult,
461
+ HeadlessSignUpResult,
462
+ SmartHiveAuthClient,
463
+ } from "@smarthivelabs-devs/auth-sdk";
464
+ ```
465
+
466
+ ---
467
+
468
+ ## Low-level: `initExpoAuth`
469
+
470
+ Direct client without the provider (advanced use):
471
+
472
+ ```ts
473
+ import { initExpoAuth, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
474
+
475
+ const client = initExpoAuth({
476
+ projectId: "proj_abc123",
477
+ publishableKey: "pk_prod_abc123",
478
+ baseUrl: "https://auth.myapp.com",
479
+ redirectUri: buildRedirectUri("myapp"),
480
+ });
481
+
482
+ await client.initialize();
483
+
484
+ // Headless sign-in
485
+ const session = await client.headless.signIn.email({ email, password });
486
+
487
+ // OAuth redirect
488
+ await client.login();
489
+ ```
490
+
491
+ ---
492
+
493
+ ## Related Packages
494
+
495
+ | Package | Use case |
496
+ |---|---|
497
+ | [`@smarthivelabs-devs/auth-sdk`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-sdk) | Core SDK — framework-agnostic |
498
+ | [`@smarthivelabs-devs/auth-react`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-react) | React web apps |
499
+ | [`@smarthivelabs-devs/auth-server`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-server) | Express / Next.js server-side JWT verification |
500
+
501
+ ---
502
+
503
+ ## License
504
+
505
+ MIT © SmartHive Labs
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
- import { SmartHiveAuthConfig, SmartHiveAuthClient, AuthSession } from '@smarthivelabs-devs/auth-sdk';
2
+ import { SmartHiveAuthConfig, SmartHiveAuthClient, AuthSession, HeadlessSignInResult, HeadlessSignUpResult } from '@smarthivelabs-devs/auth-sdk';
3
3
 
4
4
  interface SmartHiveExpoConfig extends Omit<SmartHiveAuthConfig, "storage"> {
5
5
  /** Deep link redirect URI, e.g. "myapp://auth/callback" */
@@ -20,6 +20,7 @@ interface AuthContextValue {
20
20
  session: AuthSession | null;
21
21
  isLoaded: boolean;
22
22
  isSignedIn: boolean;
23
+ /** OAuth 2.0 + PKCE browser redirect sign-in (unchanged). */
23
24
  login(options?: {
24
25
  redirectUri?: string;
25
26
  }): Promise<void>;
@@ -27,23 +28,56 @@ interface AuthContextValue {
27
28
  refreshSession(): Promise<void>;
28
29
  getAuthorizationHeader(): Promise<Record<string, string>>;
29
30
  authFetch(input: string | URL | Request, init?: RequestInit): Promise<Response>;
31
+ /**
32
+ * Headless (no browser) sign-in methods for custom login screens.
33
+ * Tokens are stored in SecureStore automatically on success.
34
+ */
35
+ signIn: {
36
+ email(params: {
37
+ email: string;
38
+ password: string;
39
+ }): Promise<HeadlessSignInResult>;
40
+ phone: {
41
+ sendOtp(params: {
42
+ phoneNumber: string;
43
+ }): Promise<void>;
44
+ verify(params: {
45
+ phoneNumber: string;
46
+ code: string;
47
+ }): Promise<HeadlessSignInResult>;
48
+ };
49
+ emailOtp: {
50
+ send(params: {
51
+ email: string;
52
+ }): Promise<void>;
53
+ verify(params: {
54
+ email: string;
55
+ code: string;
56
+ }): Promise<HeadlessSignInResult>;
57
+ };
58
+ magicLink: {
59
+ send(params: {
60
+ email: string;
61
+ callbackURL?: string;
62
+ }): Promise<void>;
63
+ };
64
+ };
65
+ signUp: {
66
+ email(params: {
67
+ email: string;
68
+ password: string;
69
+ name?: string;
70
+ }): Promise<HeadlessSignUpResult>;
71
+ };
30
72
  }
31
73
  interface SmartHiveProviderProps extends SmartHiveExpoConfig {
32
74
  children: React.ReactNode;
33
75
  }
34
76
  /**
35
77
  * Wraps your Expo app and provides auth state to all child components.
36
- * Tokens are stored in SecureStore. OAuth is handled via deep links.
37
- *
38
- * @example
39
- * <SmartHiveProvider
40
- * projectId="proj_..."
41
- * publishableKey="pk_..."
42
- * baseUrl="https://authcore.smarthivelabs.dev/prod"
43
- * redirectUri="myapp://auth/callback"
44
- * >
45
- * <App />
46
- * </SmartHiveProvider>
78
+ * Tokens are stored in SecureStore. Supports both:
79
+ * - OAuth 2.0 + PKCE browser redirect via `login()`
80
+ * - Headless direct sign-in via `signIn.*` (no browser, custom login screens)
47
81
  */
48
82
  declare function SmartHiveProvider({ children, ...config }: SmartHiveProviderProps): react_jsx_runtime.JSX.Element;
49
83
  declare function useAuth(): AuthContextValue;
package/dist/index.js CHANGED
@@ -111,6 +111,52 @@ function SmartHiveProvider({ children, ...config }) {
111
111
  (input, init) => client.fetch(input, init),
112
112
  [client]
113
113
  );
114
+ function wrapHeadlessSignIn(fn) {
115
+ return async (params) => {
116
+ const result = await fn(params);
117
+ setSession({
118
+ accessToken: result.accessToken,
119
+ refreshToken: result.refreshToken,
120
+ expiresAt: result.expiresAt,
121
+ user: result.user
122
+ });
123
+ return result;
124
+ };
125
+ }
126
+ const signIn = useMemo(() => {
127
+ const h = client.headless;
128
+ return {
129
+ email: wrapHeadlessSignIn(h.signIn.email.bind(h.signIn)),
130
+ phone: {
131
+ sendOtp: (p) => h.signIn.phone.sendOtp(p),
132
+ verify: wrapHeadlessSignIn(h.signIn.phone.verify.bind(h.signIn.phone))
133
+ },
134
+ emailOtp: {
135
+ send: (p) => h.signIn.emailOtp.send(p),
136
+ verify: wrapHeadlessSignIn(h.signIn.emailOtp.verify.bind(h.signIn.emailOtp))
137
+ },
138
+ magicLink: {
139
+ send: (p) => h.signIn.magicLink.send(p)
140
+ }
141
+ };
142
+ }, [client]);
143
+ const signUp = useMemo(() => {
144
+ const h = client.headless;
145
+ return {
146
+ email: async (params) => {
147
+ const result = await h.signUp.email(params);
148
+ if (!result.requiresVerification) {
149
+ setSession({
150
+ accessToken: result.accessToken,
151
+ refreshToken: result.refreshToken,
152
+ expiresAt: result.expiresAt,
153
+ user: result.user
154
+ });
155
+ }
156
+ return result;
157
+ }
158
+ };
159
+ }, [client]);
114
160
  const value = useMemo(
115
161
  () => ({
116
162
  client,
@@ -121,9 +167,11 @@ function SmartHiveProvider({ children, ...config }) {
121
167
  logout,
122
168
  refreshSession,
123
169
  getAuthorizationHeader,
124
- authFetch
170
+ authFetch,
171
+ signIn,
172
+ signUp
125
173
  }),
126
- [client, session, isLoaded, login, logout, refreshSession, getAuthorizationHeader, authFetch]
174
+ [client, session, isLoaded, login, logout, refreshSession, getAuthorizationHeader, authFetch, signIn, signUp]
127
175
  );
128
176
  return /* @__PURE__ */ jsx(AuthContext.Provider, { value, children });
129
177
  }
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 SecureStore from \"expo-secure-store\";\nimport {\n initAuth,\n type AuthSession,\n type AuthStorage,\n type SmartHiveAuthClient,\n type SmartHiveAuthConfig,\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\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}\n\n// ── Context ────────────────────────────────────────────────────────────────────\n\ninterface AuthContextValue {\n client: SmartHiveAuthClient;\n session: AuthSession | null;\n isLoaded: boolean;\n isSignedIn: boolean;\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\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ── Provider ───────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveProviderProps extends SmartHiveExpoConfig {\n children: React.ReactNode;\n}\n\n/**\n * Wraps your Expo app and provides auth state to all child components.\n * Tokens are stored in SecureStore. OAuth is handled via deep links.\n *\n * @example\n * <SmartHiveProvider\n * projectId=\"proj_...\"\n * publishableKey=\"pk_...\"\n * baseUrl=\"https://authcore.smarthivelabs.dev/prod\"\n * redirectUri=\"myapp://auth/callback\"\n * >\n * <App />\n * </SmartHiveProvider>\n */\nexport function SmartHiveProvider({ children, ...config }: SmartHiveProviderProps) {\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 the OAuth callback redirect back into the app\n useEffect(() => {\n function handleUrl({ url }: { url: string }) {\n client\n .handleCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\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 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 }),\n [client, session, isLoaded, login, logout, refreshSession, getAuthorizationHeader, authFetch]\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 <SmartHiveProvider>.\");\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// ── 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":[]}
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 SecureStore from \"expo-secure-store\";\nimport {\n initAuth,\n type AuthSession,\n type AuthStorage,\n type HeadlessClient,\n type HeadlessSignInResult,\n type HeadlessSignUpResult,\n type SmartHiveAuthClient,\n type SmartHiveAuthConfig,\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\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}\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 };\n signUp: {\n email(params: { email: string; password: string; name?: string }): Promise<HeadlessSignUpResult>;\n };\n}\n\nconst AuthContext = createContext<AuthContextValue | null>(null);\n\n// ── Provider ───────────────────────────────────────────────────────────────────\n\nexport interface SmartHiveProviderProps extends SmartHiveExpoConfig {\n children: React.ReactNode;\n}\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 SmartHiveProvider({ children, ...config }: SmartHiveProviderProps) {\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 the OAuth callback redirect back into the app\n useEffect(() => {\n function handleUrl({ url }: { url: string }) {\n client\n .handleCallback({ url })\n .then((s) => setSession(s))\n .catch(() => {});\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 };\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 };\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 <SmartHiveProvider>.\");\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// ── 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": "0.1.0",
3
+ "version": "1.1.0",
4
4
  "description": "SmartHive Auth provider, hooks, and SecureStore integration for React Native / Expo",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -25,7 +25,7 @@
25
25
  "LICENSE"
26
26
  ],
27
27
  "peerDependencies": {
28
- "@smarthivelabs-devs/auth-sdk": "^0.1.0",
28
+ "@smarthivelabs-devs/auth-sdk": "^1.1.0",
29
29
  "expo-auth-session": ">=5",
30
30
  "expo-secure-store": ">=12",
31
31
  "react": ">=18",
@@ -36,7 +36,7 @@
36
36
  "@types/react-native": "^0.73.0",
37
37
  "tsup": "^8.3.0",
38
38
  "typescript": "^5.7.3",
39
- "@smarthivelabs-devs/auth-sdk": "^0.1.0"
39
+ "@smarthivelabs-devs/auth-sdk": "^1.1.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsup",