binoauth 0.0.11 → 0.0.12
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 +359 -165
- package/dist/core/src/admin/client.d.ts +203 -0
- package/dist/core/src/admin/client.d.ts.map +1 -0
- package/dist/core/src/admin/client.js +391 -0
- package/dist/core/src/admin/client.js.map +1 -0
- package/dist/core/src/admin/index.d.ts +6 -0
- package/dist/core/src/admin/index.d.ts.map +1 -0
- package/dist/core/src/admin/index.js +5 -0
- package/dist/core/src/admin/index.js.map +1 -0
- package/dist/core/src/admin/types.d.ts +412 -0
- package/dist/core/src/admin/types.d.ts.map +1 -0
- package/dist/core/src/admin/types.js +5 -0
- package/dist/core/src/admin/types.js.map +1 -0
- package/dist/core/src/auth/client.d.ts +330 -0
- package/dist/core/src/auth/client.d.ts.map +1 -0
- package/dist/core/src/auth/client.js +408 -0
- package/dist/core/src/auth/client.js.map +1 -0
- package/dist/core/src/auth/error.d.ts +113 -0
- package/dist/core/src/auth/error.d.ts.map +1 -0
- package/dist/core/src/auth/error.js +257 -0
- package/dist/core/src/auth/error.js.map +1 -0
- package/dist/core/src/auth/flows/base-flow.d.ts +98 -0
- package/dist/core/src/auth/flows/base-flow.d.ts.map +1 -0
- package/dist/core/src/auth/flows/base-flow.js +182 -0
- package/dist/core/src/auth/flows/base-flow.js.map +1 -0
- package/dist/core/src/auth/flows/magic-link.d.ts +175 -0
- package/dist/core/src/auth/flows/magic-link.d.ts.map +1 -0
- package/dist/core/src/auth/flows/magic-link.js +228 -0
- package/dist/core/src/auth/flows/magic-link.js.map +1 -0
- package/dist/core/src/auth/flows/mfa.d.ts +81 -0
- package/dist/core/src/auth/flows/mfa.d.ts.map +1 -0
- package/dist/core/src/auth/flows/mfa.js +103 -0
- package/dist/core/src/auth/flows/mfa.js.map +1 -0
- package/dist/core/src/auth/flows/otp.d.ts +172 -0
- package/dist/core/src/auth/flows/otp.d.ts.map +1 -0
- package/dist/core/src/auth/flows/otp.js +222 -0
- package/dist/core/src/auth/flows/otp.js.map +1 -0
- package/dist/core/src/auth/flows/password.d.ts +242 -0
- package/dist/core/src/auth/flows/password.d.ts.map +1 -0
- package/dist/core/src/auth/flows/password.js +344 -0
- package/dist/core/src/auth/flows/password.js.map +1 -0
- package/dist/core/src/auth/flows/social.d.ts +209 -0
- package/dist/core/src/auth/flows/social.d.ts.map +1 -0
- package/dist/core/src/auth/flows/social.js +284 -0
- package/dist/core/src/auth/flows/social.js.map +1 -0
- package/dist/core/src/auth/index.d.ts +19 -0
- package/dist/core/src/auth/index.d.ts.map +1 -0
- package/dist/core/src/auth/index.js +32 -0
- package/dist/core/src/auth/index.js.map +1 -0
- package/dist/core/src/auth/types.d.ts +151 -0
- package/dist/core/src/auth/types.d.ts.map +1 -0
- package/dist/core/src/auth/types.js +7 -0
- package/dist/core/src/auth/types.js.map +1 -0
- package/dist/core/src/index.d.ts +53 -49
- package/dist/core/src/index.d.ts.map +1 -1
- package/dist/core/src/index.js +61 -343
- package/dist/core/src/index.js.map +1 -1
- package/dist/core/src/oauth/client.d.ts +322 -0
- package/dist/core/src/oauth/client.d.ts.map +1 -0
- package/dist/core/src/oauth/client.js +491 -0
- package/dist/core/src/oauth/client.js.map +1 -0
- package/dist/core/src/oauth/error.d.ts +18 -0
- package/dist/core/src/oauth/error.d.ts.map +1 -0
- package/dist/core/src/oauth/error.js +24 -0
- package/dist/core/src/oauth/error.js.map +1 -0
- package/dist/core/src/oauth/flows/authorization-code.d.ts +122 -0
- package/dist/core/src/oauth/flows/authorization-code.d.ts.map +1 -0
- package/dist/core/src/oauth/flows/authorization-code.js +278 -0
- package/dist/core/src/oauth/flows/authorization-code.js.map +1 -0
- package/dist/core/src/oauth/flows/base-flow.d.ts +17 -0
- package/dist/core/src/oauth/flows/base-flow.d.ts.map +1 -0
- package/dist/core/src/oauth/flows/base-flow.js +107 -0
- package/dist/core/src/oauth/flows/base-flow.js.map +1 -0
- package/dist/core/src/oauth/flows/client-credentials.d.ts +72 -0
- package/dist/core/src/oauth/flows/client-credentials.d.ts.map +1 -0
- package/dist/core/src/oauth/flows/client-credentials.js +100 -0
- package/dist/core/src/oauth/flows/client-credentials.js.map +1 -0
- package/dist/core/src/oauth/flows/device-code.d.ts +108 -0
- package/dist/core/src/oauth/flows/device-code.d.ts.map +1 -0
- package/dist/core/src/oauth/flows/device-code.js +193 -0
- package/dist/core/src/oauth/flows/device-code.js.map +1 -0
- package/dist/core/src/oauth/flows/refresh-token.d.ts +59 -0
- package/dist/core/src/oauth/flows/refresh-token.d.ts.map +1 -0
- package/dist/core/src/oauth/flows/refresh-token.js +105 -0
- package/dist/core/src/oauth/flows/refresh-token.js.map +1 -0
- package/dist/core/src/oauth/index.d.ts +12 -0
- package/dist/core/src/oauth/index.d.ts.map +1 -0
- package/dist/core/src/oauth/index.js +11 -0
- package/dist/core/src/oauth/index.js.map +1 -0
- package/dist/core/src/oauth/storage/encryption.d.ts +12 -0
- package/dist/core/src/oauth/storage/encryption.d.ts.map +1 -0
- package/dist/core/src/oauth/storage/encryption.js +76 -0
- package/dist/core/src/oauth/storage/encryption.js.map +1 -0
- package/dist/core/src/oauth/storage/index.d.ts +201 -0
- package/dist/core/src/oauth/storage/index.d.ts.map +1 -0
- package/dist/core/src/oauth/storage/index.js +322 -0
- package/dist/core/src/oauth/storage/index.js.map +1 -0
- package/dist/core/src/oauth/storage/strategies.d.ts +34 -0
- package/dist/core/src/oauth/storage/strategies.d.ts.map +1 -0
- package/dist/core/src/oauth/storage/strategies.js +100 -0
- package/dist/core/src/oauth/storage/strategies.js.map +1 -0
- package/dist/core/src/oauth/types.d.ts +261 -0
- package/dist/core/src/oauth/types.d.ts.map +1 -0
- package/dist/core/src/oauth/types.js +39 -0
- package/dist/core/src/oauth/types.js.map +1 -0
- package/dist/core/src/oauth/utils.d.ts +56 -0
- package/dist/core/src/oauth/utils.d.ts.map +1 -0
- package/dist/core/src/oauth/utils.js +140 -0
- package/dist/core/src/oauth/utils.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
@@ -0,0 +1,491 @@
|
|
1
|
+
import { createAuthConfigFromIssuer } from "./types";
|
2
|
+
import { TokenStorage } from "./storage";
|
3
|
+
import { AuthorizationCodeFlow } from "./flows/authorization-code";
|
4
|
+
import { RefreshTokenFlow } from "./flows/refresh-token";
|
5
|
+
import { DeviceCodeFlow } from "./flows/device-code";
|
6
|
+
import { ClientCredentialsFlow } from "./flows/client-credentials";
|
7
|
+
import { isJWT } from "./utils";
|
8
|
+
import { AuthError, ErrorCode } from "./error";
|
9
|
+
import { Configuration } from "@binoauth/tenant-sdk";
|
10
|
+
/**
|
11
|
+
* BinoAuth OAuth 2.0 Client
|
12
|
+
*
|
13
|
+
* A comprehensive OAuth 2.0 client that supports multiple grant types
|
14
|
+
* including Authorization Code, Device Code, Client Credentials, and Refresh Token flows.
|
15
|
+
*
|
16
|
+
* Features:
|
17
|
+
* - Automatic token refresh and management
|
18
|
+
* - PKCE support for secure authentication
|
19
|
+
* - Device authorization for IoT and limited-input devices
|
20
|
+
* - Server-to-server authentication with client credentials
|
21
|
+
* - Secure token storage with encryption
|
22
|
+
* - Rate limiting and CSRF protection
|
23
|
+
*
|
24
|
+
* @example
|
25
|
+
* ```typescript
|
26
|
+
* import { BinoAuthOAuth, InMemoryTokenStorage } from 'binoauth';
|
27
|
+
*
|
28
|
+
* // Simple configuration using issuer
|
29
|
+
* const client = new BinoAuthOAuth({
|
30
|
+
* issuer: 'https://auth.binoauth.com',
|
31
|
+
* clientId: 'your_client_id',
|
32
|
+
* redirectUri: 'https://yourapp.com/callback',
|
33
|
+
* scope: 'openid profile email'
|
34
|
+
* }, {
|
35
|
+
* storage: new InMemoryTokenStorage(),
|
36
|
+
* clientId: 'your_client_id'
|
37
|
+
* });
|
38
|
+
*
|
39
|
+
* // Authorization Code Flow (for web/mobile apps)
|
40
|
+
* const loginUrl = await client.getLoginUrl();
|
41
|
+
* window.location.href = loginUrl;
|
42
|
+
*
|
43
|
+
* // Handle callback after user returns
|
44
|
+
* const urlParams = new URLSearchParams(window.location.search);
|
45
|
+
* await client.handleCallback(
|
46
|
+
* urlParams.get('code')!,
|
47
|
+
* urlParams.get('state')!
|
48
|
+
* );
|
49
|
+
*
|
50
|
+
* // Check authentication status
|
51
|
+
* const isLoggedIn = await client.isAuthenticated();
|
52
|
+
* if (isLoggedIn) {
|
53
|
+
* const userInfo = await client.getUserInfo();
|
54
|
+
* console.log('Logged in as:', userInfo.email);
|
55
|
+
* }
|
56
|
+
* ```
|
57
|
+
*
|
58
|
+
* @example
|
59
|
+
* ```typescript
|
60
|
+
* // Device Code Flow (for TVs, IoT devices, CLI tools)
|
61
|
+
* const deviceAuth = await client.requestDeviceCode();
|
62
|
+
* console.log(`Visit: ${deviceAuth.verification_uri}`);
|
63
|
+
* console.log(`Enter code: ${deviceAuth.user_code}`);
|
64
|
+
*
|
65
|
+
* // Poll for authorization
|
66
|
+
* const pollInterval = deviceAuth.interval * 1000;
|
67
|
+
* while (true) {
|
68
|
+
* try {
|
69
|
+
* await client.pollForToken(deviceAuth.device_code);
|
70
|
+
* console.log('Device authorized successfully!');
|
71
|
+
* break;
|
72
|
+
* } catch (error) {
|
73
|
+
* if (error.code === 'authorization_pending') {
|
74
|
+
* await new Promise(resolve => setTimeout(resolve, pollInterval));
|
75
|
+
* continue;
|
76
|
+
* }
|
77
|
+
* throw error;
|
78
|
+
* }
|
79
|
+
* }
|
80
|
+
* ```
|
81
|
+
*
|
82
|
+
* @example
|
83
|
+
* ```typescript
|
84
|
+
* // Client Credentials Flow (for server-to-server)
|
85
|
+
* client.initializeClientCredentials('your_client_secret');
|
86
|
+
* await client.getClientCredentialsTokens();
|
87
|
+
*
|
88
|
+
* // Use token for API calls
|
89
|
+
* const accessToken = await client.getAccessToken();
|
90
|
+
* const response = await fetch('/api/protected', {
|
91
|
+
* headers: { 'Authorization': `Bearer ${accessToken}` }
|
92
|
+
* });
|
93
|
+
* ```
|
94
|
+
*/
|
95
|
+
export class BinoAuthOAuth {
|
96
|
+
config;
|
97
|
+
storage;
|
98
|
+
oauthApi;
|
99
|
+
authCodeFlow;
|
100
|
+
refreshFlow;
|
101
|
+
deviceFlow;
|
102
|
+
clientCredentialsFlow;
|
103
|
+
/**
|
104
|
+
* Creates a new BinoAuth OAuth client
|
105
|
+
*
|
106
|
+
* @param config - OAuth configuration (can use simplified BinoAuthConfig with issuer)
|
107
|
+
* @param storageConfig - Token storage configuration
|
108
|
+
*
|
109
|
+
* @example
|
110
|
+
* ```typescript
|
111
|
+
* // Simple configuration with issuer
|
112
|
+
* const client = new BinoAuthOAuth({
|
113
|
+
* issuer: 'https://auth.binoauth.com',
|
114
|
+
* clientId: 'your_client_id',
|
115
|
+
* redirectUri: 'https://yourapp.com/callback'
|
116
|
+
* }, {
|
117
|
+
* storage: new InMemoryTokenStorage(),
|
118
|
+
* clientId: 'your_client_id'
|
119
|
+
* });
|
120
|
+
*
|
121
|
+
* // Full configuration
|
122
|
+
* const client = new BinoAuthOAuth({
|
123
|
+
* clientId: 'your_client_id',
|
124
|
+
* redirectUri: 'https://yourapp.com/callback',
|
125
|
+
* authorizeEndpoint: 'https://auth.binoauth.com/api/v1/oauth/authorize',
|
126
|
+
* tokenEndpoint: 'https://auth.binoauth.com/api/v1/oauth/token',
|
127
|
+
* userInfoEndpoint: 'https://auth.binoauth.com/api/v1/oauth/userinfo'
|
128
|
+
* }, storageConfig);
|
129
|
+
* ```
|
130
|
+
*/
|
131
|
+
constructor(config, storageConfig) {
|
132
|
+
// Convert BinoAuthConfig to AuthConfig if needed
|
133
|
+
this.config = 'issuer' in config ? createAuthConfigFromIssuer(config) : config;
|
134
|
+
this.storage = new TokenStorage({
|
135
|
+
...storageConfig,
|
136
|
+
clientId: storageConfig.clientId || this.config.clientId,
|
137
|
+
});
|
138
|
+
// Create tenant-SDK OAuth API if we have issuer-based config
|
139
|
+
if ('issuer' in config) {
|
140
|
+
try {
|
141
|
+
const { OAuth2Api } = require("@binoauth/tenant-sdk");
|
142
|
+
const tenantConfig = new Configuration({
|
143
|
+
basePath: config.issuer,
|
144
|
+
});
|
145
|
+
this.oauthApi = new OAuth2Api(tenantConfig);
|
146
|
+
}
|
147
|
+
catch (error) {
|
148
|
+
console.warn("Failed to initialize tenant-SDK OAuth API:", error);
|
149
|
+
}
|
150
|
+
}
|
151
|
+
this.authCodeFlow = new AuthorizationCodeFlow(this.config, this.storage, this.oauthApi);
|
152
|
+
this.refreshFlow = new RefreshTokenFlow(this.config, this.storage, this.oauthApi);
|
153
|
+
this.deviceFlow = new DeviceCodeFlow(this.config, this.storage, this.oauthApi);
|
154
|
+
}
|
155
|
+
/**
|
156
|
+
* Initializes client credentials flow for server-to-server authentication
|
157
|
+
*
|
158
|
+
* @param clientSecret - The client secret for authentication
|
159
|
+
*
|
160
|
+
* @example
|
161
|
+
* ```typescript
|
162
|
+
* client.initializeClientCredentials('your_client_secret');
|
163
|
+
* await client.getClientCredentialsTokens();
|
164
|
+
* ```
|
165
|
+
*/
|
166
|
+
initializeClientCredentials(clientSecret) {
|
167
|
+
this.clientCredentialsFlow = new ClientCredentialsFlow({ ...this.config, clientSecret }, this.storage, this.oauthApi);
|
168
|
+
}
|
169
|
+
/**
|
170
|
+
* Updates the token storage instance
|
171
|
+
*
|
172
|
+
* @param tokenStorage - New token storage instance
|
173
|
+
*/
|
174
|
+
setTokenStorage(tokenStorage) {
|
175
|
+
this.storage = tokenStorage;
|
176
|
+
}
|
177
|
+
/**
|
178
|
+
* Gets the current token storage instance
|
179
|
+
*
|
180
|
+
* @returns The current token storage
|
181
|
+
*/
|
182
|
+
getTokenStorage() {
|
183
|
+
return this.storage;
|
184
|
+
}
|
185
|
+
// Authorization Code Flow methods
|
186
|
+
/**
|
187
|
+
* Generates the authorization URL for the OAuth flow
|
188
|
+
*
|
189
|
+
* @returns Promise resolving to the authorization URL
|
190
|
+
*
|
191
|
+
* @example
|
192
|
+
* ```typescript
|
193
|
+
* const loginUrl = await client.getLoginUrl();
|
194
|
+
* window.location.href = loginUrl; // Redirect user to BinoAuth
|
195
|
+
* ```
|
196
|
+
*/
|
197
|
+
async getLoginUrl() {
|
198
|
+
return this.authCodeFlow.getLoginUrl();
|
199
|
+
}
|
200
|
+
/**
|
201
|
+
* Handles the OAuth callback and exchanges code for tokens
|
202
|
+
*
|
203
|
+
* @param code - Authorization code from callback
|
204
|
+
* @param state - State parameter from callback
|
205
|
+
*
|
206
|
+
* @example
|
207
|
+
* ```typescript
|
208
|
+
* const urlParams = new URLSearchParams(window.location.search);
|
209
|
+
* await client.handleCallback(
|
210
|
+
* urlParams.get('code')!,
|
211
|
+
* urlParams.get('state')!
|
212
|
+
* );
|
213
|
+
* ```
|
214
|
+
*/
|
215
|
+
async handleCallback(code, state) {
|
216
|
+
return this.authCodeFlow.handleCallback(code, state);
|
217
|
+
}
|
218
|
+
/**
|
219
|
+
* Generates the logout URL
|
220
|
+
*
|
221
|
+
* @returns Promise resolving to the logout URL
|
222
|
+
*/
|
223
|
+
async getLogoutUrl() {
|
224
|
+
return this.authCodeFlow.getLogoutUrl();
|
225
|
+
}
|
226
|
+
/**
|
227
|
+
* Generates the logout page URL with optional return URL
|
228
|
+
*
|
229
|
+
* @param returnTo - Optional URL to redirect to after logout
|
230
|
+
* @returns Promise resolving to the logout page URL
|
231
|
+
*/
|
232
|
+
async getLogoutPageUrl(returnTo) {
|
233
|
+
return this.authCodeFlow.getLogoutPageUrl(returnTo);
|
234
|
+
}
|
235
|
+
// Device Code Flow methods
|
236
|
+
/**
|
237
|
+
* Initiates device authorization flow
|
238
|
+
*
|
239
|
+
* @returns Promise resolving to device authorization response
|
240
|
+
*
|
241
|
+
* @example
|
242
|
+
* ```typescript
|
243
|
+
* const deviceAuth = await client.requestDeviceCode();
|
244
|
+
* console.log(`Visit: ${deviceAuth.verification_uri}`);
|
245
|
+
* console.log(`Enter code: ${deviceAuth.user_code}`);
|
246
|
+
* ```
|
247
|
+
*/
|
248
|
+
async requestDeviceCode() {
|
249
|
+
return this.deviceFlow.requestDeviceCode();
|
250
|
+
}
|
251
|
+
/**
|
252
|
+
* Polls for token using device code
|
253
|
+
*
|
254
|
+
* @param deviceCode - Device code from requestDeviceCode()
|
255
|
+
*
|
256
|
+
* @example
|
257
|
+
* ```typescript
|
258
|
+
* try {
|
259
|
+
* await client.pollForToken(deviceAuth.device_code);
|
260
|
+
* console.log('Device authorized!');
|
261
|
+
* } catch (error) {
|
262
|
+
* if (error.code === 'authorization_pending') {
|
263
|
+
* // User hasn't entered code yet, continue polling
|
264
|
+
* }
|
265
|
+
* }
|
266
|
+
* ```
|
267
|
+
*/
|
268
|
+
async pollForToken(deviceCode) {
|
269
|
+
return this.deviceFlow.pollForToken(deviceCode);
|
270
|
+
}
|
271
|
+
// Client Credentials Flow methods
|
272
|
+
/**
|
273
|
+
* Requests tokens using client credentials flow
|
274
|
+
*
|
275
|
+
* @example
|
276
|
+
* ```typescript
|
277
|
+
* client.initializeClientCredentials('your_client_secret');
|
278
|
+
* await client.getClientCredentialsTokens();
|
279
|
+
*
|
280
|
+
* const accessToken = await client.getAccessToken();
|
281
|
+
* // Use token for server-to-server API calls
|
282
|
+
* ```
|
283
|
+
*
|
284
|
+
* @throws {AuthError} When client credentials flow is not initialized
|
285
|
+
*/
|
286
|
+
async getClientCredentialsTokens() {
|
287
|
+
if (!this.clientCredentialsFlow) {
|
288
|
+
throw new AuthError("Client credentials flow not initialized. Call initializeClientCredentials() first.", ErrorCode.InvalidConfig);
|
289
|
+
}
|
290
|
+
return this.clientCredentialsFlow.getTokens();
|
291
|
+
}
|
292
|
+
// Token management
|
293
|
+
/**
|
294
|
+
* Gets a valid access token, automatically refreshing if needed
|
295
|
+
*
|
296
|
+
* @returns Promise resolving to access token or null if not authenticated
|
297
|
+
*
|
298
|
+
* @example
|
299
|
+
* ```typescript
|
300
|
+
* const accessToken = await client.getAccessToken();
|
301
|
+
* if (accessToken) {
|
302
|
+
* const response = await fetch('/api/protected', {
|
303
|
+
* headers: { 'Authorization': `Bearer ${accessToken}` }
|
304
|
+
* });
|
305
|
+
* }
|
306
|
+
* ```
|
307
|
+
*/
|
308
|
+
async getAccessToken() {
|
309
|
+
const token = await this.storage.getAccessToken();
|
310
|
+
if (!token || this.storage.isTokenExpired(token)) {
|
311
|
+
const refreshToken = await this.storage.getRefreshToken();
|
312
|
+
if (refreshToken && !this.storage.isTokenExpired(refreshToken)) {
|
313
|
+
try {
|
314
|
+
await this.refreshFlow.refreshTokens();
|
315
|
+
const newToken = await this.storage.getAccessToken();
|
316
|
+
return newToken?.value || null;
|
317
|
+
}
|
318
|
+
catch {
|
319
|
+
return null;
|
320
|
+
}
|
321
|
+
}
|
322
|
+
return null;
|
323
|
+
}
|
324
|
+
return token.value;
|
325
|
+
}
|
326
|
+
/**
|
327
|
+
* Manually refreshes access tokens using refresh token
|
328
|
+
*
|
329
|
+
* @example
|
330
|
+
* ```typescript
|
331
|
+
* try {
|
332
|
+
* await client.refreshTokens();
|
333
|
+
* console.log('Tokens refreshed successfully');
|
334
|
+
* } catch (error) {
|
335
|
+
* console.log('Token refresh failed:', error.message);
|
336
|
+
* }
|
337
|
+
* ```
|
338
|
+
*/
|
339
|
+
async refreshTokens() {
|
340
|
+
return this.refreshFlow.refreshTokens();
|
341
|
+
}
|
342
|
+
// User information
|
343
|
+
/**
|
344
|
+
* Fetches authenticated user information
|
345
|
+
*
|
346
|
+
* @returns Promise resolving to user info or null if not authenticated
|
347
|
+
*
|
348
|
+
* @example
|
349
|
+
* ```typescript
|
350
|
+
* const userInfo = await client.getUserInfo();
|
351
|
+
* if (userInfo) {
|
352
|
+
* console.log(`Welcome, ${userInfo.name}!`);
|
353
|
+
* console.log(`Email: ${userInfo.email}`);
|
354
|
+
* }
|
355
|
+
* ```
|
356
|
+
*
|
357
|
+
* @throws {AuthError} When userInfoEndpoint is missing from config
|
358
|
+
*/
|
359
|
+
async getUserInfo() {
|
360
|
+
if (!this.config.userInfoEndpoint) {
|
361
|
+
throw new AuthError("Missing userInfoEndpoint in config", ErrorCode.InvalidConfig);
|
362
|
+
}
|
363
|
+
let token = await this.storage.getAccessToken();
|
364
|
+
if (!token || this.storage.isTokenExpired(token)) {
|
365
|
+
const refreshToken = await this.storage.getRefreshToken();
|
366
|
+
if (refreshToken && !this.storage.isTokenExpired(refreshToken)) {
|
367
|
+
try {
|
368
|
+
await this.refreshFlow.refreshTokens();
|
369
|
+
const newToken = await this.storage.getAccessToken();
|
370
|
+
if (!newToken || this.storage.isTokenExpired(newToken)) {
|
371
|
+
return null;
|
372
|
+
}
|
373
|
+
token = newToken;
|
374
|
+
}
|
375
|
+
catch (e) {
|
376
|
+
return null;
|
377
|
+
}
|
378
|
+
}
|
379
|
+
else {
|
380
|
+
return null;
|
381
|
+
}
|
382
|
+
}
|
383
|
+
if (!isJWT(token.value)) {
|
384
|
+
return null;
|
385
|
+
}
|
386
|
+
const response = await fetch(this.config.userInfoEndpoint, {
|
387
|
+
headers: {
|
388
|
+
Authorization: `Bearer ${token.value}`,
|
389
|
+
},
|
390
|
+
credentials: "include",
|
391
|
+
});
|
392
|
+
if (response.ok)
|
393
|
+
return response.json();
|
394
|
+
return null;
|
395
|
+
}
|
396
|
+
// Authentication status
|
397
|
+
/**
|
398
|
+
* Checks if the user is currently authenticated
|
399
|
+
*
|
400
|
+
* Automatically attempts token refresh if access token is expired
|
401
|
+
* but refresh token is still valid.
|
402
|
+
*
|
403
|
+
* @returns Promise resolving to true if authenticated, false otherwise
|
404
|
+
*
|
405
|
+
* @example
|
406
|
+
* ```typescript
|
407
|
+
* if (await client.isAuthenticated()) {
|
408
|
+
* // User is logged in, show authenticated content
|
409
|
+
* const userInfo = await client.getUserInfo();
|
410
|
+
* } else {
|
411
|
+
* // User needs to log in
|
412
|
+
* const loginUrl = await client.getLoginUrl();
|
413
|
+
* window.location.href = loginUrl;
|
414
|
+
* }
|
415
|
+
* ```
|
416
|
+
*/
|
417
|
+
async isAuthenticated() {
|
418
|
+
const token = await this.storage.getAccessToken();
|
419
|
+
if (!token || this.storage.isTokenExpired(token) || !isJWT(token.value)) {
|
420
|
+
const refreshToken = await this.storage.getRefreshToken();
|
421
|
+
if (refreshToken && !this.storage.isTokenExpired(refreshToken)) {
|
422
|
+
if (!isJWT(refreshToken.value)) {
|
423
|
+
return false;
|
424
|
+
}
|
425
|
+
try {
|
426
|
+
await this.refreshFlow.refreshTokens();
|
427
|
+
return true;
|
428
|
+
}
|
429
|
+
catch {
|
430
|
+
return false;
|
431
|
+
}
|
432
|
+
}
|
433
|
+
return false;
|
434
|
+
}
|
435
|
+
return true;
|
436
|
+
}
|
437
|
+
// Logout
|
438
|
+
/**
|
439
|
+
* Logs out the user and revokes tokens
|
440
|
+
*
|
441
|
+
* Attempts to revoke tokens on the server if revoke endpoint is configured,
|
442
|
+
* then clears local token storage.
|
443
|
+
*
|
444
|
+
* @example
|
445
|
+
* ```typescript
|
446
|
+
* await client.logout();
|
447
|
+
* console.log('User logged out successfully');
|
448
|
+
*
|
449
|
+
* // Redirect to login page
|
450
|
+
* const loginUrl = await client.getLoginUrl();
|
451
|
+
* window.location.href = loginUrl;
|
452
|
+
* ```
|
453
|
+
*/
|
454
|
+
async logout() {
|
455
|
+
if (!this.config.revokeEndpoint) {
|
456
|
+
this.storage.clearTokens();
|
457
|
+
return;
|
458
|
+
}
|
459
|
+
const accessToken = await this.storage.getAccessToken();
|
460
|
+
const refreshToken = await this.storage.getRefreshToken();
|
461
|
+
const accessTokenValue = accessToken?.value;
|
462
|
+
const refreshTokenValue = refreshToken?.value;
|
463
|
+
try {
|
464
|
+
if (accessTokenValue && isJWT(accessTokenValue)) {
|
465
|
+
const body = JSON.stringify({
|
466
|
+
client_id: this.config.clientId,
|
467
|
+
access_token: accessTokenValue,
|
468
|
+
refresh_token: refreshTokenValue,
|
469
|
+
});
|
470
|
+
const response = await fetch(this.config.revokeEndpoint, {
|
471
|
+
method: "POST",
|
472
|
+
headers: {
|
473
|
+
"Content-Type": "application/json",
|
474
|
+
Authorization: `Bearer ${accessTokenValue}`,
|
475
|
+
},
|
476
|
+
body,
|
477
|
+
});
|
478
|
+
if (!response.ok) {
|
479
|
+
console.debug("Token revocation returned non-OK response:", await response.text());
|
480
|
+
}
|
481
|
+
}
|
482
|
+
}
|
483
|
+
catch (e) {
|
484
|
+
console.debug("Failed to revoke tokens:", e);
|
485
|
+
}
|
486
|
+
finally {
|
487
|
+
this.storage.clearTokens();
|
488
|
+
}
|
489
|
+
}
|
490
|
+
}
|
491
|
+
//# sourceMappingURL=client.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../../../src/oauth/client.ts"],"names":[],"mappings":"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"}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export declare enum ErrorCode {
|
2
|
+
InvalidConfig = "invalid_config",
|
3
|
+
InvalidState = "invalid_state",
|
4
|
+
MissingVerifier = "missing_verifier",
|
5
|
+
TokenExchangeFailed = "token_exchange_failed",
|
6
|
+
TokenRefreshFailed = "token_refresh_failed",
|
7
|
+
MissingRefreshToken = "missing_refresh_token",
|
8
|
+
RateLimitExceeded = "rate_limit_exceeded",
|
9
|
+
InvalidToken = "invalid_token",
|
10
|
+
NetworkError = "network_error",
|
11
|
+
UnknownError = "unknown_error"
|
12
|
+
}
|
13
|
+
export declare class AuthError extends Error {
|
14
|
+
code: ErrorCode;
|
15
|
+
description?: string;
|
16
|
+
constructor(message: string, code: ErrorCode, description?: string);
|
17
|
+
}
|
18
|
+
//# sourceMappingURL=error.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../../../src/oauth/error.ts"],"names":[],"mappings":"AAAA,oBAAY,SAAS;IACnB,aAAa,mBAAmB;IAChC,YAAY,kBAAkB;IAC9B,eAAe,qBAAqB;IACpC,mBAAmB,0BAA0B;IAC7C,kBAAkB,yBAAyB;IAC3C,mBAAmB,0BAA0B;IAC7C,iBAAiB,wBAAwB;IACzC,YAAY,kBAAkB;IAC9B,YAAY,kBAAkB;IAC9B,YAAY,kBAAkB;CAC/B;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,IAAI,EAAE,SAAS,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;gBAET,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,EAAE,MAAM;CAMnE"}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
export var ErrorCode;
|
2
|
+
(function (ErrorCode) {
|
3
|
+
ErrorCode["InvalidConfig"] = "invalid_config";
|
4
|
+
ErrorCode["InvalidState"] = "invalid_state";
|
5
|
+
ErrorCode["MissingVerifier"] = "missing_verifier";
|
6
|
+
ErrorCode["TokenExchangeFailed"] = "token_exchange_failed";
|
7
|
+
ErrorCode["TokenRefreshFailed"] = "token_refresh_failed";
|
8
|
+
ErrorCode["MissingRefreshToken"] = "missing_refresh_token";
|
9
|
+
ErrorCode["RateLimitExceeded"] = "rate_limit_exceeded";
|
10
|
+
ErrorCode["InvalidToken"] = "invalid_token";
|
11
|
+
ErrorCode["NetworkError"] = "network_error";
|
12
|
+
ErrorCode["UnknownError"] = "unknown_error";
|
13
|
+
})(ErrorCode || (ErrorCode = {}));
|
14
|
+
export class AuthError extends Error {
|
15
|
+
code;
|
16
|
+
description;
|
17
|
+
constructor(message, code, description) {
|
18
|
+
super(message);
|
19
|
+
this.name = "AuthError";
|
20
|
+
this.code = code;
|
21
|
+
this.description = description;
|
22
|
+
}
|
23
|
+
}
|
24
|
+
//# sourceMappingURL=error.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"error.js","sourceRoot":"","sources":["../../../../src/oauth/error.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,SAWX;AAXD,WAAY,SAAS;IACnB,6CAAgC,CAAA;IAChC,2CAA8B,CAAA;IAC9B,iDAAoC,CAAA;IACpC,0DAA6C,CAAA;IAC7C,wDAA2C,CAAA;IAC3C,0DAA6C,CAAA;IAC7C,sDAAyC,CAAA;IACzC,2CAA8B,CAAA;IAC9B,2CAA8B,CAAA;IAC9B,2CAA8B,CAAA;AAChC,CAAC,EAXW,SAAS,KAAT,SAAS,QAWpB;AAED,MAAM,OAAO,SAAU,SAAQ,KAAK;IAClC,IAAI,CAAY;IAChB,WAAW,CAAU;IAErB,YAAY,OAAe,EAAE,IAAe,EAAE,WAAoB;QAChE,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF"}
|
@@ -0,0 +1,122 @@
|
|
1
|
+
import type { AuthConfig } from "../types";
|
2
|
+
import { TokenStorage } from "../storage";
|
3
|
+
import { BaseFlow } from "./base-flow";
|
4
|
+
import type { OAuth2Api } from "@binoauth/tenant-sdk";
|
5
|
+
/**
|
6
|
+
* OAuth 2.0 Authorization Code Flow with PKCE support
|
7
|
+
*
|
8
|
+
* This flow is designed for browser-based applications (SPAs) and mobile apps
|
9
|
+
* where the client cannot securely store a client secret. It uses PKCE
|
10
|
+
* (Proof Key for Code Exchange) for enhanced security.
|
11
|
+
*
|
12
|
+
* @example
|
13
|
+
* ```typescript
|
14
|
+
* const flow = new AuthorizationCodeFlow(config, storage, oauthApi);
|
15
|
+
*
|
16
|
+
* // Step 1: Get authorization URL
|
17
|
+
* const loginUrl = await flow.getLoginUrl();
|
18
|
+
* window.location.href = loginUrl; // Redirect user to BinoAuth
|
19
|
+
*
|
20
|
+
* // Step 2: Handle callback (after user returns from BinoAuth)
|
21
|
+
* const urlParams = new URLSearchParams(window.location.search);
|
22
|
+
* const code = urlParams.get('code');
|
23
|
+
* const state = urlParams.get('state');
|
24
|
+
*
|
25
|
+
* if (code && state) {
|
26
|
+
* await flow.handleCallback(code, state);
|
27
|
+
* // User is now authenticated, tokens are stored
|
28
|
+
* }
|
29
|
+
* ```
|
30
|
+
*/
|
31
|
+
export declare class AuthorizationCodeFlow extends BaseFlow {
|
32
|
+
private loginUrlGenerationInProgress;
|
33
|
+
constructor(config: AuthConfig, storage: TokenStorage, oauthApi?: OAuth2Api);
|
34
|
+
/**
|
35
|
+
* Generates the authorization URL for initiating the OAuth flow
|
36
|
+
*
|
37
|
+
* This method creates a secure authorization URL with PKCE parameters
|
38
|
+
* and stores the necessary state and verifier tokens for later validation.
|
39
|
+
*
|
40
|
+
* @returns Promise resolving to the authorization URL
|
41
|
+
*
|
42
|
+
* @example
|
43
|
+
* ```typescript
|
44
|
+
* const loginUrl = await flow.getLoginUrl();
|
45
|
+
* // Returns: "https://auth.binoauth.com/api/v1/oauth/authorize?response_type=code&client_id=..."
|
46
|
+
*
|
47
|
+
* // Redirect user to the authorization server
|
48
|
+
* window.location.href = loginUrl;
|
49
|
+
* ```
|
50
|
+
*
|
51
|
+
* @throws {AuthError} When authorization endpoint is missing from config
|
52
|
+
*/
|
53
|
+
getLoginUrl(): Promise<string>;
|
54
|
+
/**
|
55
|
+
* Handles the OAuth callback and exchanges the authorization code for tokens
|
56
|
+
*
|
57
|
+
* This method validates the state parameter for CSRF protection, then exchanges
|
58
|
+
* the authorization code for access and refresh tokens using PKCE verification.
|
59
|
+
*
|
60
|
+
* @param code - The authorization code returned from the authorization server
|
61
|
+
* @param state - The state parameter returned from the authorization server
|
62
|
+
*
|
63
|
+
* @example
|
64
|
+
* ```typescript
|
65
|
+
* // Extract parameters from callback URL
|
66
|
+
* const urlParams = new URLSearchParams(window.location.search);
|
67
|
+
* const code = urlParams.get('code')!;
|
68
|
+
* const state = urlParams.get('state')!;
|
69
|
+
*
|
70
|
+
* // Exchange code for tokens
|
71
|
+
* await flow.handleCallback(code, state);
|
72
|
+
*
|
73
|
+
* // Tokens are now stored and user is authenticated
|
74
|
+
* const accessToken = await storage.getAccessToken();
|
75
|
+
* ```
|
76
|
+
*
|
77
|
+
* @throws {AuthError} When state validation fails (CSRF protection)
|
78
|
+
* @throws {AuthError} When state or verifier tokens are missing/expired
|
79
|
+
* @throws {AuthError} When token exchange fails
|
80
|
+
*/
|
81
|
+
handleCallback(code: string, state: string): Promise<void>;
|
82
|
+
/**
|
83
|
+
* Generates the logout URL for ending the OAuth session
|
84
|
+
*
|
85
|
+
* @returns Promise resolving to the logout URL
|
86
|
+
*
|
87
|
+
* @example
|
88
|
+
* ```typescript
|
89
|
+
* const logoutUrl = await flow.getLogoutUrl();
|
90
|
+
* // Returns: "https://auth.binoauth.com/api/v1/oauth/logout?client_id=..."
|
91
|
+
*
|
92
|
+
* window.location.href = logoutUrl; // Redirect to logout
|
93
|
+
* ```
|
94
|
+
*
|
95
|
+
* @throws {AuthError} When logout endpoint is missing from config
|
96
|
+
*/
|
97
|
+
getLogoutUrl(): Promise<string>;
|
98
|
+
/**
|
99
|
+
* Generates the logout page URL with optional return URL
|
100
|
+
*
|
101
|
+
* This URL points to the BinoAuth logout page where the user can
|
102
|
+
* terminate their session and optionally be redirected back.
|
103
|
+
*
|
104
|
+
* @param returnTo - Optional URL to redirect to after logout
|
105
|
+
* @returns Promise resolving to the logout page URL
|
106
|
+
*
|
107
|
+
* @example
|
108
|
+
* ```typescript
|
109
|
+
* // Simple logout
|
110
|
+
* const logoutPageUrl = await flow.getLogoutPageUrl();
|
111
|
+
* // Returns: "https://auth.binoauth.com/auth/logout?client_id=..."
|
112
|
+
*
|
113
|
+
* // Logout with return URL
|
114
|
+
* const logoutPageUrl = await flow.getLogoutPageUrl('https://myapp.com/goodbye');
|
115
|
+
* // Returns: "https://auth.binoauth.com/auth/logout?client_id=...&return_to=..."
|
116
|
+
*
|
117
|
+
* window.location.href = logoutPageUrl;
|
118
|
+
* ```
|
119
|
+
*/
|
120
|
+
getLogoutPageUrl(returnTo?: string): Promise<string>;
|
121
|
+
}
|
122
|
+
//# sourceMappingURL=authorization-code.d.ts.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"file":"authorization-code.d.ts","sourceRoot":"","sources":["../../../../../src/oauth/flows/authorization-code.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAY,MAAM,UAAU,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG1C,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBAAa,qBAAsB,SAAQ,QAAQ;IACjD,OAAO,CAAC,4BAA4B,CAAS;gBAEjC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,QAAQ,CAAC,EAAE,SAAS;IAkB3E;;;;;;;;;;;;;;;;;;OAkBG;IACG,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC;IA6DpC;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgHhE;;;;;;;;;;;;;;OAcG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAgBrC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACG,gBAAgB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAQ3D"}
|