@alepha/react 0.12.0 → 0.13.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/dist/auth/chunk-DhGyd7sr.js +28 -0
- package/dist/auth/index.browser.js +394 -114
- package/dist/auth/index.browser.js.map +1 -1
- package/dist/auth/index.cjs +80 -1927
- package/dist/auth/index.cjs.map +1 -1
- package/dist/auth/index.d.cts +1130 -420
- package/dist/auth/index.d.ts +1130 -420
- package/dist/auth/index.js +72 -1918
- package/dist/auth/index.js.map +1 -1
- package/dist/core/chunk-DhGyd7sr.js +28 -0
- package/dist/core/index.browser.js +79 -79
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.cjs +89 -85
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +1654 -154
- package/dist/core/index.d.ts +1654 -154
- package/dist/core/index.js +79 -79
- package/dist/core/index.js.map +1 -1
- package/dist/form/chunk-DhGyd7sr.js +28 -0
- package/dist/form/index.cjs +28 -8
- package/dist/form/index.cjs.map +1 -1
- package/dist/form/index.d.cts +215 -7
- package/dist/form/index.d.ts +215 -7
- package/dist/form/index.js +18 -3
- package/dist/form/index.js.map +1 -1
- package/dist/head/chunk-DhGyd7sr.js +28 -0
- package/dist/head/index.browser.js +385 -59
- package/dist/head/index.browser.js.map +1 -1
- package/dist/head/index.cjs +12 -8
- package/dist/head/index.cjs.map +1 -1
- package/dist/head/index.d.cts +1230 -24
- package/dist/head/index.d.ts +1230 -29
- package/dist/head/index.js +2 -2
- package/dist/head/index.js.map +1 -1
- package/dist/i18n/chunk-DhGyd7sr.js +28 -0
- package/dist/i18n/index.cjs +33 -20
- package/dist/i18n/index.cjs.map +1 -1
- package/dist/i18n/index.d.cts +282 -13
- package/dist/i18n/index.d.ts +282 -13
- package/dist/i18n/index.js +23 -14
- package/dist/i18n/index.js.map +1 -1
- package/dist/websocket/index.cjs +21 -8
- package/dist/websocket/index.cjs.map +1 -1
- package/dist/websocket/index.js +11 -2
- package/dist/websocket/index.js.map +1 -1
- package/package.json +7 -6
- package/src/auth/index.browser.ts +3 -6
- package/src/auth/index.shared.ts +0 -1
- package/src/auth/index.ts +3 -16
- package/src/auth/providers/ReactAuthProvider.ts +1 -614
- package/src/auth/services/ReactAuth.ts +6 -17
- package/src/core/descriptors/$page.ts +1 -1
- package/src/core/index.browser.ts +1 -0
- package/src/core/index.native.ts +21 -0
- package/src/core/index.shared-router.ts +15 -0
- package/src/core/index.shared.ts +0 -14
- package/src/core/index.ts +1 -0
- package/src/core/services/ReactRouter.ts +2 -2
- package/src/form/errors/FormValidationError.ts +20 -0
- package/src/form/hooks/useForm.ts +1 -1
- package/src/form/index.ts +1 -0
- package/src/head/providers/BrowserHeadProvider.ts +1 -1
- package/src/i18n/descriptors/$dictionary.ts +7 -3
- package/src/i18n/providers/I18nProvider.ts +9 -10
- package/src/websocket/hooks/useRoom.tsx +21 -2
- package/dist/auth/index.d.cts.map +0 -1
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/core/index.d.cts.map +0 -1
- package/dist/core/index.d.ts.map +0 -1
- package/dist/form/index.d.cts.map +0 -1
- package/dist/form/index.d.ts.map +0 -1
- package/dist/head/index.d.cts.map +0 -1
- package/dist/head/index.d.ts.map +0 -1
- package/dist/i18n/index.d.cts.map +0 -1
- package/dist/i18n/index.d.ts.map +0 -1
- package/dist/websocket/index.d.cts.map +0 -1
- package/dist/websocket/index.d.ts.map +0 -1
- package/src/auth/descriptors/$auth.ts +0 -436
- package/src/auth/descriptors/$authApple.ts +0 -8
- package/src/auth/descriptors/$authGithub.ts +0 -81
- package/src/auth/descriptors/$authGoogle.ts +0 -38
- package/src/auth/errors/SessionExpiredError.ts +0 -6
- package/src/auth/schemas/tokenResponseSchema.ts +0 -11
- package/src/auth/schemas/tokensSchema.ts +0 -21
- package/src/auth/schemas/userinfoResponseSchema.ts +0 -10
|
@@ -1,436 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
$inject,
|
|
3
|
-
AlephaError,
|
|
4
|
-
type Async,
|
|
5
|
-
createDescriptor,
|
|
6
|
-
Descriptor,
|
|
7
|
-
KIND,
|
|
8
|
-
} from "alepha";
|
|
9
|
-
import { DateTimeProvider } from "alepha/datetime";
|
|
10
|
-
import {
|
|
11
|
-
type AccessTokenResponse,
|
|
12
|
-
type RealmDescriptor,
|
|
13
|
-
SecurityError,
|
|
14
|
-
SecurityProvider,
|
|
15
|
-
type UserAccount,
|
|
16
|
-
} from "alepha/security";
|
|
17
|
-
import {
|
|
18
|
-
allowInsecureRequests,
|
|
19
|
-
Configuration,
|
|
20
|
-
discovery,
|
|
21
|
-
refreshTokenGrant,
|
|
22
|
-
} from "openid-client";
|
|
23
|
-
import type { OAuth2Profile } from "../providers/ReactAuthProvider.ts";
|
|
24
|
-
import type { Tokens } from "../schemas/tokensSchema.ts";
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Creates an authentication provider descriptor for handling user login flows.
|
|
28
|
-
*
|
|
29
|
-
* Supports multiple authentication strategies: credentials (username/password), OAuth2,
|
|
30
|
-
* and OIDC (OpenID Connect). Handles token management, user profile retrieval, and
|
|
31
|
-
* integration with both external identity providers (Auth0, Keycloak) and internal realms.
|
|
32
|
-
*
|
|
33
|
-
* **Authentication Types**: Credentials, OAuth2 (Google, GitHub), OIDC, External providers
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```ts
|
|
37
|
-
* class AuthProviders {
|
|
38
|
-
* // Internal credentials-based auth
|
|
39
|
-
* credentials = $auth({
|
|
40
|
-
* realm: this.userRealm,
|
|
41
|
-
* credentials: {
|
|
42
|
-
* account: async ({ username, password }) => {
|
|
43
|
-
* return await this.validateUser(username, password);
|
|
44
|
-
* }
|
|
45
|
-
* }
|
|
46
|
-
* });
|
|
47
|
-
*
|
|
48
|
-
* // External OIDC provider
|
|
49
|
-
* keycloak = $auth({
|
|
50
|
-
* oidc: {
|
|
51
|
-
* issuer: "https://auth.example.com",
|
|
52
|
-
* clientId: "my-app",
|
|
53
|
-
* clientSecret: "secret",
|
|
54
|
-
* redirectUri: "/auth/callback"
|
|
55
|
-
* }
|
|
56
|
-
* });
|
|
57
|
-
* }
|
|
58
|
-
* ```
|
|
59
|
-
*/
|
|
60
|
-
export const $auth = (options: AuthDescriptorOptions): AuthDescriptor => {
|
|
61
|
-
return createDescriptor(AuthDescriptor, options);
|
|
62
|
-
};
|
|
63
|
-
|
|
64
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
65
|
-
|
|
66
|
-
export type AuthDescriptorOptions = {
|
|
67
|
-
/**
|
|
68
|
-
* Name of the identity provider.
|
|
69
|
-
* If not provided, it will be derived from the property key.
|
|
70
|
-
*/
|
|
71
|
-
name?: string;
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* If true, auth provider will be skipped.
|
|
75
|
-
*/
|
|
76
|
-
disabled?: boolean;
|
|
77
|
-
} & (AuthExternal | AuthInternal);
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* When you let an external service handle authentication. (e.g. Keycloak, Auth0, etc.)
|
|
81
|
-
*/
|
|
82
|
-
export type AuthExternal = {
|
|
83
|
-
/**
|
|
84
|
-
* Only OIDC is supported for external authentication.
|
|
85
|
-
*/
|
|
86
|
-
oidc: OidcOptions;
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* For anonymous access, this will expect a service account access token.
|
|
90
|
-
*
|
|
91
|
-
* ```ts
|
|
92
|
-
* class App {
|
|
93
|
-
* anonymous = $serviceAccount(...);
|
|
94
|
-
* auth = $auth({
|
|
95
|
-
* // ... config ...
|
|
96
|
-
* fallback: this.anonymous,
|
|
97
|
-
* })
|
|
98
|
-
* }
|
|
99
|
-
* ```
|
|
100
|
-
*/
|
|
101
|
-
fallback?: () => Async<AccessToken>;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* When using your own authentication system, e.g. using a database to store user accounts.
|
|
106
|
-
* This is usually used with a custom login form.
|
|
107
|
-
*
|
|
108
|
-
* This relies on the `realm`, which is used to create/verify the access token.
|
|
109
|
-
*/
|
|
110
|
-
export type AuthInternal = {
|
|
111
|
-
realm: RealmDescriptor;
|
|
112
|
-
} & (
|
|
113
|
-
| {
|
|
114
|
-
/**
|
|
115
|
-
* The common username/password authentication.
|
|
116
|
-
*
|
|
117
|
-
* - It uses the OAuth2 Client Credentials flow to obtain an access token.
|
|
118
|
-
*
|
|
119
|
-
* This is usually used with a custom login form on your website or mobile app.
|
|
120
|
-
*/
|
|
121
|
-
credentials: CredentialsOptions;
|
|
122
|
-
}
|
|
123
|
-
| {
|
|
124
|
-
/**
|
|
125
|
-
* OAuth2 authentication. Delegates authentication to an OAuth2 provider. (e.g. Google, GitHub, etc.)
|
|
126
|
-
*
|
|
127
|
-
* - It uses the OAuth2 Authorization Code flow to obtain an access token and user information.
|
|
128
|
-
*
|
|
129
|
-
* This is usually used with a login button that redirects to the OAuth2 provider.
|
|
130
|
-
*/
|
|
131
|
-
oauth: OAuth2Options;
|
|
132
|
-
}
|
|
133
|
-
| {
|
|
134
|
-
/**
|
|
135
|
-
* Like OAuth2, but uses OIDC (OpenID Connect) for authentication and user information retrieval.
|
|
136
|
-
* OIDC is an identity layer on top of OAuth2, providing user authentication and profile information.
|
|
137
|
-
*
|
|
138
|
-
* - It uses the OAuth2 Authorization Code flow to obtain an access token and user information.
|
|
139
|
-
* - PCKE (Proof Key for Code Exchange) is recommended for security.
|
|
140
|
-
*
|
|
141
|
-
* This is usually used with a login button that redirects to the OIDC provider.
|
|
142
|
-
*/
|
|
143
|
-
oidc: OidcOptions;
|
|
144
|
-
}
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
export type CredentialsOptions = {
|
|
148
|
-
account: (credentials: {
|
|
149
|
-
username: string;
|
|
150
|
-
password: string;
|
|
151
|
-
}) => Async<UserAccount>;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
export interface OidcOptions {
|
|
155
|
-
/**
|
|
156
|
-
* URL of the OIDC issuer.
|
|
157
|
-
*/
|
|
158
|
-
issuer: string;
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Client ID for the OIDC client.
|
|
162
|
-
*/
|
|
163
|
-
clientId: string;
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Client secret for the OIDC client.
|
|
167
|
-
* Optional if PKCE (Proof Key for Code Exchange) is used.
|
|
168
|
-
*/
|
|
169
|
-
clientSecret?: string;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Redirect URI for the OIDC client.
|
|
173
|
-
* This is where the user will be redirected after authentication.
|
|
174
|
-
*/
|
|
175
|
-
redirectUri?: string;
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* For external auth providers only.
|
|
179
|
-
* Take the ID token instead of the access token for validation.
|
|
180
|
-
*/
|
|
181
|
-
useIdToken?: boolean;
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* URI to redirect the user after logout.
|
|
185
|
-
*/
|
|
186
|
-
logoutUri?: string;
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Optional scope for the OIDC client.
|
|
190
|
-
* @default "openid profile email".
|
|
191
|
-
*/
|
|
192
|
-
scope?: string;
|
|
193
|
-
|
|
194
|
-
account?: (tokens: {
|
|
195
|
-
access_token: string;
|
|
196
|
-
user: OAuth2Profile;
|
|
197
|
-
id_token?: string;
|
|
198
|
-
expires_in?: number;
|
|
199
|
-
scope?: string;
|
|
200
|
-
}) => Async<UserAccount>;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
export interface OAuth2Options {
|
|
204
|
-
/**
|
|
205
|
-
* URL of the OAuth2 authorization endpoint.
|
|
206
|
-
*/
|
|
207
|
-
clientId: string;
|
|
208
|
-
|
|
209
|
-
/**
|
|
210
|
-
* Client secret for the OAuth2 client.
|
|
211
|
-
*/
|
|
212
|
-
clientSecret: string;
|
|
213
|
-
|
|
214
|
-
/**
|
|
215
|
-
* URL of the OAuth2 authorization endpoint.
|
|
216
|
-
*/
|
|
217
|
-
authorization: string;
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* URL of the OAuth2 token endpoint.
|
|
221
|
-
*/
|
|
222
|
-
token: string;
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Function to retrieve user profile information from the OAuth2 tokens.
|
|
226
|
-
*/
|
|
227
|
-
userinfo: (tokens: Tokens) => Async<OAuth2Profile>;
|
|
228
|
-
|
|
229
|
-
account?: (tokens: {
|
|
230
|
-
access_token: string;
|
|
231
|
-
user: OAuth2Profile;
|
|
232
|
-
id_token?: string;
|
|
233
|
-
expires_in?: number;
|
|
234
|
-
scope?: string;
|
|
235
|
-
}) => Async<UserAccount>;
|
|
236
|
-
|
|
237
|
-
/**
|
|
238
|
-
* URL of the OAuth2 authorization endpoint.
|
|
239
|
-
*/
|
|
240
|
-
redirectUri?: string;
|
|
241
|
-
|
|
242
|
-
/**
|
|
243
|
-
* URL of the OAuth2 authorization endpoint.
|
|
244
|
-
*/
|
|
245
|
-
scope?: string;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
249
|
-
|
|
250
|
-
export class AuthDescriptor extends Descriptor<AuthDescriptorOptions> {
|
|
251
|
-
protected readonly securityProvider = $inject(SecurityProvider);
|
|
252
|
-
protected readonly dateTimeProvider = $inject(DateTimeProvider);
|
|
253
|
-
|
|
254
|
-
public oauth?: Configuration;
|
|
255
|
-
|
|
256
|
-
public get name() {
|
|
257
|
-
return this.options.name ?? this.config.propertyKey;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
public get jwks_uri(): string {
|
|
261
|
-
const jwks = this.oauth?.serverMetadata().jwks_uri;
|
|
262
|
-
if (!jwks) {
|
|
263
|
-
throw new AlephaError("No JWKS URI available for the auth provider");
|
|
264
|
-
}
|
|
265
|
-
return jwks;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
public get scope(): string | undefined {
|
|
269
|
-
if ("oauth" in this.options) {
|
|
270
|
-
return this.options.oauth.scope;
|
|
271
|
-
}
|
|
272
|
-
if ("oidc" in this.options) {
|
|
273
|
-
return this.options.oidc.scope || "openid profile email";
|
|
274
|
-
}
|
|
275
|
-
throw new AlephaError(
|
|
276
|
-
"No OAuth2 or OIDC configuration available for the auth provider",
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
public get redirect_uri() {
|
|
281
|
-
if ("oauth" in this.options) {
|
|
282
|
-
return this.options.oauth.redirectUri;
|
|
283
|
-
}
|
|
284
|
-
if ("oidc" in this.options) {
|
|
285
|
-
return this.options.oidc.redirectUri;
|
|
286
|
-
}
|
|
287
|
-
throw new AlephaError(
|
|
288
|
-
"No OAuth2 or OIDC configuration available for the auth provider",
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* Refreshes the access token using the refresh token.
|
|
294
|
-
* Can be used on oauth2, oidc or credentials auth providers.
|
|
295
|
-
*/
|
|
296
|
-
public async refresh(
|
|
297
|
-
refreshToken: string,
|
|
298
|
-
accessToken?: string,
|
|
299
|
-
): Promise<AccessTokenResponse> {
|
|
300
|
-
if ("realm" in this.options) {
|
|
301
|
-
return this.options.realm
|
|
302
|
-
.refreshToken(refreshToken, accessToken)
|
|
303
|
-
.then((it) => it.tokens)
|
|
304
|
-
.catch((error) => {
|
|
305
|
-
throw new SecurityError(
|
|
306
|
-
"Failed to refresh access token using the refresh token (realm)",
|
|
307
|
-
{
|
|
308
|
-
cause: error,
|
|
309
|
-
},
|
|
310
|
-
);
|
|
311
|
-
});
|
|
312
|
-
} else if (this.oauth) {
|
|
313
|
-
try {
|
|
314
|
-
return {
|
|
315
|
-
...(await refreshTokenGrant(this.oauth, refreshToken)),
|
|
316
|
-
issued_at: this.dateTimeProvider.now().unix(),
|
|
317
|
-
};
|
|
318
|
-
} catch (error) {
|
|
319
|
-
throw new SecurityError(
|
|
320
|
-
"Failed to refresh access token using the refresh token (oauth2)",
|
|
321
|
-
{
|
|
322
|
-
cause: error,
|
|
323
|
-
},
|
|
324
|
-
);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
throw new AlephaError(
|
|
329
|
-
"No realm or OAuth2 configuration available for refreshing the access token",
|
|
330
|
-
);
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Extracts user information from the access token.
|
|
335
|
-
* This is used to create a user account from the access token.
|
|
336
|
-
*/
|
|
337
|
-
public async user(tokens: Tokens): Promise<UserAccount> {
|
|
338
|
-
try {
|
|
339
|
-
if ("oauth" in this.options) {
|
|
340
|
-
const profile = await this.options.oauth.userinfo(tokens);
|
|
341
|
-
|
|
342
|
-
if (this.options.oauth.account) {
|
|
343
|
-
return this.options.oauth.account({
|
|
344
|
-
...tokens,
|
|
345
|
-
user: profile,
|
|
346
|
-
});
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
return this.securityProvider.createUserFromPayload(profile);
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
if ("oidc" in this.options) {
|
|
353
|
-
const payload = this.getUserFromIdToken(tokens.id_token || "");
|
|
354
|
-
|
|
355
|
-
if (this.options.oidc.account) {
|
|
356
|
-
return this.options.oidc.account({
|
|
357
|
-
...tokens,
|
|
358
|
-
user: payload,
|
|
359
|
-
});
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
return this.securityProvider.createUserFromPayload(payload);
|
|
363
|
-
}
|
|
364
|
-
} catch (error) {
|
|
365
|
-
throw new SecurityError(
|
|
366
|
-
"Failed to extract user from identity provider tokens",
|
|
367
|
-
{
|
|
368
|
-
cause: error,
|
|
369
|
-
},
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
throw new AlephaError(
|
|
374
|
-
"This authentication does not support user extraction from tokens",
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
protected getUserFromIdToken(idToken: string): OAuth2Profile {
|
|
379
|
-
try {
|
|
380
|
-
return JSON.parse(
|
|
381
|
-
Buffer.from(idToken.split(".")[1], "base64").toString("utf8"),
|
|
382
|
-
) as OAuth2Profile;
|
|
383
|
-
} catch (error) {
|
|
384
|
-
throw new AlephaError("Failed to parse ID Token payload", {
|
|
385
|
-
cause: error,
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
|
|
390
|
-
public async prepare() {
|
|
391
|
-
const addons: Array<(config: Configuration) => void> = [];
|
|
392
|
-
|
|
393
|
-
addons.push(allowInsecureRequests);
|
|
394
|
-
|
|
395
|
-
if ("oidc" in this.options) {
|
|
396
|
-
const { oidc } = this.options;
|
|
397
|
-
|
|
398
|
-
this.oauth = await discovery(
|
|
399
|
-
new URL(oidc.issuer),
|
|
400
|
-
oidc.clientId,
|
|
401
|
-
{
|
|
402
|
-
client_secret: oidc.clientSecret,
|
|
403
|
-
},
|
|
404
|
-
undefined,
|
|
405
|
-
{
|
|
406
|
-
execute: addons,
|
|
407
|
-
},
|
|
408
|
-
);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
if ("oauth" in this.options) {
|
|
412
|
-
const { oauth } = this.options;
|
|
413
|
-
|
|
414
|
-
this.oauth = new Configuration(
|
|
415
|
-
{
|
|
416
|
-
authorization_endpoint: oauth.authorization,
|
|
417
|
-
token_endpoint: oauth.token,
|
|
418
|
-
issuer: oauth.authorization, // use authorization URL as a pseudo-issuer?
|
|
419
|
-
// we don't need all of these endpoints
|
|
420
|
-
jwks_uri: undefined,
|
|
421
|
-
end_session_endpoint: undefined,
|
|
422
|
-
},
|
|
423
|
-
oauth.clientId,
|
|
424
|
-
{
|
|
425
|
-
client_secret: oauth.clientSecret,
|
|
426
|
-
},
|
|
427
|
-
);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
$auth[KIND] = AuthDescriptor;
|
|
433
|
-
|
|
434
|
-
// ---------------------------------------------------------------------------------------------------------------------
|
|
435
|
-
|
|
436
|
-
export type AccessToken = string | { token: () => Async<string> };
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import { $context, t } from "alepha";
|
|
2
|
-
import type { RealmDescriptor } from "alepha/security";
|
|
3
|
-
import type { OAuth2Profile } from "../providers/ReactAuthProvider.ts";
|
|
4
|
-
import { $auth, type OidcOptions } from "./$auth.ts";
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Already configured GitHub authentication descriptor.
|
|
8
|
-
*
|
|
9
|
-
* Uses OAuth2 to authenticate users via their GitHub accounts.
|
|
10
|
-
* Upon successful authentication, it links the GitHub account to a user session.
|
|
11
|
-
*
|
|
12
|
-
* Environment Variables:
|
|
13
|
-
* - `GITHUB_CLIENT_ID`: The client ID obtained from the GitHub Developer Settings.
|
|
14
|
-
* - `GITHUB_CLIENT_SECRET`: The client secret obtained from the GitHub Developer Settings.
|
|
15
|
-
*/
|
|
16
|
-
export const $authGithub = (
|
|
17
|
-
realm: RealmDescriptor,
|
|
18
|
-
options: Partial<OidcOptions>,
|
|
19
|
-
) => {
|
|
20
|
-
const { alepha } = $context();
|
|
21
|
-
|
|
22
|
-
const env = alepha.parseEnv(
|
|
23
|
-
t.object({
|
|
24
|
-
GITHUB_CLIENT_ID: t.string(),
|
|
25
|
-
GITHUB_CLIENT_SECRET: t.string(),
|
|
26
|
-
}),
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
return $auth({
|
|
30
|
-
realm,
|
|
31
|
-
name: "github",
|
|
32
|
-
oauth: {
|
|
33
|
-
clientId: env.GITHUB_CLIENT_ID,
|
|
34
|
-
clientSecret: env.GITHUB_CLIENT_SECRET,
|
|
35
|
-
authorization: "https://github.com/login/oauth/authorize",
|
|
36
|
-
token: "https://github.com/login/oauth/access_token",
|
|
37
|
-
scope: "read:user user:email",
|
|
38
|
-
userinfo: async (tokens) => {
|
|
39
|
-
const BASE_URL = "https://api.github.com";
|
|
40
|
-
const res = await fetch(`${BASE_URL}/user`, {
|
|
41
|
-
headers: {
|
|
42
|
-
Authorization: `Bearer ${tokens.access_token}`,
|
|
43
|
-
"User-Agent": "Alepha",
|
|
44
|
-
},
|
|
45
|
-
}).then((res) => res.json());
|
|
46
|
-
|
|
47
|
-
const user: OAuth2Profile = {
|
|
48
|
-
sub: res.id.toString(),
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
if (res.email) {
|
|
52
|
-
user.email = res.email;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (res.name) {
|
|
56
|
-
user.name = res.name.trim();
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (res.avatar_url) {
|
|
60
|
-
user.picture = res.avatar_url;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (!user.email) {
|
|
64
|
-
const res = await fetch(`${BASE_URL}/user/emails`, {
|
|
65
|
-
headers: {
|
|
66
|
-
Authorization: `Bearer ${tokens.access_token}`,
|
|
67
|
-
"User-Agent": "Alepha",
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
if (res.ok) {
|
|
71
|
-
const emails: any[] = await res.json();
|
|
72
|
-
user.email = (emails.find((e) => e.primary) ?? emails[0]).email;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
return user;
|
|
77
|
-
},
|
|
78
|
-
...options,
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
};
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { $context, t } from "alepha";
|
|
2
|
-
import type { RealmDescriptor } from "alepha/security";
|
|
3
|
-
import { $auth, type OidcOptions } from "./$auth.ts";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Already configured Google authentication descriptor.
|
|
7
|
-
*
|
|
8
|
-
* Uses OpenID Connect (OIDC) to authenticate users via their Google accounts.
|
|
9
|
-
* Upon successful authentication, it links the Google account to a user session.
|
|
10
|
-
*
|
|
11
|
-
* Environment Variables:
|
|
12
|
-
* - `GOOGLE_CLIENT_ID`: The client ID obtained from the Google Developer Console.
|
|
13
|
-
* - `GOOGLE_CLIENT_SECRET`: The client secret obtained from the Google Developer Console.
|
|
14
|
-
*/
|
|
15
|
-
export const $authGoogle = (
|
|
16
|
-
realm: RealmDescriptor,
|
|
17
|
-
options: Partial<OidcOptions>,
|
|
18
|
-
) => {
|
|
19
|
-
const { alepha } = $context();
|
|
20
|
-
|
|
21
|
-
const env = alepha.parseEnv(
|
|
22
|
-
t.object({
|
|
23
|
-
GOOGLE_CLIENT_ID: t.string(),
|
|
24
|
-
GOOGLE_CLIENT_SECRET: t.string(),
|
|
25
|
-
}),
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
return $auth({
|
|
29
|
-
realm,
|
|
30
|
-
name: "google",
|
|
31
|
-
oidc: {
|
|
32
|
-
issuer: "https://accounts.google.com",
|
|
33
|
-
clientId: env.GOOGLE_CLIENT_ID,
|
|
34
|
-
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
|
35
|
-
...options,
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
};
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { type Static, t } from "alepha";
|
|
2
|
-
import { userAccountInfoSchema } from "alepha/security";
|
|
3
|
-
import { apiLinksResponseSchema } from "alepha/server/links";
|
|
4
|
-
import { tokensSchema } from "./tokensSchema.ts";
|
|
5
|
-
|
|
6
|
-
export const tokenResponseSchema = t.extend(tokensSchema, {
|
|
7
|
-
user: userAccountInfoSchema,
|
|
8
|
-
api: apiLinksResponseSchema,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
export type TokenResponse = Static<typeof tokenResponseSchema>;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import type { Static } from "alepha";
|
|
2
|
-
import { t } from "alepha";
|
|
3
|
-
|
|
4
|
-
export const tokensSchema = t.object({
|
|
5
|
-
provider: t.text(),
|
|
6
|
-
access_token: t.text({ size: "rich" }),
|
|
7
|
-
issued_at: t.number(),
|
|
8
|
-
expires_in: t.optional(t.number()),
|
|
9
|
-
refresh_token: t.optional(t.text({ size: "rich" })),
|
|
10
|
-
refresh_token_expires_in: t.optional(t.number()),
|
|
11
|
-
refresh_expires_in: t.optional(
|
|
12
|
-
t.number({
|
|
13
|
-
description:
|
|
14
|
-
"Alias of `refresh_token_expires_in` for compatibility with some providers.",
|
|
15
|
-
}),
|
|
16
|
-
),
|
|
17
|
-
id_token: t.optional(t.text({ size: "rich" })),
|
|
18
|
-
scope: t.optional(t.text()),
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
export type Tokens = Static<typeof tokensSchema>;
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { type Static, t } from "alepha";
|
|
2
|
-
import { userAccountInfoSchema } from "alepha/security";
|
|
3
|
-
import { apiLinksResponseSchema } from "alepha/server/links";
|
|
4
|
-
|
|
5
|
-
export const userinfoResponseSchema = t.object({
|
|
6
|
-
user: t.optional(userAccountInfoSchema),
|
|
7
|
-
api: apiLinksResponseSchema,
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
export type UserinfoResponse = Static<typeof userinfoResponseSchema>;
|