@smarthivelabs-devs/auth-expo 0.1.0 → 1.0.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.
Files changed (2) hide show
  1. package/README.md +472 -0
  2. package/package.json +3 -3
package/README.md ADDED
@@ -0,0 +1,472 @@
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 and deep-link OAuth support.
4
+
5
+ ---
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ npx expo install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
11
+ ```
12
+
13
+ Or with npm/pnpm (non-Expo managed workflow):
14
+
15
+ ```bash
16
+ npm install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
17
+ pnpm add @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
18
+ ```
19
+
20
+ > **Peer dependencies required:** `expo-auth-session>=5`, `expo-secure-store>=12`, `react>=18`, `react-native>=0.73`
21
+
22
+ ---
23
+
24
+ ## Prerequisites
25
+
26
+ 1. A SmartHive Auth project — grab `projectId`, `publishableKey`, and `baseUrl` from your dashboard.
27
+ 2. A registered deep link scheme for your Expo app (see below).
28
+
29
+ ---
30
+
31
+ ## Deep Link Setup
32
+
33
+ SmartHive Auth uses OAuth 2.0 + PKCE. After login, the server redirects back to your app via a deep link (e.g. `myapp://auth/callback`). You must register a custom scheme in `app.json`:
34
+
35
+ ```json
36
+ // app.json
37
+ {
38
+ "expo": {
39
+ "scheme": "myapp",
40
+ "android": {
41
+ "intentFilters": [
42
+ {
43
+ "action": "VIEW",
44
+ "autoVerify": true,
45
+ "data": [{ "scheme": "myapp" }],
46
+ "category": ["BROWSABLE", "DEFAULT"]
47
+ }
48
+ ]
49
+ }
50
+ }
51
+ }
52
+ ```
53
+
54
+ Rebuild your native app after this change:
55
+
56
+ ```bash
57
+ npx expo prebuild
58
+ ```
59
+
60
+ ---
61
+
62
+ ## Setup
63
+
64
+ Wrap your root component with `SmartHiveProvider`. Use `buildRedirectUri` to construct the deep link URI from your scheme.
65
+
66
+ ```tsx
67
+ // App.tsx
68
+ import { SmartHiveProvider, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
69
+
70
+ const REDIRECT_URI = buildRedirectUri("myapp"); // → "myapp://auth/callback"
71
+
72
+ export default function App() {
73
+ return (
74
+ <SmartHiveProvider
75
+ projectId={process.env.EXPO_PUBLIC_AUTH_PROJECT_ID!}
76
+ publishableKey={process.env.EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY!}
77
+ baseUrl={process.env.EXPO_PUBLIC_AUTH_BASE_URL!}
78
+ redirectUri={REDIRECT_URI}
79
+ >
80
+ <RootNavigator />
81
+ </SmartHiveProvider>
82
+ );
83
+ }
84
+ ```
85
+
86
+ ### `buildRedirectUri(scheme, path?)`
87
+
88
+ Helper that constructs a deep link URI:
89
+
90
+ ```ts
91
+ buildRedirectUri("myapp") // → "myapp://auth/callback"
92
+ buildRedirectUri("myapp", "auth/done") // → "myapp://auth/done"
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Provider Props
98
+
99
+ | Prop | Type | Required | Description |
100
+ |---|---|---|---|
101
+ | `projectId` | `string` | Yes | Your SmartHive project ID |
102
+ | `publishableKey` | `string` | Yes | Your publishable key |
103
+ | `baseUrl` | `string` | Yes | URL of your SmartHive Auth service |
104
+ | `redirectUri` | `string` | Yes | Deep link callback URI (e.g. `myapp://auth/callback`) |
105
+ | `authDomain` | `string` | No | Custom branded auth domain |
106
+ | `children` | `ReactNode` | Yes | Your app tree |
107
+
108
+ ---
109
+
110
+ ## How OAuth Works in Expo
111
+
112
+ 1. User taps **Sign in** → `login()` is called
113
+ 2. The SDK builds the OAuth URL with PKCE, saves the verifier in SecureStore, and opens the URL in the system browser via `Linking.openURL()`
114
+ 3. The user logs in via the browser
115
+ 4. The server redirects to `myapp://auth/callback?code=...&state=...`
116
+ 5. `Linking` fires a `url` event — `SmartHiveProvider` handles it automatically, exchanges the code for tokens, and updates the session state
117
+
118
+ No manual callback handling is needed — the provider sets up the `Linking` event listener internally.
119
+
120
+ ---
121
+
122
+ ## Hooks
123
+
124
+ ### `useAuth()`
125
+
126
+ Returns the full auth context.
127
+
128
+ ```tsx
129
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
130
+
131
+ function HomeScreen() {
132
+ const {
133
+ session, // AuthSession | null
134
+ isLoaded, // true once initial SecureStore read is done
135
+ isSignedIn, // boolean
136
+ login, // (options?) => Promise<void>
137
+ logout, // () => Promise<void>
138
+ refreshSession, // () => Promise<void>
139
+ authFetch, // authenticated fetch wrapper
140
+ } = useAuth();
141
+
142
+ if (!isLoaded) return <ActivityIndicator />;
143
+
144
+ return isSignedIn ? (
145
+ <Button title="Sign out" onPress={logout} />
146
+ ) : (
147
+ <Button title="Sign in" onPress={() => login()} />
148
+ );
149
+ }
150
+ ```
151
+
152
+ ---
153
+
154
+ ### `useSession()`
155
+
156
+ Returns the current `AuthSession` or `null`.
157
+
158
+ ```tsx
159
+ import { useSession } from "@smarthivelabs-devs/auth-expo";
160
+
161
+ function TokenScreen() {
162
+ const session = useSession();
163
+ if (!session) return <Text>Not signed in</Text>;
164
+ return <Text selectable>{session.accessToken}</Text>;
165
+ }
166
+ ```
167
+
168
+ ---
169
+
170
+ ### `useUser()`
171
+
172
+ Returns the user object from the session, or `null`.
173
+
174
+ ```tsx
175
+ import { useUser } from "@smarthivelabs-devs/auth-expo";
176
+
177
+ function ProfileScreen() {
178
+ const user = useUser();
179
+ if (!user) return null;
180
+ return <Text>Welcome, {(user as any).email}</Text>;
181
+ }
182
+ ```
183
+
184
+ ---
185
+
186
+ ### `useIsLoaded()`
187
+
188
+ Returns `true` once the initial SecureStore session check is done. Prevents a flash of the signed-out state on app startup.
189
+
190
+ ```tsx
191
+ import { useIsLoaded } from "@smarthivelabs-devs/auth-expo";
192
+
193
+ function RootNavigator() {
194
+ const isLoaded = useIsLoaded();
195
+ if (!isLoaded) return <SplashScreen />;
196
+ return <AppNavigator />;
197
+ }
198
+ ```
199
+
200
+ ---
201
+
202
+ ### `useIsSignedIn()`
203
+
204
+ Returns `true` (signed in), `false` (signed out), or `null` (still loading).
205
+
206
+ ```tsx
207
+ import { useIsSignedIn } from "@smarthivelabs-devs/auth-expo";
208
+
209
+ function AuthGate({ children }: { children: React.ReactNode }) {
210
+ const isSignedIn = useIsSignedIn();
211
+ const router = useRouter();
212
+
213
+ useEffect(() => {
214
+ if (isSignedIn === false) router.replace("/login");
215
+ }, [isSignedIn]);
216
+
217
+ if (isSignedIn === null) return <ActivityIndicator />;
218
+ if (!isSignedIn) return null;
219
+ return <>{children}</>;
220
+ }
221
+ ```
222
+
223
+ ---
224
+
225
+ ### `useAuthFetch()`
226
+
227
+ Returns an authenticated `fetch` function. Automatically injects the Bearer token on every request — tokens are refreshed transparently.
228
+
229
+ ```tsx
230
+ import { useAuthFetch } from "@smarthivelabs-devs/auth-expo";
231
+
232
+ function DataScreen() {
233
+ const authFetch = useAuthFetch();
234
+ const [data, setData] = useState(null);
235
+
236
+ async function load() {
237
+ const res = await authFetch("https://api.myapp.com/protected");
238
+ setData(await res.json());
239
+ }
240
+
241
+ return (
242
+ <View>
243
+ <Button title="Load data" onPress={load} />
244
+ {data && <Text>{JSON.stringify(data)}</Text>}
245
+ </View>
246
+ );
247
+ }
248
+ ```
249
+
250
+ ---
251
+
252
+ ### `useAuthorizationHeader()`
253
+
254
+ Returns a function that resolves to `{ authorization: "Bearer <token>" }`. Useful for passing to SDKs or GraphQL clients.
255
+
256
+ ```tsx
257
+ import { useAuthorizationHeader } from "@smarthivelabs-devs/auth-expo";
258
+
259
+ function ApolloSetup() {
260
+ const getAuthorizationHeader = useAuthorizationHeader();
261
+
262
+ const authLink = new ApolloLink(async (operation, forward) => {
263
+ const headers = await getAuthorizationHeader();
264
+ operation.setContext({ headers });
265
+ return forward(operation);
266
+ });
267
+
268
+ // ...
269
+ }
270
+ ```
271
+
272
+ ---
273
+
274
+ ## Render Helpers
275
+
276
+ ### `<SignedIn>`
277
+
278
+ Renders children only when auth has loaded **and** a session exists.
279
+
280
+ ```tsx
281
+ import { SignedIn } from "@smarthivelabs-devs/auth-expo";
282
+
283
+ <SignedIn>
284
+ <UserDashboard />
285
+ </SignedIn>
286
+ ```
287
+
288
+ ### `<SignedOut>`
289
+
290
+ Renders children only when auth has loaded **and** there is no session.
291
+
292
+ ```tsx
293
+ import { SignedOut } from "@smarthivelabs-devs/auth-expo";
294
+
295
+ <SignedOut>
296
+ <LandingScreen />
297
+ </SignedOut>
298
+ ```
299
+
300
+ ### `<AuthLoading>`
301
+
302
+ Renders children while the initial session check is running.
303
+
304
+ ```tsx
305
+ import { AuthLoading } from "@smarthivelabs-devs/auth-expo";
306
+
307
+ <AuthLoading>
308
+ <ActivityIndicator size="large" />
309
+ </AuthLoading>
310
+ ```
311
+
312
+ ---
313
+
314
+ ## Expo Router Integration
315
+
316
+ ```
317
+ app/
318
+ ├── _layout.tsx ← SmartHiveProvider here
319
+ ├── index.tsx ← SignedIn / SignedOut
320
+ ├── login.tsx ← login screen
321
+ └── (protected)/
322
+ └── dashboard.tsx ← authenticated screen
323
+ ```
324
+
325
+ ```tsx
326
+ // app/_layout.tsx
327
+ import { Stack } from "expo-router";
328
+ import { SmartHiveProvider, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
329
+
330
+ export default function Layout() {
331
+ return (
332
+ <SmartHiveProvider
333
+ projectId={process.env.EXPO_PUBLIC_AUTH_PROJECT_ID!}
334
+ publishableKey={process.env.EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY!}
335
+ baseUrl={process.env.EXPO_PUBLIC_AUTH_BASE_URL!}
336
+ redirectUri={buildRedirectUri("myapp")}
337
+ >
338
+ <Stack />
339
+ </SmartHiveProvider>
340
+ );
341
+ }
342
+ ```
343
+
344
+ ```tsx
345
+ // app/index.tsx
346
+ import { SignedIn, SignedOut, AuthLoading } from "@smarthivelabs-devs/auth-expo";
347
+ import { Redirect } from "expo-router";
348
+ import { ActivityIndicator } from "react-native";
349
+
350
+ export default function Index() {
351
+ return (
352
+ <>
353
+ <AuthLoading><ActivityIndicator /></AuthLoading>
354
+ <SignedIn><Redirect href="/dashboard" /></SignedIn>
355
+ <SignedOut><Redirect href="/login" /></SignedOut>
356
+ </>
357
+ );
358
+ }
359
+ ```
360
+
361
+ ```tsx
362
+ // app/login.tsx
363
+ import { useAuth } from "@smarthivelabs-devs/auth-expo";
364
+ import { Button, View } from "react-native";
365
+
366
+ export default function LoginScreen() {
367
+ const { login } = useAuth();
368
+ return (
369
+ <View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
370
+ <Button title="Sign in with SmartHive" onPress={() => login()} />
371
+ </View>
372
+ );
373
+ }
374
+ ```
375
+
376
+ ```tsx
377
+ // app/(protected)/dashboard.tsx
378
+ import { useAuth, SignedIn } from "@smarthivelabs-devs/auth-expo";
379
+ import { Button, Text, View } from "react-native";
380
+
381
+ export default function Dashboard() {
382
+ const { logout, authFetch } = useAuth();
383
+
384
+ return (
385
+ <SignedIn>
386
+ <View>
387
+ <Text>Dashboard</Text>
388
+ <Button title="Sign out" onPress={logout} />
389
+ </View>
390
+ </SignedIn>
391
+ );
392
+ }
393
+ ```
394
+
395
+ ---
396
+
397
+ ## Low-level: `initExpoAuth`
398
+
399
+ If you need the client directly without the provider (advanced use), use `initExpoAuth`:
400
+
401
+ ```ts
402
+ import { initExpoAuth, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
403
+
404
+ const client = initExpoAuth({
405
+ projectId: "proj_abc123",
406
+ publishableKey: "pk_live_abc123",
407
+ baseUrl: "https://auth.myapp.com",
408
+ redirectUri: buildRedirectUri("myapp"),
409
+ });
410
+
411
+ await client.initialize();
412
+
413
+ // The client behaves the same as SmartHiveAuthClient from auth-sdk,
414
+ // but uses SecureStore and Linking instead of localStorage + location.assign
415
+ ```
416
+
417
+ ---
418
+
419
+ ## Environment Variables
420
+
421
+ With Expo's built-in env support (SDK 49+):
422
+
423
+ ```bash
424
+ # .env
425
+ EXPO_PUBLIC_AUTH_PROJECT_ID=proj_abc123
426
+ EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY=pk_live_abc123
427
+ EXPO_PUBLIC_AUTH_BASE_URL=https://auth.myapp.com
428
+ ```
429
+
430
+ > Variables prefixed `EXPO_PUBLIC_` are embedded in the app bundle at build time.
431
+
432
+ ---
433
+
434
+ ## Token Storage
435
+
436
+ Tokens are stored in `expo-secure-store`, which uses:
437
+ - **iOS**: Keychain Services
438
+ - **Android**: Keystore System (Android Keystore-backed AES encryption)
439
+
440
+ PKCE verifier and state are also stored in SecureStore during the login flow and deleted after the code exchange.
441
+
442
+ ---
443
+
444
+ ## TypeScript Types
445
+
446
+ ```ts
447
+ import type {
448
+ SmartHiveExpoConfig,
449
+ SmartHiveProviderProps,
450
+ } from "@smarthivelabs-devs/auth-expo";
451
+
452
+ import type {
453
+ AuthSession,
454
+ SmartHiveAuthClient,
455
+ } from "@smarthivelabs-devs/auth-sdk";
456
+ ```
457
+
458
+ ---
459
+
460
+ ## Related Packages
461
+
462
+ | Package | Use case |
463
+ |---|---|
464
+ | [`@smarthivelabs-devs/auth-sdk`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-sdk) | Core SDK — framework-agnostic |
465
+ | [`@smarthivelabs-devs/auth-react`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-react) | React web apps |
466
+ | [`@smarthivelabs-devs/auth-server`](https://www.npmjs.com/package/@smarthivelabs-devs/auth-server) | Express / Next.js server-side JWT verification |
467
+
468
+ ---
469
+
470
+ ## License
471
+
472
+ MIT © SmartHive Labs
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.0.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.0.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.0.0"
40
40
  },
41
41
  "scripts": {
42
42
  "build": "tsup",