@smarthivelabs-devs/auth-expo 1.0.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 +286 -253
- package/dist/index.d.ts +46 -12
- package/dist/index.js +50 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
# @smarthivelabs-devs/auth-expo
|
|
2
2
|
|
|
3
|
-
SmartHive Auth for React Native and Expo. Provides a provider, hooks, and components with SecureStore-backed token storage
|
|
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 |
|
|
4
11
|
|
|
5
12
|
---
|
|
6
13
|
|
|
@@ -10,303 +17,316 @@ SmartHive Auth for React Native and Expo. Provides a provider, hooks, and compon
|
|
|
10
17
|
npx expo install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
|
|
11
18
|
```
|
|
12
19
|
|
|
13
|
-
Or with npm/pnpm (non-Expo managed workflow):
|
|
14
|
-
|
|
15
20
|
```bash
|
|
21
|
+
# npm / pnpm
|
|
16
22
|
npm install @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
|
|
17
23
|
pnpm add @smarthivelabs-devs/auth-expo @smarthivelabs-devs/auth-sdk expo-auth-session expo-secure-store
|
|
18
24
|
```
|
|
19
25
|
|
|
20
|
-
> **Peer dependencies
|
|
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
|
-
```
|
|
26
|
+
> **Peer dependencies:** `expo-auth-session>=5`, `expo-secure-store>=12`, `react>=18`, `react-native>=0.73`
|
|
59
27
|
|
|
60
28
|
---
|
|
61
29
|
|
|
62
30
|
## Setup
|
|
63
31
|
|
|
64
|
-
Wrap your root component with `SmartHiveProvider`.
|
|
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.
|
|
65
33
|
|
|
66
34
|
```tsx
|
|
67
|
-
//
|
|
35
|
+
// app/_layout.tsx
|
|
68
36
|
import { SmartHiveProvider, buildRedirectUri } from "@smarthivelabs-devs/auth-expo";
|
|
69
37
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
export default function App() {
|
|
38
|
+
export default function Layout() {
|
|
73
39
|
return (
|
|
74
40
|
<SmartHiveProvider
|
|
75
41
|
projectId={process.env.EXPO_PUBLIC_AUTH_PROJECT_ID!}
|
|
76
42
|
publishableKey={process.env.EXPO_PUBLIC_AUTH_PUBLISHABLE_KEY!}
|
|
77
43
|
baseUrl={process.env.EXPO_PUBLIC_AUTH_BASE_URL!}
|
|
78
|
-
redirectUri={
|
|
44
|
+
redirectUri={buildRedirectUri("myapp")}
|
|
79
45
|
>
|
|
80
|
-
<
|
|
46
|
+
<Stack />
|
|
81
47
|
</SmartHiveProvider>
|
|
82
48
|
);
|
|
83
49
|
}
|
|
84
50
|
```
|
|
85
51
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
buildRedirectUri("myapp") // → "myapp://auth/callback"
|
|
92
|
-
buildRedirectUri("myapp", "auth/done") // → "myapp://auth/done"
|
|
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
|
|
93
57
|
```
|
|
94
58
|
|
|
95
59
|
---
|
|
96
60
|
|
|
97
|
-
##
|
|
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
|
|
61
|
+
## Headless Sign-in (Custom Login Screen)
|
|
117
62
|
|
|
118
|
-
No
|
|
63
|
+
No browser, no redirect. Call the method, get tokens. Full control of your UI.
|
|
119
64
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
## Hooks
|
|
123
|
-
|
|
124
|
-
### `useAuth()`
|
|
125
|
-
|
|
126
|
-
Returns the full auth context.
|
|
65
|
+
### Email + Password
|
|
127
66
|
|
|
128
67
|
```tsx
|
|
129
68
|
import { useAuth } from "@smarthivelabs-devs/auth-expo";
|
|
69
|
+
import { useState } from "react";
|
|
70
|
+
import { Button, TextInput, View, Text } from "react-native";
|
|
130
71
|
|
|
131
|
-
function
|
|
132
|
-
const {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
<
|
|
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>
|
|
148
94
|
);
|
|
149
95
|
}
|
|
150
96
|
```
|
|
151
97
|
|
|
152
|
-
|
|
98
|
+
### Phone OTP
|
|
153
99
|
|
|
154
|
-
|
|
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
|
+
}
|
|
155
115
|
|
|
156
|
-
|
|
116
|
+
async function verifyOtp() {
|
|
117
|
+
await signIn.phone.verify({ phoneNumber: phone, code });
|
|
118
|
+
// Signed in — session saved automatically
|
|
119
|
+
}
|
|
157
120
|
|
|
158
|
-
|
|
159
|
-
|
|
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
|
+
}
|
|
160
129
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
+
);
|
|
165
136
|
}
|
|
166
137
|
```
|
|
167
138
|
|
|
168
|
-
|
|
139
|
+
### Email OTP
|
|
169
140
|
|
|
170
|
-
|
|
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
|
+
```
|
|
171
150
|
|
|
172
|
-
|
|
151
|
+
### Magic Link
|
|
173
152
|
|
|
174
153
|
```tsx
|
|
175
|
-
|
|
154
|
+
const { signIn } = useAuth();
|
|
176
155
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (!user) return null;
|
|
180
|
-
return <Text>Welcome, {(user as any).email}</Text>;
|
|
181
|
-
}
|
|
156
|
+
// Sends an email — user clicks the link to sign in (no token returned here)
|
|
157
|
+
await signIn.magicLink.send({ email: "user@example.com" });
|
|
182
158
|
```
|
|
183
159
|
|
|
184
160
|
---
|
|
185
161
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
Returns `true` once the initial SecureStore session check is done. Prevents a flash of the signed-out state on app startup.
|
|
162
|
+
## Headless Sign-up
|
|
189
163
|
|
|
190
164
|
```tsx
|
|
191
|
-
import {
|
|
165
|
+
import { useAuth } from "@smarthivelabs-devs/auth-expo";
|
|
192
166
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
197
180
|
}
|
|
198
181
|
```
|
|
199
182
|
|
|
200
183
|
---
|
|
201
184
|
|
|
202
|
-
|
|
185
|
+
## OAuth Redirect Sign-in (Browser)
|
|
203
186
|
|
|
204
|
-
|
|
187
|
+
The original flow is completely unchanged. Use it for social login or when you want the hosted login page.
|
|
205
188
|
|
|
206
189
|
```tsx
|
|
207
|
-
import {
|
|
190
|
+
import { useAuth } from "@smarthivelabs-devs/auth-expo";
|
|
191
|
+
import { Button } from "react-native";
|
|
208
192
|
|
|
209
|
-
function
|
|
210
|
-
const
|
|
211
|
-
|
|
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)
|
|
212
200
|
|
|
213
|
-
|
|
214
|
-
if (isSignedIn === false) router.replace("/login");
|
|
215
|
-
}, [isSignedIn]);
|
|
201
|
+
If you use `login()`, register a custom scheme in `app.json` so the browser can redirect back:
|
|
216
202
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
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
|
+
}
|
|
220
218
|
}
|
|
221
219
|
```
|
|
222
220
|
|
|
223
|
-
|
|
221
|
+
Rebuild after changing `app.json`:
|
|
224
222
|
|
|
225
|
-
|
|
223
|
+
```bash
|
|
224
|
+
npx expo prebuild
|
|
225
|
+
```
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
No extra setup needed for headless sign-in — it works without a deep link.
|
|
228
228
|
|
|
229
|
-
|
|
230
|
-
import { useAuthFetch } from "@smarthivelabs-devs/auth-expo";
|
|
229
|
+
---
|
|
231
230
|
|
|
232
|
-
|
|
233
|
-
const authFetch = useAuthFetch();
|
|
234
|
-
const [data, setData] = useState(null);
|
|
231
|
+
## Sign-out
|
|
235
232
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
setData(await res.json());
|
|
239
|
-
}
|
|
233
|
+
```tsx
|
|
234
|
+
const { logout } = useAuth();
|
|
240
235
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
<Button title="Load data" onPress={load} />
|
|
244
|
-
{data && <Text>{JSON.stringify(data)}</Text>}
|
|
245
|
-
</View>
|
|
246
|
-
);
|
|
247
|
-
}
|
|
236
|
+
// Clears SecureStore + invalidates session on the server
|
|
237
|
+
await logout();
|
|
248
238
|
```
|
|
249
239
|
|
|
250
240
|
---
|
|
251
241
|
|
|
252
|
-
|
|
242
|
+
## Hooks
|
|
253
243
|
|
|
254
|
-
|
|
244
|
+
### `useAuth()`
|
|
245
|
+
|
|
246
|
+
Returns the full auth context.
|
|
255
247
|
|
|
256
248
|
```tsx
|
|
257
|
-
|
|
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
|
+
```
|
|
258
262
|
|
|
259
|
-
|
|
260
|
-
const getAuthorizationHeader = useAuthorizationHeader();
|
|
263
|
+
### `useSession()`
|
|
261
264
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
});
|
|
265
|
+
```tsx
|
|
266
|
+
const session = useSession(); // AuthSession | null
|
|
267
|
+
// session.accessToken, session.refreshToken, session.expiresAt, session.user
|
|
268
|
+
```
|
|
267
269
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
+
### `useUser()`
|
|
271
|
+
|
|
272
|
+
```tsx
|
|
273
|
+
const user = useUser(); // unknown | null
|
|
270
274
|
```
|
|
271
275
|
|
|
272
|
-
|
|
276
|
+
### `useIsLoaded()`
|
|
273
277
|
|
|
274
|
-
|
|
278
|
+
`true` once the initial SecureStore check is complete. Use this to avoid a flash of unauthenticated state on startup.
|
|
275
279
|
|
|
276
|
-
|
|
280
|
+
```tsx
|
|
281
|
+
const isLoaded = useIsLoaded();
|
|
282
|
+
if (!isLoaded) return <SplashScreen />;
|
|
283
|
+
```
|
|
277
284
|
|
|
278
|
-
|
|
285
|
+
### `useIsSignedIn()`
|
|
286
|
+
|
|
287
|
+
Returns `true` (signed in), `false` (signed out), or `null` (still loading).
|
|
279
288
|
|
|
280
289
|
```tsx
|
|
281
|
-
|
|
290
|
+
const isSignedIn = useIsSignedIn();
|
|
282
291
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
292
|
+
useEffect(() => {
|
|
293
|
+
if (isSignedIn === false) router.replace("/login");
|
|
294
|
+
}, [isSignedIn]);
|
|
286
295
|
```
|
|
287
296
|
|
|
288
|
-
###
|
|
297
|
+
### `useAuthFetch()`
|
|
289
298
|
|
|
290
|
-
|
|
299
|
+
Authenticated `fetch` wrapper. Bearer token is injected automatically and refreshed transparently when near expiry.
|
|
291
300
|
|
|
292
301
|
```tsx
|
|
293
|
-
|
|
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.
|
|
294
309
|
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
310
|
+
```tsx
|
|
311
|
+
const getAuthorizationHeader = useAuthorizationHeader();
|
|
312
|
+
const headers = await getAuthorizationHeader();
|
|
298
313
|
```
|
|
299
314
|
|
|
300
|
-
|
|
315
|
+
---
|
|
301
316
|
|
|
302
|
-
|
|
317
|
+
## Render Helpers
|
|
303
318
|
|
|
304
319
|
```tsx
|
|
305
|
-
import { AuthLoading } from "@smarthivelabs-devs/auth-expo";
|
|
320
|
+
import { SignedIn, SignedOut, AuthLoading } from "@smarthivelabs-devs/auth-expo";
|
|
306
321
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
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>
|
|
310
330
|
```
|
|
311
331
|
|
|
312
332
|
---
|
|
@@ -315,11 +335,11 @@ import { AuthLoading } from "@smarthivelabs-devs/auth-expo";
|
|
|
315
335
|
|
|
316
336
|
```
|
|
317
337
|
app/
|
|
318
|
-
├── _layout.tsx
|
|
319
|
-
├── index.tsx
|
|
320
|
-
├── login.tsx
|
|
338
|
+
├── _layout.tsx ← SmartHiveProvider here
|
|
339
|
+
├── index.tsx ← SignedIn / SignedOut routing
|
|
340
|
+
├── login.tsx ← your custom login screen using signIn.*
|
|
321
341
|
└── (protected)/
|
|
322
|
-
└── dashboard.tsx
|
|
342
|
+
└── dashboard.tsx
|
|
323
343
|
```
|
|
324
344
|
|
|
325
345
|
```tsx
|
|
@@ -359,85 +379,70 @@ export default function Index() {
|
|
|
359
379
|
```
|
|
360
380
|
|
|
361
381
|
```tsx
|
|
362
|
-
// app/login.tsx
|
|
382
|
+
// app/login.tsx — custom screen, no browser redirect
|
|
363
383
|
import { useAuth } from "@smarthivelabs-devs/auth-expo";
|
|
364
|
-
import {
|
|
384
|
+
import { useState } from "react";
|
|
385
|
+
import { Button, TextInput, View, Text, StyleSheet } from "react-native";
|
|
365
386
|
|
|
366
387
|
export default function LoginScreen() {
|
|
367
|
-
const {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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
|
+
}
|
|
383
405
|
|
|
384
406
|
return (
|
|
385
|
-
<
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
</
|
|
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>
|
|
391
413
|
);
|
|
392
414
|
}
|
|
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
415
|
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
redirectUri: buildRedirectUri("myapp"),
|
|
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 },
|
|
409
420
|
});
|
|
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
421
|
```
|
|
416
422
|
|
|
417
423
|
---
|
|
418
424
|
|
|
419
|
-
##
|
|
425
|
+
## Token Storage
|
|
420
426
|
|
|
421
|
-
|
|
427
|
+
All tokens are stored in `expo-secure-store`:
|
|
422
428
|
|
|
423
|
-
|
|
424
|
-
|
|
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
|
+
- **iOS**: Keychain Services
|
|
430
|
+
- **Android**: Android Keystore (AES encryption)
|
|
429
431
|
|
|
430
|
-
|
|
432
|
+
PKCE verifier and state (used during OAuth redirect flow) are also stored in SecureStore and deleted after the code exchange completes.
|
|
431
433
|
|
|
432
434
|
---
|
|
433
435
|
|
|
434
|
-
##
|
|
435
|
-
|
|
436
|
-
Tokens are stored in `expo-secure-store`, which uses:
|
|
437
|
-
- **iOS**: Keychain Services
|
|
438
|
-
- **Android**: Keystore System (Android Keystore-backed AES encryption)
|
|
436
|
+
## Provider Props
|
|
439
437
|
|
|
440
|
-
|
|
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 |
|
|
441
446
|
|
|
442
447
|
---
|
|
443
448
|
|
|
@@ -451,12 +456,40 @@ import type {
|
|
|
451
456
|
|
|
452
457
|
import type {
|
|
453
458
|
AuthSession,
|
|
459
|
+
HeadlessClient,
|
|
460
|
+
HeadlessSignInResult,
|
|
461
|
+
HeadlessSignUpResult,
|
|
454
462
|
SmartHiveAuthClient,
|
|
455
463
|
} from "@smarthivelabs-devs/auth-sdk";
|
|
456
464
|
```
|
|
457
465
|
|
|
458
466
|
---
|
|
459
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
|
+
|
|
460
493
|
## Related Packages
|
|
461
494
|
|
|
462
495
|
| Package | Use case |
|
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.
|
|
37
|
-
*
|
|
38
|
-
*
|
|
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": "1.
|
|
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": "^1.
|
|
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": "^1.
|
|
39
|
+
"@smarthivelabs-devs/auth-sdk": "^1.1.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsup",
|