@idealyst/oauth-client 0.0.1 → 1.0.70
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 +172 -182
- package/package.json +3 -9
- package/src/examples/simple-oauth-example.ts +60 -0
- package/src/index.native.ts +10 -0
- package/src/index.ts +8 -53
- package/src/index.web.ts +10 -0
- package/src/oauth-client.native.ts +130 -0
- package/src/oauth-client.web.ts +83 -0
- package/src/types.ts +8 -36
- package/src/examples/google-example.ts +0 -108
- package/src/native-client.ts +0 -159
- package/src/storage.ts +0 -60
- package/src/web-client.ts +0 -272
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @idealyst/oauth-client
|
|
2
2
|
|
|
3
|
-
Universal OAuth2 client for web and React Native applications with
|
|
3
|
+
Universal OAuth2 client for web and React Native applications with minimal server requirements.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
- 🌐 **Universal**: Works on both web and React Native with the same API
|
|
8
|
-
-
|
|
8
|
+
- 🖥️ **Minimal Server**: Server only needs to redirect - no token handling required
|
|
9
|
+
- 🔗 **Deep Link Support**: Handles mobile OAuth callbacks via custom URL schemes
|
|
10
|
+
- 🔐 **Secure**: Uses PKCE flow, client exchanges tokens directly with OAuth provider
|
|
9
11
|
- 🏪 **Storage**: Automatic token storage with customizable adapters
|
|
10
|
-
- 🔄 **Refresh**:
|
|
11
|
-
- 🚪 **Logout**: Proper logout with token revocation
|
|
12
|
-
- 📱 **Mobile**: Uses `react-native-app-auth` for secure system browser flow
|
|
12
|
+
- 🔄 **Refresh**: Direct token refresh with OAuth provider
|
|
13
13
|
- 🎯 **TypeScript**: Fully typed for better developer experience
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
@@ -24,8 +24,7 @@ yarn add @idealyst/oauth-client
|
|
|
24
24
|
|
|
25
25
|
#### For React Native:
|
|
26
26
|
```bash
|
|
27
|
-
npm install
|
|
28
|
-
# Follow react-native-app-auth setup instructions for iOS/Android
|
|
27
|
+
npm install @react-native-async-storage/async-storage
|
|
29
28
|
```
|
|
30
29
|
|
|
31
30
|
#### For Web:
|
|
@@ -34,47 +33,130 @@ No additional dependencies required.
|
|
|
34
33
|
## Quick Start
|
|
35
34
|
|
|
36
35
|
```typescript
|
|
37
|
-
import { createOAuthClient
|
|
36
|
+
import { createOAuthClient } from '@idealyst/oauth-client'
|
|
38
37
|
|
|
39
|
-
// Create OAuth client (works on both web and mobile)
|
|
40
38
|
const client = createOAuthClient({
|
|
41
|
-
|
|
39
|
+
apiBaseUrl: 'https://api.yourapp.com',
|
|
40
|
+
provider: 'google', // Your server endpoint: /auth/google (just redirects)
|
|
41
|
+
redirectUrl: 'com.yourapp://oauth/callback',
|
|
42
|
+
|
|
43
|
+
// OAuth provider config for direct token exchange
|
|
44
|
+
issuer: 'https://accounts.google.com',
|
|
42
45
|
clientId: 'your-google-client-id',
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
|
|
47
|
+
scopes: ['profile', 'email'],
|
|
45
48
|
})
|
|
46
49
|
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
// Works on both web and mobile!
|
|
51
|
+
const result = await client.authorize()
|
|
52
|
+
console.log('Access token:', result.tokens.accessToken)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## How It Works
|
|
56
|
+
|
|
57
|
+
This library uses a hybrid approach that minimizes server requirements:
|
|
58
|
+
|
|
59
|
+
### Web Flow:
|
|
60
|
+
1. Client redirects to `GET /api/auth/google?redirect_uri=com.yourapp://oauth/callback&state=xyz&code_challenge=abc`
|
|
61
|
+
2. Server redirects to Google OAuth with server's client credentials + client's PKCE challenge
|
|
62
|
+
3. Google redirects back to `com.yourapp://oauth/callback?code=123&state=xyz`
|
|
63
|
+
4. Client automatically detects callback and exchanges code directly with Google using PKCE
|
|
64
|
+
|
|
65
|
+
### Mobile Flow:
|
|
66
|
+
1. App opens browser to `GET /api/auth/google?redirect_uri=com.yourapp://oauth/callback&state=xyz&code_challenge=abc`
|
|
67
|
+
2. Server redirects to Google OAuth with server's client credentials + client's PKCE challenge
|
|
68
|
+
3. Google redirects back to `com.yourapp://oauth/callback?code=123&state=xyz`
|
|
69
|
+
4. Mobile OS opens app via deep link, client exchanges code directly with Google using PKCE
|
|
70
|
+
|
|
71
|
+
## Minimal Server Setup
|
|
72
|
+
|
|
73
|
+
Your server only needs **ONE simple endpoint**:
|
|
74
|
+
|
|
75
|
+
### GET `/auth/{provider}` - OAuth Redirect
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
app.get('/auth/:provider', (req, res) => {
|
|
79
|
+
const { redirect_uri, state, scope, code_challenge, code_challenge_method } = req.query
|
|
80
|
+
|
|
81
|
+
// Build OAuth URL with your server's credentials + client's PKCE
|
|
82
|
+
const authUrl = buildOAuthUrl(req.params.provider, {
|
|
83
|
+
client_id: process.env.GOOGLE_CLIENT_ID,
|
|
84
|
+
redirect_uri: redirect_uri, // Client's redirect URI
|
|
85
|
+
state: state, // Client's state for CSRF protection
|
|
86
|
+
scope: scope || 'profile email',
|
|
87
|
+
response_type: 'code',
|
|
88
|
+
code_challenge: code_challenge, // Client's PKCE challenge
|
|
89
|
+
code_challenge_method: code_challenge_method || 'S256',
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
res.redirect(authUrl)
|
|
93
|
+
})
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
That's it! No token exchange, no callbacks, no database - just a simple redirect.
|
|
97
|
+
|
|
98
|
+
## React Native Setup
|
|
99
|
+
|
|
100
|
+
### iOS Configuration
|
|
101
|
+
|
|
102
|
+
Add URL scheme to your `Info.plist`:
|
|
103
|
+
|
|
104
|
+
```xml
|
|
105
|
+
<key>CFBundleURLTypes</key>
|
|
106
|
+
<array>
|
|
107
|
+
<dict>
|
|
108
|
+
<key>CFBundleURLName</key>
|
|
109
|
+
<string>com.yourapp.oauth</string>
|
|
110
|
+
<key>CFBundleURLSchemes</key>
|
|
111
|
+
<array>
|
|
112
|
+
<string>com.yourapp</string>
|
|
113
|
+
</array>
|
|
114
|
+
</dict>
|
|
115
|
+
</array>
|
|
55
116
|
```
|
|
56
117
|
|
|
118
|
+
### Android Configuration
|
|
119
|
+
|
|
120
|
+
Add intent filter to `android/app/src/main/AndroidManifest.xml`:
|
|
121
|
+
|
|
122
|
+
```xml
|
|
123
|
+
<activity android:name=".MainActivity">
|
|
124
|
+
<intent-filter android:label="oauth_callback">
|
|
125
|
+
<action android:name="android.intent.action.VIEW" />
|
|
126
|
+
<category android:name="android.intent.category.DEFAULT" />
|
|
127
|
+
<category android:name="android.intent.category.BROWSABLE" />
|
|
128
|
+
<data android:scheme="com.yourapp" />
|
|
129
|
+
</intent-filter>
|
|
130
|
+
</activity>
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Deep Link Handling (Automatic)
|
|
134
|
+
|
|
135
|
+
The client automatically handles OAuth deep links. The deep link handler is built-in and requires no additional setup.
|
|
136
|
+
|
|
57
137
|
## API Reference
|
|
58
138
|
|
|
59
139
|
### createOAuthClient(config, storage?)
|
|
60
140
|
|
|
61
|
-
Creates a platform-specific OAuth client with a unified API.
|
|
62
|
-
|
|
63
141
|
```typescript
|
|
64
142
|
const client = createOAuthClient({
|
|
143
|
+
// Your server (just redirects)
|
|
144
|
+
apiBaseUrl: 'https://api.yourapp.com',
|
|
145
|
+
provider: 'google',
|
|
146
|
+
redirectUrl: 'com.yourapp://oauth/callback',
|
|
147
|
+
|
|
148
|
+
// OAuth provider config (for direct token exchange)
|
|
65
149
|
issuer: 'https://accounts.google.com',
|
|
66
|
-
clientId: 'your-client-id',
|
|
67
|
-
|
|
68
|
-
scopes: ['openid', 'profile', 'email'],
|
|
150
|
+
clientId: 'your-google-client-id',
|
|
151
|
+
tokenEndpoint: 'https://oauth2.googleapis.com/token', // Optional
|
|
69
152
|
|
|
70
153
|
// Optional
|
|
154
|
+
scopes: ['profile', 'email'],
|
|
71
155
|
additionalParameters: { prompt: 'consent' },
|
|
72
|
-
customHeaders: { '
|
|
156
|
+
customHeaders: { 'Authorization': 'Bearer api-key' },
|
|
73
157
|
})
|
|
74
158
|
```
|
|
75
159
|
|
|
76
|
-
**⚠️ Security**: Client secrets are **never** used in this library. All flows use PKCE for security, which is the OAuth 2.1 standard for public clients.
|
|
77
|
-
|
|
78
160
|
### OAuthClient Methods
|
|
79
161
|
|
|
80
162
|
#### authorize()
|
|
@@ -83,18 +165,17 @@ Initiates the OAuth flow and returns tokens.
|
|
|
83
165
|
```typescript
|
|
84
166
|
const result = await client.authorize()
|
|
85
167
|
// result.tokens: { accessToken, refreshToken, idToken, expiresAt, ... }
|
|
86
|
-
// result.user: Additional user data (provider-specific)
|
|
87
168
|
```
|
|
88
169
|
|
|
89
170
|
#### refresh(refreshToken)
|
|
90
|
-
Refreshes an expired access token.
|
|
171
|
+
Refreshes an expired access token directly with OAuth provider.
|
|
91
172
|
|
|
92
173
|
```typescript
|
|
93
174
|
const result = await client.refresh(refreshToken)
|
|
94
175
|
```
|
|
95
176
|
|
|
96
177
|
#### getStoredTokens()
|
|
97
|
-
Retrieves stored tokens from storage.
|
|
178
|
+
Retrieves stored tokens from local storage.
|
|
98
179
|
|
|
99
180
|
```typescript
|
|
100
181
|
const tokens = await client.getStoredTokens()
|
|
@@ -103,179 +184,80 @@ if (tokens?.expiresAt && tokens.expiresAt < new Date()) {
|
|
|
103
184
|
}
|
|
104
185
|
```
|
|
105
186
|
|
|
106
|
-
#### revoke(token)
|
|
107
|
-
Revokes a specific token.
|
|
108
|
-
|
|
109
|
-
```typescript
|
|
110
|
-
await client.revoke(accessToken)
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
#### logout()
|
|
114
|
-
Logs out the user, revokes tokens, and clears storage.
|
|
115
|
-
|
|
116
|
-
```typescript
|
|
117
|
-
await client.logout()
|
|
118
|
-
```
|
|
119
|
-
|
|
120
187
|
#### clearStoredTokens()
|
|
121
|
-
|
|
188
|
+
Clears stored tokens from local storage.
|
|
122
189
|
|
|
123
190
|
```typescript
|
|
124
191
|
await client.clearStoredTokens()
|
|
125
192
|
```
|
|
126
193
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
Pre-configured settings for popular OAuth providers:
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
import { providers } from '@idealyst/oauth-client'
|
|
133
|
-
|
|
134
|
-
// Google
|
|
135
|
-
const googleClient = createOAuthClient({
|
|
136
|
-
...providers.google,
|
|
137
|
-
clientId: 'your-google-client-id',
|
|
138
|
-
redirectUrl: 'your-redirect-url',
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
// GitHub
|
|
142
|
-
const githubClient = createOAuthClient({
|
|
143
|
-
...providers.github,
|
|
144
|
-
clientId: 'your-github-client-id',
|
|
145
|
-
redirectUrl: 'your-redirect-url',
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
// Microsoft
|
|
149
|
-
const msClient = createOAuthClient({
|
|
150
|
-
...providers.microsoft,
|
|
151
|
-
clientId: 'your-microsoft-client-id',
|
|
152
|
-
redirectUrl: 'your-redirect-url',
|
|
153
|
-
})
|
|
154
|
-
|
|
155
|
-
// Auth0
|
|
156
|
-
const auth0Client = createOAuthClient({
|
|
157
|
-
...providers.auth0('your-domain.auth0.com'),
|
|
158
|
-
clientId: 'your-auth0-client-id',
|
|
159
|
-
redirectUrl: 'your-redirect-url',
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
// Okta
|
|
163
|
-
const oktaClient = createOAuthClient({
|
|
164
|
-
...providers.okta('dev-123.okta.com'),
|
|
165
|
-
clientId: 'your-okta-client-id',
|
|
166
|
-
redirectUrl: 'your-redirect-url',
|
|
167
|
-
})
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
## Custom Storage
|
|
171
|
-
|
|
172
|
-
By default, the library uses `localStorage` on web and `AsyncStorage` on mobile. You can provide a custom storage adapter:
|
|
194
|
+
#### logout()
|
|
195
|
+
Clears stored tokens (server handles actual logout/revocation).
|
|
173
196
|
|
|
174
197
|
```typescript
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const customStorage = {
|
|
178
|
-
async getItem(key: string): Promise<string | null> {
|
|
179
|
-
// Your storage implementation
|
|
180
|
-
},
|
|
181
|
-
async setItem(key: string, value: string): Promise<void> {
|
|
182
|
-
// Your storage implementation
|
|
183
|
-
},
|
|
184
|
-
async removeItem(key: string): Promise<void> {
|
|
185
|
-
// Your storage implementation
|
|
186
|
-
},
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const client = createOAuthClient(config, customStorage)
|
|
198
|
+
await client.logout()
|
|
190
199
|
```
|
|
191
200
|
|
|
192
|
-
##
|
|
193
|
-
|
|
194
|
-
### Web Configuration
|
|
201
|
+
## Provider Examples
|
|
195
202
|
|
|
196
|
-
|
|
203
|
+
### Google OAuth
|
|
197
204
|
|
|
198
205
|
```typescript
|
|
199
|
-
const
|
|
200
|
-
|
|
206
|
+
const client = createOAuthClient({
|
|
207
|
+
apiBaseUrl: 'https://api.yourapp.com',
|
|
208
|
+
provider: 'google',
|
|
209
|
+
redirectUrl: 'com.yourapp://oauth/callback',
|
|
210
|
+
|
|
211
|
+
issuer: 'https://accounts.google.com',
|
|
201
212
|
clientId: 'your-google-client-id',
|
|
202
|
-
|
|
213
|
+
scopes: ['profile', 'email'],
|
|
203
214
|
})
|
|
204
215
|
```
|
|
205
216
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
### Mobile Configuration
|
|
209
|
-
|
|
210
|
-
For mobile apps, you need to:
|
|
211
|
-
|
|
212
|
-
1. **Configure custom URL scheme** in your app
|
|
213
|
-
2. **Register the URL scheme** with your OAuth provider
|
|
214
|
-
3. **Use the custom URL** as redirect URL
|
|
217
|
+
### GitHub OAuth
|
|
215
218
|
|
|
216
219
|
```typescript
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
+
const client = createOAuthClient({
|
|
221
|
+
apiBaseUrl: 'https://api.yourapp.com',
|
|
222
|
+
provider: 'github',
|
|
220
223
|
redirectUrl: 'com.yourapp://oauth/callback',
|
|
224
|
+
|
|
225
|
+
issuer: 'https://github.com',
|
|
226
|
+
clientId: 'your-github-client-id',
|
|
227
|
+
tokenEndpoint: 'https://github.com/login/oauth/access_token',
|
|
228
|
+
scopes: ['user', 'user:email'],
|
|
221
229
|
})
|
|
222
230
|
```
|
|
223
231
|
|
|
224
|
-
##
|
|
232
|
+
## Token Management
|
|
225
233
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
<
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
1. Add intent filter to `android/app/src/main/AndroidManifest.xml`:
|
|
234
|
+
```typescript
|
|
235
|
+
async function getValidTokens() {
|
|
236
|
+
const storedTokens = await client.getStoredTokens()
|
|
237
|
+
|
|
238
|
+
if (storedTokens) {
|
|
239
|
+
// Check if expired
|
|
240
|
+
if (storedTokens.expiresAt && storedTokens.expiresAt < new Date()) {
|
|
241
|
+
if (storedTokens.refreshToken) {
|
|
242
|
+
try {
|
|
243
|
+
const refreshed = await client.refresh(storedTokens.refreshToken)
|
|
244
|
+
return refreshed.tokens
|
|
245
|
+
} catch (error) {
|
|
246
|
+
// Refresh failed, need to re-authenticate
|
|
247
|
+
await client.clearStoredTokens()
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
return storedTokens
|
|
252
|
+
}
|
|
253
|
+
}
|
|
247
254
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<category android:name="android.intent.category.DEFAULT" />
|
|
253
|
-
<category android:name="android.intent.category.BROWSABLE" />
|
|
254
|
-
<data android:scheme="com.yourapp" />
|
|
255
|
-
</intent-filter>
|
|
256
|
-
</activity>
|
|
255
|
+
// No valid tokens, start OAuth flow
|
|
256
|
+
const result = await client.authorize()
|
|
257
|
+
return result.tokens
|
|
258
|
+
}
|
|
257
259
|
```
|
|
258
260
|
|
|
259
|
-
2. Follow the [react-native-app-auth setup guide](https://github.com/FormidableLabs/react-native-app-auth#setup) for additional configuration.
|
|
260
|
-
|
|
261
|
-
### OAuth Provider Setup
|
|
262
|
-
|
|
263
|
-
Register your custom URL scheme as a valid redirect URI in your OAuth provider:
|
|
264
|
-
|
|
265
|
-
- **Google**: Add `com.yourapp://oauth/callback` to "Authorized redirect URIs"
|
|
266
|
-
- **GitHub**: Set "Authorization callback URL" to `com.yourapp://oauth/callback`
|
|
267
|
-
- **Other providers**: Add the URL scheme to allowed callback URLs
|
|
268
|
-
|
|
269
|
-
## How Mobile OAuth Works
|
|
270
|
-
|
|
271
|
-
1. **App opens system browser** (Safari/Chrome) with OAuth URL
|
|
272
|
-
2. **User authenticates** in the browser (can use saved passwords, Touch ID, etc.)
|
|
273
|
-
3. **Provider redirects** to your custom URL scheme (`com.yourapp://oauth/callback`)
|
|
274
|
-
4. **OS recognizes the scheme** and opens your app
|
|
275
|
-
5. **react-native-app-auth** automatically extracts tokens and returns them
|
|
276
|
-
|
|
277
|
-
This provides the most secure OAuth flow for mobile apps, as recommended by OAuth 2.0 security best practices.
|
|
278
|
-
|
|
279
261
|
## Error Handling
|
|
280
262
|
|
|
281
263
|
```typescript
|
|
@@ -284,21 +266,29 @@ try {
|
|
|
284
266
|
} catch (error) {
|
|
285
267
|
if (error.message.includes('User cancelled')) {
|
|
286
268
|
// User cancelled the authorization
|
|
287
|
-
} else if (error.message.includes('
|
|
288
|
-
//
|
|
269
|
+
} else if (error.message.includes('Invalid state')) {
|
|
270
|
+
// CSRF protection triggered
|
|
271
|
+
} else if (error.message.includes('timeout')) {
|
|
272
|
+
// User didn't complete OAuth in time (mobile)
|
|
289
273
|
} else {
|
|
290
274
|
// Other OAuth error
|
|
291
275
|
}
|
|
292
276
|
}
|
|
293
277
|
```
|
|
294
278
|
|
|
295
|
-
##
|
|
279
|
+
## Security Benefits
|
|
296
280
|
|
|
297
|
-
|
|
281
|
+
✅ **No client secrets in client code** - Only client ID needed
|
|
282
|
+
✅ **PKCE protection** - Secure code exchange without client secrets
|
|
283
|
+
✅ **CSRF protection** - Uses state parameter
|
|
284
|
+
✅ **Direct token exchange** - Client communicates directly with OAuth provider
|
|
285
|
+
✅ **Minimal server attack surface** - Server only redirects, doesn't handle tokens
|
|
286
|
+
|
|
287
|
+
## TypeScript
|
|
298
288
|
|
|
299
289
|
```typescript
|
|
300
290
|
import type {
|
|
301
|
-
|
|
291
|
+
ServerOAuthConfig,
|
|
302
292
|
OAuthTokens,
|
|
303
293
|
OAuthResult,
|
|
304
294
|
OAuthClient
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@idealyst/oauth-client",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.70",
|
|
4
4
|
"description": "Universal OAuth2 client for web and React Native",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"author": "",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"
|
|
33
|
+
"@idealyst/storage": "1.0.70"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/react": "^18.3.18",
|
|
@@ -41,13 +41,7 @@
|
|
|
41
41
|
"typescript": "^5.7.3"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"
|
|
45
|
-
"react-native": ">=0.72"
|
|
46
|
-
},
|
|
47
|
-
"peerDependenciesMeta": {
|
|
48
|
-
"react-native": {
|
|
49
|
-
"optional": true
|
|
50
|
-
}
|
|
44
|
+
"@idealyst/storage": "1.0.70"
|
|
51
45
|
},
|
|
52
46
|
"files": [
|
|
53
47
|
"dist",
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Import will resolve to index.web.ts or index.native.ts based on platform
|
|
2
|
+
import { createOAuthClient } from '../index'
|
|
3
|
+
|
|
4
|
+
// Example: Simple OAuth flow (works on both web and mobile)
|
|
5
|
+
export async function setupGoogleOAuth() {
|
|
6
|
+
const client = createOAuthClient({
|
|
7
|
+
oauthUrl: 'https://api.yourapp.com/auth/google', // Your server endpoint
|
|
8
|
+
redirectUrl: 'com.yourapp://oauth/callback', // Same for web and mobile
|
|
9
|
+
additionalParameters: {
|
|
10
|
+
scope: 'profile email'
|
|
11
|
+
}
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
// Works on both web and mobile:
|
|
16
|
+
// 1. Redirects to your server endpoint
|
|
17
|
+
// 2. Server redirects to Google OAuth
|
|
18
|
+
// 3. Google redirects back to com.yourapp://oauth/callback?code=xyz
|
|
19
|
+
// 4. Client returns the code
|
|
20
|
+
const result = await client.authorize()
|
|
21
|
+
|
|
22
|
+
console.log('Authorization code:', result.code)
|
|
23
|
+
console.log('State:', result.state)
|
|
24
|
+
|
|
25
|
+
// Now you can exchange the code for tokens on your server
|
|
26
|
+
return result.code
|
|
27
|
+
} catch (error) {
|
|
28
|
+
console.error('OAuth error:', error)
|
|
29
|
+
throw error
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Example: GitHub OAuth
|
|
34
|
+
export async function setupGitHubOAuth() {
|
|
35
|
+
const client = createOAuthClient({
|
|
36
|
+
oauthUrl: 'https://api.yourapp.com/auth/github',
|
|
37
|
+
redirectUrl: 'com.yourapp://oauth/callback',
|
|
38
|
+
additionalParameters: {
|
|
39
|
+
scope: 'user user:email'
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const result = await client.authorize()
|
|
44
|
+
return result.code
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Example: Custom OAuth provider
|
|
48
|
+
export async function setupCustomOAuth() {
|
|
49
|
+
const client = createOAuthClient({
|
|
50
|
+
oauthUrl: 'https://api.yourapp.com/auth/custom-provider',
|
|
51
|
+
redirectUrl: 'com.yourapp://oauth/callback',
|
|
52
|
+
additionalParameters: {
|
|
53
|
+
scope: 'read write',
|
|
54
|
+
prompt: 'consent'
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const result = await client.authorize()
|
|
59
|
+
return result.code
|
|
60
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './types'
|
|
2
|
+
|
|
3
|
+
import type { OAuthConfig, OAuthClient } from './types'
|
|
4
|
+
import { NativeOAuthClient } from './oauth-client.native'
|
|
5
|
+
|
|
6
|
+
export function createOAuthClient(config: OAuthConfig): OAuthClient {
|
|
7
|
+
return new NativeOAuthClient(config)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { NativeOAuthClient }
|
package/src/index.ts
CHANGED
|
@@ -1,55 +1,10 @@
|
|
|
1
|
+
// This file should not be used - use index.web.ts or index.native.ts instead
|
|
1
2
|
export * from './types'
|
|
2
|
-
export * from './storage'
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
storage?: StorageAdapter
|
|
12
|
-
): OAuthClient {
|
|
13
|
-
const storageAdapter = storage || createDefaultStorage()
|
|
14
|
-
|
|
15
|
-
// Check if we're in React Native environment
|
|
16
|
-
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
17
|
-
return new NativeOAuthClient(config, storageAdapter)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
// Default to web client
|
|
21
|
-
return new WebOAuthClient(config, storageAdapter)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Common provider configurations
|
|
25
|
-
export const providers = {
|
|
26
|
-
google: {
|
|
27
|
-
issuer: 'https://accounts.google.com',
|
|
28
|
-
scopes: ['openid', 'profile', 'email'],
|
|
29
|
-
},
|
|
30
|
-
|
|
31
|
-
github: {
|
|
32
|
-
issuer: 'https://github.com',
|
|
33
|
-
authorizationEndpoint: 'https://github.com/login/oauth/authorize',
|
|
34
|
-
tokenEndpoint: 'https://github.com/login/oauth/access_token',
|
|
35
|
-
scopes: ['user'],
|
|
36
|
-
},
|
|
37
|
-
|
|
38
|
-
microsoft: {
|
|
39
|
-
issuer: 'https://login.microsoftonline.com/common/v2.0',
|
|
40
|
-
scopes: ['openid', 'profile', 'email'],
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
auth0: (domain: string) => ({
|
|
44
|
-
issuer: `https://${domain}`,
|
|
45
|
-
scopes: ['openid', 'profile', 'email'],
|
|
46
|
-
}),
|
|
47
|
-
|
|
48
|
-
okta: (domain: string) => ({
|
|
49
|
-
issuer: `https://${domain}`,
|
|
50
|
-
scopes: ['openid', 'profile', 'email'],
|
|
51
|
-
}),
|
|
52
|
-
} as const
|
|
53
|
-
|
|
54
|
-
export { WebOAuthClient } from './web-client'
|
|
55
|
-
export { NativeOAuthClient } from './native-client'
|
|
4
|
+
// Fallback export that throws an error
|
|
5
|
+
export function createOAuthClient(): never {
|
|
6
|
+
throw new Error(
|
|
7
|
+
'@idealyst/oauth-client: Platform-specific bundle not found. ' +
|
|
8
|
+
'Make sure your bundler is configured to use .web.ts or .native.ts files.'
|
|
9
|
+
)
|
|
10
|
+
}
|
package/src/index.web.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from './types'
|
|
2
|
+
|
|
3
|
+
import type { OAuthConfig, OAuthClient } from './types'
|
|
4
|
+
import { WebOAuthClient } from './oauth-client.web'
|
|
5
|
+
|
|
6
|
+
export function createOAuthClient(config: OAuthConfig): OAuthClient {
|
|
7
|
+
return new WebOAuthClient(config)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export { WebOAuthClient }
|