@effect-auth/core 0.1.0-alpha.10 → 0.1.0-alpha.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +65 -4
  2. package/dist/AuditLog.d.ts +60 -0
  3. package/dist/AuditLog.d.ts.map +1 -1
  4. package/dist/AuditLog.js +105 -0
  5. package/dist/AuditLog.js.map +1 -1
  6. package/dist/AuthKernel.d.ts +1 -1
  7. package/dist/Client.d.ts +72 -1
  8. package/dist/Client.d.ts.map +1 -1
  9. package/dist/Client.js +218 -1
  10. package/dist/Client.js.map +1 -1
  11. package/dist/DomainVerification.d.ts +200 -0
  12. package/dist/DomainVerification.d.ts.map +1 -0
  13. package/dist/DomainVerification.js +304 -0
  14. package/dist/DomainVerification.js.map +1 -0
  15. package/dist/EffectQbSqliteStorage.d.ts +389 -4
  16. package/dist/EffectQbSqliteStorage.d.ts.map +1 -1
  17. package/dist/EffectQbSqliteStorage.js +1191 -4
  18. package/dist/EffectQbSqliteStorage.js.map +1 -1
  19. package/dist/EmailOtp.d.ts +2 -2
  20. package/dist/HttpApi/Api.d.ts +477 -23
  21. package/dist/HttpApi/Api.d.ts.map +1 -1
  22. package/dist/HttpApi/Api.js +705 -4
  23. package/dist/HttpApi/Api.js.map +1 -1
  24. package/dist/HttpApi/Endpoints.d.ts +133 -5
  25. package/dist/HttpApi/Endpoints.d.ts.map +1 -1
  26. package/dist/HttpApi/Endpoints.js +57 -2
  27. package/dist/HttpApi/Endpoints.js.map +1 -1
  28. package/dist/HttpApi/Errors.d.ts +32 -0
  29. package/dist/HttpApi/Errors.d.ts.map +1 -1
  30. package/dist/HttpApi/Errors.js +46 -0
  31. package/dist/HttpApi/Errors.js.map +1 -1
  32. package/dist/HttpApi/Schemas.d.ts +149 -8
  33. package/dist/HttpApi/Schemas.d.ts.map +1 -1
  34. package/dist/HttpApi/Schemas.js +128 -0
  35. package/dist/HttpApi/Schemas.js.map +1 -1
  36. package/dist/Identifiers.d.ts +3 -0
  37. package/dist/Identifiers.d.ts.map +1 -1
  38. package/dist/Identifiers.js +2 -0
  39. package/dist/Identifiers.js.map +1 -1
  40. package/dist/IncidentAction.d.ts +80 -0
  41. package/dist/IncidentAction.d.ts.map +1 -0
  42. package/dist/IncidentAction.js +158 -0
  43. package/dist/IncidentAction.js.map +1 -0
  44. package/dist/Jwt.d.ts +10 -0
  45. package/dist/Jwt.d.ts.map +1 -1
  46. package/dist/Jwt.js +209 -0
  47. package/dist/Jwt.js.map +1 -1
  48. package/dist/LoginNotification.d.ts +1 -1
  49. package/dist/LoginRisk.d.ts +272 -1
  50. package/dist/LoginRisk.d.ts.map +1 -1
  51. package/dist/LoginRisk.js +427 -0
  52. package/dist/LoginRisk.js.map +1 -1
  53. package/dist/MachineAuth.d.ts +73 -0
  54. package/dist/MachineAuth.d.ts.map +1 -0
  55. package/dist/MachineAuth.js +147 -0
  56. package/dist/MachineAuth.js.map +1 -0
  57. package/dist/MagicLink.d.ts +2 -2
  58. package/dist/OAuth.d.ts +957 -1
  59. package/dist/OAuth.d.ts.map +1 -1
  60. package/dist/OAuth.js +1403 -1
  61. package/dist/OAuth.js.map +1 -1
  62. package/dist/Password.d.ts +2 -2
  63. package/dist/Retention.d.ts +98 -0
  64. package/dist/Retention.d.ts.map +1 -0
  65. package/dist/Retention.js +204 -0
  66. package/dist/Retention.js.map +1 -0
  67. package/dist/SecurityTimeline.d.ts +8 -2
  68. package/dist/SecurityTimeline.d.ts.map +1 -1
  69. package/dist/SecurityTimeline.js +75 -0
  70. package/dist/SecurityTimeline.js.map +1 -1
  71. package/dist/StorageMigrations.d.ts +11 -1
  72. package/dist/StorageMigrations.d.ts.map +1 -1
  73. package/dist/StorageMigrations.js +206 -0
  74. package/dist/StorageMigrations.js.map +1 -1
  75. package/dist/Webhook.d.ts +310 -2
  76. package/dist/Webhook.d.ts.map +1 -1
  77. package/dist/Webhook.js +511 -1
  78. package/dist/Webhook.js.map +1 -1
  79. package/dist/index.d.ts +4 -0
  80. package/dist/index.d.ts.map +1 -1
  81. package/dist/index.js +4 -0
  82. package/dist/index.js.map +1 -1
  83. package/migrations/0014_auth_webhook_outbox.sql +16 -0
  84. package/migrations/0015_auth_webhook_replay.sql +7 -0
  85. package/migrations/0016_auth_login_risk_history.sql +23 -0
  86. package/migrations/0017_auth_audit_log.sql +17 -0
  87. package/migrations/0018_auth_domain_verification.sql +18 -0
  88. package/migrations/0019_auth_oauth_authorization_code.sql +17 -0
  89. package/migrations/0020_auth_oauth_provider_mode_token.sql +24 -0
  90. package/migrations/0021_auth_oauth_client_secret.sql +14 -0
  91. package/migrations/0022_auth_oauth_client.sql +15 -0
  92. package/migrations/0023_auth_oauth_consent.sql +15 -0
  93. package/package.json +17 -1
@@ -1,22 +1,26 @@
1
- import { Effect, Layer, Option, Redacted } from "effect";
2
- import { HttpServerResponse } from "effect/unstable/http";
1
+ import { Context, Effect, Encoding, Layer, Option, Redacted, Result } from "effect";
2
+ import { HttpServerRequest, HttpServerResponse } from "effect/unstable/http";
3
3
  import { HttpApi, HttpApiBuilder, HttpApiGroup } from "effect/unstable/httpapi";
4
4
  import { ApiKeyId } from "../ApiKey.js";
5
+ import { Jwks } from "../Jwt.js";
5
6
  import { AuthFlow, AuthFlowState, AuthResult, LoginApprovalFinalize, } from "../AuthFlow.js";
6
7
  import { EmailOtpLogin } from "../EmailOtp.js";
7
8
  import { EmailDeliveryError, EmailVerification, EmailVerificationFlow, EmailVerificationIssueError, } from "../EmailVerification.js";
9
+ import { OAuthClientId as makeOAuthClientId } from "../Identifiers.js";
8
10
  import { currentUnixMillis } from "../Internal.js";
9
11
  import { LoginApproval, LoginApprovalReview } from "../LoginApproval.js";
10
12
  import { LoginNotification } from "../LoginNotification.js";
11
13
  import { MagicLinkLogin } from "../MagicLink.js";
14
+ import { OAuthAuthorizationCode, OAuthAuthorizationCodeTokenGrant, OAuthClientCredentialsGrant, OAuthProviderAuthorization, OAuthRefreshToken, OAuthRefreshTokenGrant, OAuthTokenIntrospection, OAuthTokenRevocation, OAuthCodeVerifier as makeOAuthCodeVerifier, } from "../OAuth.js";
12
15
  import { PasswordLogin, PasswordManagement, PasswordRegistration, PasswordReset, } from "../Password.js";
13
16
  import { PasskeyCredentialId } from "../Passkey.js";
14
17
  import { CurrentSession, SessionCookie, Sessions, sessionRequestMetadataKey, upgradeCurrentSession, } from "../Sessions.js";
15
18
  import { SecurityTimelineStore } from "../SecurityTimeline.js";
16
19
  import { UserStore } from "../Storage.js";
17
20
  import { TrustedDevice, TrustedDeviceCookie, TrustedDeviceStore, } from "../TrustedDevice.js";
18
- import { apiKeyCreateEndpoint, apiKeyListEndpoint, apiKeyRevokeEndpoint, currentSessionEndpoint, emailOtpStartEndpoint, emailOtpVerifyEndpoint, emailVerificationStartEndpoint, emailVerificationVerifyEndpoint, loginApprovalApproveEndpoint, loginApprovalFinalizeEndpoint, loginApprovalStatusEndpoint, loginNotificationReportEndpoint, jwtIntrospectEndpoint, jwtRevokeEndpoint, listSessionsEndpoint, logoutEndpoint, magicLinkStartEndpoint, magicLinkVerifyEndpoint, mfaOptionsEndpoint, oauthAccountUnlinkEndpoint, oauthAuthorizationStartEndpoint, oauthLinkConfirmationConfirmEndpoint, oauthLinkConfirmationInspectEndpoint, oauthLinkConfirmationStartEndpoint, passwordChangeEndpoint, passwordResetStartEndpoint, passwordResetVerifyEndpoint, passwordSetEndpoint, passwordSignInEndpoint, passwordSignUpEndpoint, passkeyMfaStartEndpoint, passkeyMfaVerifyEndpoint, recoveryCodeMfaVerifyEndpoint, recoveryCodeMfaVerifyForFlowEndpoint, refreshTokenRefreshEndpoint, refreshSessionEndpoint, revokeOtherSessionsEndpoint, revokeSessionEndpoint, securityTimelineListEndpoint, stepUpOptionsEndpoint, stepUpPasskeyStartEndpoint, stepUpPasskeyVerifyEndpoint, stepUpRecoveryCodeVerifyEndpoint, stepUpTotpVerifyEndpoint, trustedDeviceListEndpoint, trustedDeviceRevokeEndpoint, totpMfaVerifyEndpoint, totpMfaVerifyForFlowEndpoint, } from "./Endpoints.js";
19
- import { AuthBadRequestError, AuthEmailAlreadyRegisteredError, AuthInternalError, AuthInvalidCredentialsError, AuthPolicyDeniedError, AuthRateLimitedError, AuthSchemaErrorMiddleware, AuthSchemaErrorMiddlewareLive, AuthUnauthenticatedError, } from "./Errors.js";
21
+ import { adminSecurityTimelineListEndpoint, adminSessionListEndpoint, adminSessionRevokeAllEndpoint, adminSessionRevokeEndpoint, adminTrustedDeviceListEndpoint, adminTrustedDeviceRevokeEndpoint, apiKeyCreateEndpoint, apiKeyListEndpoint, apiKeyRevokeEndpoint, currentSessionEndpoint, emailOtpStartEndpoint, emailOtpVerifyEndpoint, emailVerificationStartEndpoint, emailVerificationVerifyEndpoint, loginApprovalApproveEndpoint, loginApprovalFinalizeEndpoint, loginApprovalStatusEndpoint, loginNotificationReportEndpoint, jwtIntrospectEndpoint, jwtJwksEndpoint, jwtRevokeEndpoint, listSessionsEndpoint, logoutEndpoint, magicLinkStartEndpoint, magicLinkVerifyEndpoint, mfaOptionsEndpoint, oauthAccountUnlinkEndpoint, oauthAuthorizationStartEndpoint, oauthLinkConfirmationConfirmEndpoint, oauthLinkConfirmationInspectEndpoint, oauthLinkConfirmationStartEndpoint, oauthProviderAuthorizeEndpoint, oauthTokenEndpoint, oauthTokenIntrospectionEndpoint, oauthTokenRevocationEndpoint, oidcDiscoveryMetadataEndpoint, passwordChangeEndpoint, passwordResetStartEndpoint, passwordResetVerifyEndpoint, passwordSetEndpoint, passwordSignInEndpoint, passwordSignUpEndpoint, passkeyMfaStartEndpoint, passkeyMfaVerifyEndpoint, recoveryCodeMfaVerifyEndpoint, recoveryCodeMfaVerifyForFlowEndpoint, refreshTokenRefreshEndpoint, refreshSessionEndpoint, revokeOtherSessionsEndpoint, revokeSessionEndpoint, securityTimelineListEndpoint, stepUpOptionsEndpoint, stepUpPasskeyStartEndpoint, stepUpPasskeyVerifyEndpoint, stepUpRecoveryCodeVerifyEndpoint, stepUpTotpVerifyEndpoint, trustedDeviceListEndpoint, trustedDeviceRevokeEndpoint, totpMfaVerifyEndpoint, totpMfaVerifyForFlowEndpoint, } from "./Endpoints.js";
22
+ import { AuthBadRequestError, AuthEmailAlreadyRegisteredError, AuthInternalError, AuthInvalidCredentialsError, AuthPolicyDeniedError, AuthRateLimitedError, AuthSchemaErrorMiddleware, AuthSchemaErrorMiddlewareLive, AuthUnauthenticatedError, OAuthAuthorizationEndpointError, OAuthTokenEndpointError, OAuthTokenIntrospectionEndpointError, OAuthTokenRevocationEndpointError, } from "./Errors.js";
23
+ import { OAuthProviderAuthorizationQuery, OAuthTokenRequestBody, OAuthTokenIntrospectionRequestBody, OAuthTokenIntrospectionResponseBody, OAuthTokenRevocationRequestBody, } from "./Schemas.js";
20
24
  import { AuthApiRateLimit, AuthApiRateLimitLive, AuthApiRateLimitNoopLive, AuthHttpApiConfig, AuthOriginCheckMiddleware, AuthOriginCheckMiddlewareConfigLive, AuthRateLimitMiddleware, makeAuthRequestMetadata, } from "./Security.js";
21
25
  import { AuthHttp, LoginApprovalPendingCookie, makeLoginApprovalPendingCookie, } from "./Transport.js";
22
26
  const canStartRequiredEmailVerification = (result) => result._tag === "RequiresEmailVerification" &&
@@ -257,6 +261,259 @@ export const oauthAuthorizationCallbackInput = (payload, options) => Effect.gen(
257
261
  : { tokenParams: options.tokenParams }),
258
262
  };
259
263
  });
264
+ const oauthTokenTextDecoder = new TextDecoder();
265
+ const oauthTokenEndpointError = (reason, errorDescription) => new OAuthTokenEndpointError({
266
+ error: reason,
267
+ ...(errorDescription === undefined ? {} : { errorDescription }),
268
+ });
269
+ const oauthTokenRevocationEndpointError = (reason, errorDescription) => new OAuthTokenRevocationEndpointError({
270
+ error: reason,
271
+ ...(errorDescription === undefined ? {} : { errorDescription }),
272
+ });
273
+ const oauthTokenIntrospectionEndpointError = (reason, errorDescription) => new OAuthTokenIntrospectionEndpointError({
274
+ error: reason,
275
+ ...(errorDescription === undefined ? {} : { errorDescription }),
276
+ });
277
+ const oauthAuthorizationEndpointError = (reason, errorDescription) => new OAuthAuthorizationEndpointError({
278
+ error: reason,
279
+ ...(errorDescription === undefined ? {} : { errorDescription }),
280
+ });
281
+ const oauthAuthorizationRedirect = (redirectUri, params) => {
282
+ const url = new URL(redirectUri);
283
+ for (const [key, value] of Object.entries(params)) {
284
+ if (value !== undefined) {
285
+ url.searchParams.set(key, value);
286
+ }
287
+ }
288
+ return HttpServerResponse.empty({
289
+ status: 302,
290
+ headers: { location: url.toString() },
291
+ });
292
+ };
293
+ const oauthTokenGrantString = (value) => {
294
+ const trimmed = value?.trim();
295
+ return trimmed === undefined || trimmed === "" ? undefined : trimmed;
296
+ };
297
+ const formUrlDecodeComponent = (value) => {
298
+ try {
299
+ return new URLSearchParams(`value=${value}`).get("value") ?? value;
300
+ }
301
+ catch {
302
+ return value;
303
+ }
304
+ };
305
+ const decodeOAuthBasicAuthentication = (request, invalidClient) => {
306
+ const { authorization } = request.headers;
307
+ if (authorization === undefined || authorization.trim() === "") {
308
+ return Effect.succeed(Option.none());
309
+ }
310
+ if (!authorization.toLowerCase().startsWith("basic ")) {
311
+ return Effect.fail(invalidClient());
312
+ }
313
+ const encoded = authorization.slice("Basic ".length).trim();
314
+ if (encoded === "") {
315
+ return Effect.fail(invalidClient());
316
+ }
317
+ return Result.match(Encoding.decodeBase64(encoded), {
318
+ onFailure: () => Effect.fail(invalidClient()),
319
+ onSuccess: (bytes) => {
320
+ const decoded = oauthTokenTextDecoder.decode(bytes);
321
+ const separator = decoded.indexOf(":");
322
+ if (separator <= 0) {
323
+ return Effect.fail(invalidClient());
324
+ }
325
+ const clientId = oauthTokenGrantString(formUrlDecodeComponent(decoded.slice(0, separator)));
326
+ const clientSecret = formUrlDecodeComponent(decoded.slice(separator + 1));
327
+ if (clientId === undefined || clientSecret === "") {
328
+ return Effect.fail(invalidClient());
329
+ }
330
+ return Effect.succeed(Option.some({
331
+ clientId: makeOAuthClientId(clientId),
332
+ method: "client_secret_basic",
333
+ clientSecret: Redacted.make(clientSecret),
334
+ }));
335
+ },
336
+ });
337
+ };
338
+ const oauthTokenRequestBody = (request) => HttpServerRequest.schemaBodyUrlParams(OAuthTokenRequestBody).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request), Effect.mapError(() => oauthTokenEndpointError("invalid_request")));
339
+ const oauthTokenRevocationRequestBody = (request) => HttpServerRequest.schemaBodyUrlParams(OAuthTokenRevocationRequestBody).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request), Effect.mapError(() => oauthTokenRevocationEndpointError("invalid_request")));
340
+ const oauthTokenIntrospectionRequestBody = (request) => HttpServerRequest.schemaBodyUrlParams(OAuthTokenIntrospectionRequestBody).pipe(Effect.provideService(HttpServerRequest.HttpServerRequest, request), Effect.mapError(() => oauthTokenIntrospectionEndpointError("invalid_request")));
341
+ const oauthPostClientAuthentication = (payload) => {
342
+ const clientSecret = oauthTokenGrantString(payload.client_secret);
343
+ const clientId = oauthTokenGrantString(payload.client_id);
344
+ if (clientSecret === undefined) {
345
+ return undefined;
346
+ }
347
+ return clientId === undefined
348
+ ? undefined
349
+ : {
350
+ clientId: makeOAuthClientId(clientId),
351
+ method: "client_secret_post",
352
+ clientSecret: Redacted.make(clientSecret),
353
+ };
354
+ };
355
+ const oauthClientEndpointAuthentication = (input) => Effect.gen(function* () {
356
+ const basicAuth = yield* decodeOAuthBasicAuthentication(input.request, input.invalidClient);
357
+ const postAuth = oauthPostClientAuthentication(input.payload);
358
+ if (Option.isSome(basicAuth) && input.payload.client_secret !== undefined) {
359
+ return yield* Effect.fail(input.invalidRequest());
360
+ }
361
+ if (input.payload.client_secret !== undefined && postAuth === undefined) {
362
+ return yield* Effect.fail(input.invalidRequest());
363
+ }
364
+ const authentication = Option.isSome(basicAuth) ? basicAuth.value : postAuth;
365
+ const payloadClientId = oauthTokenGrantString(input.payload.client_id);
366
+ if (authentication !== undefined &&
367
+ payloadClientId !== undefined &&
368
+ authentication.clientId !== payloadClientId) {
369
+ return yield* Effect.fail(input.invalidRequest());
370
+ }
371
+ const clientAuthenticated = authentication === undefined
372
+ ? false
373
+ : yield* Effect.gen(function* () {
374
+ if (input.authenticateClient === undefined) {
375
+ return yield* Effect.fail(input.invalidClient());
376
+ }
377
+ const authenticated = yield* input
378
+ .authenticateClient({
379
+ clientId: authentication.clientId,
380
+ method: authentication.method,
381
+ clientSecret: authentication.clientSecret,
382
+ request: input.request,
383
+ })
384
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to authenticate OAuth client")));
385
+ if (!authenticated) {
386
+ return yield* Effect.fail(input.invalidClient());
387
+ }
388
+ return true;
389
+ });
390
+ const clientId = authentication === undefined
391
+ ? payloadClientId === undefined
392
+ ? undefined
393
+ : makeOAuthClientId(payloadClientId)
394
+ : authentication.clientId;
395
+ return {
396
+ ...(clientId === undefined ? {} : { clientId }),
397
+ clientAuthenticated,
398
+ };
399
+ });
400
+ const oauthTokenResponseHttpBody = (tokens) => ({
401
+ access_token: Redacted.value(tokens.accessToken),
402
+ token_type: tokens.tokenType,
403
+ ...(tokens.expiresIn === undefined ? {} : { expires_in: tokens.expiresIn }),
404
+ ...(tokens.refreshToken === undefined
405
+ ? {}
406
+ : { refresh_token: Redacted.value(tokens.refreshToken) }),
407
+ ...(tokens.idToken === undefined
408
+ ? {}
409
+ : { id_token: Redacted.value(tokens.idToken) }),
410
+ ...(tokens.scopes === undefined ? {} : { scope: tokens.scopes.join(" ") }),
411
+ ...(tokens.extra === undefined ? {} : { extra: tokens.extra }),
412
+ });
413
+ const oauthUnixMillisToSeconds = (value) => Math.floor(Number(value) / 1000);
414
+ const oauthTokenIntrospectionResponseHttpBody = (introspection) => {
415
+ if (!introspection.active) {
416
+ return { active: false };
417
+ }
418
+ return {
419
+ active: true,
420
+ ...(introspection.scopes === undefined
421
+ ? {}
422
+ : { scope: introspection.scopes.join(" ") }),
423
+ ...(introspection.clientId === undefined
424
+ ? {}
425
+ : { client_id: String(introspection.clientId) }),
426
+ ...(introspection.username === undefined
427
+ ? {}
428
+ : { username: introspection.username }),
429
+ ...(introspection.tokenType === undefined
430
+ ? {}
431
+ : { token_type: introspection.tokenType }),
432
+ ...(introspection.expiresAt === undefined
433
+ ? {}
434
+ : { exp: oauthUnixMillisToSeconds(introspection.expiresAt) }),
435
+ ...(introspection.issuedAt === undefined
436
+ ? {}
437
+ : { iat: oauthUnixMillisToSeconds(introspection.issuedAt) }),
438
+ ...(introspection.notBefore === undefined
439
+ ? {}
440
+ : { nbf: oauthUnixMillisToSeconds(introspection.notBefore) }),
441
+ ...(introspection.subject === undefined ? {} : { sub: introspection.subject }),
442
+ ...(introspection.audience === undefined
443
+ ? {}
444
+ : {
445
+ aud: Array.isArray(introspection.audience)
446
+ ? [...introspection.audience]
447
+ : introspection.audience,
448
+ }),
449
+ ...(introspection.issuer === undefined ? {} : { iss: introspection.issuer }),
450
+ ...(introspection.jwtId === undefined ? {} : { jti: introspection.jwtId }),
451
+ ...(introspection.extra === undefined ? {} : { extra: introspection.extra }),
452
+ };
453
+ };
454
+ const oauthRefreshTokenResponse = (input) => Effect.gen(function* () {
455
+ if (input.dependencies.refreshTokenGrant === undefined) {
456
+ return yield* oauthTokenEndpointError("unsupported_grant_type");
457
+ }
458
+ const refreshToken = oauthTokenGrantString(input.payload.refresh_token);
459
+ const scope = oauthTokenGrantString(input.payload.scope);
460
+ const result = yield* input.dependencies.refreshTokenGrant
461
+ .exchange({
462
+ grantType: input.payload.grant_type,
463
+ ...(refreshToken === undefined
464
+ ? {}
465
+ : { refreshToken: Redacted.make(OAuthRefreshToken(refreshToken)) }),
466
+ ...(input.clientId === undefined ? {} : { clientId: input.clientId }),
467
+ ...(scope === undefined ? {} : { scopes: scope }),
468
+ clientAuthenticated: input.clientAuthenticated,
469
+ })
470
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to exchange OAuth token")));
471
+ if (!result.valid) {
472
+ return yield* oauthTokenEndpointError(result.reason);
473
+ }
474
+ return oauthTokenResponseHttpBody(result.tokens);
475
+ });
476
+ const oauthClientCredentialsTokenResponse = (input) => Effect.gen(function* () {
477
+ if (input.dependencies.clientCredentialsGrant === undefined) {
478
+ return yield* oauthTokenEndpointError("unsupported_grant_type");
479
+ }
480
+ const scope = oauthTokenGrantString(input.payload.scope);
481
+ const result = yield* input.dependencies.clientCredentialsGrant
482
+ .exchange({
483
+ grantType: input.payload.grant_type,
484
+ ...(input.clientId === undefined ? {} : { clientId: input.clientId }),
485
+ ...(scope === undefined ? {} : { scopes: scope }),
486
+ clientAuthenticated: input.clientAuthenticated,
487
+ })
488
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to exchange OAuth token")));
489
+ if (!result.valid) {
490
+ return yield* oauthTokenEndpointError(result.reason);
491
+ }
492
+ return oauthTokenResponseHttpBody(result.tokens);
493
+ });
494
+ const oauthAuthorizationCodeTokenResponse = (input) => Effect.gen(function* () {
495
+ const code = oauthTokenGrantString(input.payload.code);
496
+ const codeVerifier = oauthTokenGrantString(input.payload.code_verifier);
497
+ const redirectUri = oauthTokenGrantString(input.payload.redirect_uri);
498
+ const result = yield* input.dependencies.tokenGrant
499
+ .exchange({
500
+ grantType: input.payload.grant_type,
501
+ ...(code === undefined
502
+ ? {}
503
+ : { code: Redacted.make(OAuthAuthorizationCode(code)) }),
504
+ ...(input.clientId === undefined ? {} : { clientId: input.clientId }),
505
+ ...(redirectUri === undefined ? {} : { redirectUri }),
506
+ ...(codeVerifier === undefined
507
+ ? {}
508
+ : { codeVerifier: Redacted.make(makeOAuthCodeVerifier(codeVerifier)) }),
509
+ clientAuthenticated: input.clientAuthenticated,
510
+ })
511
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to exchange OAuth token")));
512
+ if (!result.valid) {
513
+ return yield* oauthTokenEndpointError(result.reason);
514
+ }
515
+ return oauthTokenResponseHttpBody(result.tokens);
516
+ });
260
517
  const oauthLinkedAccountHttpBody = (account) => ({
261
518
  accountId: account.id,
262
519
  providerId: account.providerId,
@@ -593,6 +850,9 @@ const jwtIntrospectionHttpBody = (result) => result.active
593
850
  ? {}
594
851
  : { revokedReason: result.revokedReason }),
595
852
  };
853
+ export const jwtJwksHttpBody = (document) => ({
854
+ keys: document.keys,
855
+ });
596
856
  export const totpMfaVerifyInput = (payload, options = {}) => ({
597
857
  userId: payload.userId,
598
858
  code: Redacted.make(payload.code),
@@ -776,6 +1036,38 @@ const activeSessionBody = (session, currentSessionId) => {
776
1036
  ...(session.metadata === undefined ? {} : { metadata: session.metadata }),
777
1037
  };
778
1038
  };
1039
+ export const adminSessionHttpBody = (session) => {
1040
+ const request = sessionRequestMetadata(session.metadata);
1041
+ const ip = metadataString(request, "ip");
1042
+ const userAgent = metadataString(request, "userAgent");
1043
+ const country = metadataString(request, "country");
1044
+ const region = metadataString(request, "region");
1045
+ const city = metadataString(request, "city");
1046
+ const latitude = metadataNumber(request, "latitude");
1047
+ const longitude = metadataNumber(request, "longitude");
1048
+ return {
1049
+ sessionId: session.sessionId,
1050
+ userId: session.userId,
1051
+ createdAt: session.createdAt,
1052
+ authTime: session.authTime,
1053
+ ...(session.lastSeenAt === undefined
1054
+ ? {}
1055
+ : { lastSeenAt: session.lastSeenAt }),
1056
+ expiresAt: session.expiresAt,
1057
+ aal: session.aal,
1058
+ amr: session.amr,
1059
+ ...(session.mfaVerifiedAt === undefined
1060
+ ? {}
1061
+ : { mfaVerifiedAt: session.mfaVerifiedAt }),
1062
+ ...(ip === undefined ? {} : { ip }),
1063
+ ...(userAgent === undefined ? {} : { userAgent }),
1064
+ ...(country === undefined ? {} : { country }),
1065
+ ...(region === undefined ? {} : { region }),
1066
+ ...(city === undefined ? {} : { city }),
1067
+ ...(latitude === undefined ? {} : { latitude }),
1068
+ ...(longitude === undefined ? {} : { longitude }),
1069
+ };
1070
+ };
779
1071
  export const stepUpAuthenticatedHttpBody = (session) => {
780
1072
  const body = {
781
1073
  type: "authenticated",
@@ -1050,6 +1342,27 @@ const trustedDeviceSetCookies = (dependencies) => Effect.gen(function* () {
1050
1342
  return [cookie];
1051
1343
  });
1052
1344
  const catchAuthApiRateLimit = (effect, internalMessage) => effect.pipe(Effect.catchTag("RateLimitExceededError", (error) => Effect.fail(rateLimited(error.retryAfter))), Effect.catchTag("RateLimitStoreError", () => Effect.fail(passwordInternalError(internalMessage))), Effect.catchTag("PrivacyError", () => Effect.fail(passwordInternalError(internalMessage))));
1345
+ export class AdminSessionAuthorization extends Context.Service()("auth/AdminSessionAuthorization") {
1346
+ }
1347
+ (function (AdminSessionAuthorization) {
1348
+ AdminSessionAuthorization.make = (service) => AdminSessionAuthorization.of(service);
1349
+ })(AdminSessionAuthorization || (AdminSessionAuthorization = {}));
1350
+ export class AdminSecurityTimelineAuthorization extends Context.Service()("auth/AdminSecurityTimelineAuthorization") {
1351
+ }
1352
+ (function (AdminSecurityTimelineAuthorization) {
1353
+ AdminSecurityTimelineAuthorization.make = (service) => AdminSecurityTimelineAuthorization.of(service);
1354
+ })(AdminSecurityTimelineAuthorization || (AdminSecurityTimelineAuthorization = {}));
1355
+ export class AdminTrustedDeviceAuthorization extends Context.Service()("auth/AdminTrustedDeviceAuthorization") {
1356
+ }
1357
+ (function (AdminTrustedDeviceAuthorization) {
1358
+ AdminTrustedDeviceAuthorization.make = (service) => AdminTrustedDeviceAuthorization.of(service);
1359
+ })(AdminTrustedDeviceAuthorization || (AdminTrustedDeviceAuthorization = {}));
1360
+ export class OidcDiscoveryMetadata extends Context.Service()("auth/OidcDiscoveryMetadata") {
1361
+ }
1362
+ (function (OidcDiscoveryMetadata) {
1363
+ OidcDiscoveryMetadata.make = (service) => OidcDiscoveryMetadata.of(service);
1364
+ })(OidcDiscoveryMetadata || (OidcDiscoveryMetadata = {}));
1365
+ export const OidcDiscoveryMetadataLive = (metadata) => Layer.succeed(OidcDiscoveryMetadata, OidcDiscoveryMetadata.make({ load: () => Effect.succeed(metadata) }));
1053
1366
  export const makePasswordSignInHandler = (dependencies) => Effect.fn("auth.http.password.sign_in")(function* ({ payload, request, }) {
1054
1367
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "password.sign_in");
1055
1368
  const trustedDeviceToken = yield* trustedDeviceTokenFromRequest(dependencies.trustedDeviceCookie, request).pipe(Effect.mapError(() => passwordInternalError("Failed to read trusted device cookie")));
@@ -1263,6 +1576,60 @@ export const makeRevokeOtherSessionsHandler = (dependencies) => Effect.fn("auth.
1263
1576
  })));
1264
1577
  return HttpServerResponse.empty({ status: 204 });
1265
1578
  });
1579
+ export const makeAdminSessionListHandler = (dependencies) => Effect.fn("auth.http.admin_session.list")(function* ({ query, request, }) {
1580
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_session.list");
1581
+ const current = yield* readRequiredSession(dependencies, request);
1582
+ yield* dependencies.authorization.authorize({
1583
+ currentSession: current.currentSession,
1584
+ targetUserId: query.userId,
1585
+ action: "list",
1586
+ request,
1587
+ });
1588
+ const sessions = yield* dependencies.sessions
1589
+ .listForUser({ userId: query.userId })
1590
+ .pipe(Effect.mapError(() => new AuthInternalError({
1591
+ code: "internal_error",
1592
+ message: "Failed to list admin sessions",
1593
+ })));
1594
+ return { sessions: sessions.map(adminSessionHttpBody) };
1595
+ });
1596
+ export const makeAdminSessionRevokeHandler = (dependencies) => Effect.fn("auth.http.admin_session.revoke")(function* ({ payload, request, }) {
1597
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_session.revoke");
1598
+ const current = yield* readRequiredSession(dependencies, request);
1599
+ yield* dependencies.authorization.authorize({
1600
+ currentSession: current.currentSession,
1601
+ targetUserId: payload.userId,
1602
+ action: "revoke",
1603
+ request,
1604
+ sessionId: payload.sessionId,
1605
+ });
1606
+ yield* dependencies.sessions
1607
+ .revokeForUser({
1608
+ userId: payload.userId,
1609
+ sessionId: payload.sessionId,
1610
+ reason: "admin_revoked_session",
1611
+ })
1612
+ .pipe(Effect.mapError(() => new AuthInternalError({
1613
+ code: "internal_error",
1614
+ message: "Failed to revoke admin session",
1615
+ })));
1616
+ return HttpServerResponse.empty({ status: 204 });
1617
+ });
1618
+ export const makeAdminSessionRevokeAllHandler = (dependencies) => Effect.fn("auth.http.admin_session.revoke_all")(function* ({ payload, request, }) {
1619
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_session.revoke_all");
1620
+ const current = yield* readRequiredSession(dependencies, request);
1621
+ yield* dependencies.authorization.authorize({
1622
+ currentSession: current.currentSession,
1623
+ targetUserId: payload.userId,
1624
+ action: "revoke_all",
1625
+ request,
1626
+ });
1627
+ yield* dependencies.sessions.revokeAllForUser(payload.userId).pipe(Effect.mapError(() => new AuthInternalError({
1628
+ code: "internal_error",
1629
+ message: "Failed to revoke admin sessions",
1630
+ })));
1631
+ return HttpServerResponse.empty({ status: 204 });
1632
+ });
1266
1633
  export const makeLoginNotificationReportHandler = (dependencies) => Effect.fn("auth.http.security.login_notification.report")(function* ({ payload, }) {
1267
1634
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "security.login_notification.report");
1268
1635
  if (dependencies.loginNotification === undefined) {
@@ -1314,6 +1681,7 @@ const securityTimelineListInput = (query, userId) => ({
1314
1681
  ...(query.types === undefined ? {} : { types: query.types }),
1315
1682
  ...(query.categories === undefined ? {} : { categories: query.categories }),
1316
1683
  });
1684
+ const adminSecurityTimelineListInput = (query) => securityTimelineListInput(query, query.userId);
1317
1685
  export const makeSecurityTimelineListHandler = (dependencies) => Effect.fn("auth.http.security_timeline.list")(function* ({ query, request, }) {
1318
1686
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "security_timeline.list");
1319
1687
  const current = yield* readRequiredSession(dependencies, request);
@@ -1325,6 +1693,22 @@ export const makeSecurityTimelineListHandler = (dependencies) => Effect.fn("auth
1325
1693
  })));
1326
1694
  return { events: events.map(securityTimelineEventHttpBody) };
1327
1695
  });
1696
+ export const makeAdminSecurityTimelineListHandler = (dependencies) => Effect.fn("auth.http.admin_security_timeline.list")(function* ({ query, request, }) {
1697
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_security_timeline.list");
1698
+ const current = yield* readRequiredSession(dependencies, request);
1699
+ yield* dependencies.authorization.authorize({
1700
+ currentSession: current.currentSession,
1701
+ targetUserId: query.userId,
1702
+ request,
1703
+ });
1704
+ const events = yield* dependencies.securityTimeline
1705
+ .listByUser(adminSecurityTimelineListInput(query))
1706
+ .pipe(Effect.mapError(() => new AuthInternalError({
1707
+ code: "internal_error",
1708
+ message: "Failed to list admin security timeline",
1709
+ })));
1710
+ return { events: events.map(securityTimelineEventHttpBody) };
1711
+ });
1328
1712
  export const trustedDeviceHttpBody = (device) => ({
1329
1713
  deviceId: device.tokenHash,
1330
1714
  createdAt: device.createdAt,
@@ -1358,6 +1742,45 @@ export const makeTrustedDeviceRevokeHandler = (dependencies) => Effect.fn("auth.
1358
1742
  })));
1359
1743
  return HttpServerResponse.empty({ status: 204 });
1360
1744
  });
1745
+ export const makeAdminTrustedDeviceListHandler = (dependencies) => Effect.fn("auth.http.admin_trusted_device.list")(function* ({ query, request, }) {
1746
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_trusted_device.list");
1747
+ const current = yield* readRequiredSession(dependencies, request);
1748
+ yield* dependencies.authorization.authorize({
1749
+ currentSession: current.currentSession,
1750
+ targetUserId: query.userId,
1751
+ action: "list",
1752
+ request,
1753
+ });
1754
+ const now = yield* currentUnixMillis;
1755
+ const devices = yield* dependencies.trustedDevices
1756
+ .listForUser({ userId: query.userId, now })
1757
+ .pipe(Effect.mapError(() => new AuthInternalError({
1758
+ code: "internal_error",
1759
+ message: "Failed to list admin trusted devices",
1760
+ })));
1761
+ return { devices: devices.map(trustedDeviceHttpBody) };
1762
+ });
1763
+ export const makeAdminTrustedDeviceRevokeHandler = (dependencies) => Effect.fn("auth.http.admin_trusted_device.revoke")(function* ({ payload, request, }) {
1764
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "admin_trusted_device.revoke");
1765
+ const current = yield* readRequiredSession(dependencies, request);
1766
+ yield* dependencies.authorization.authorize({
1767
+ currentSession: current.currentSession,
1768
+ targetUserId: payload.userId,
1769
+ action: "revoke",
1770
+ request,
1771
+ deviceId: payload.deviceId,
1772
+ });
1773
+ yield* dependencies.trustedDevices
1774
+ .revokeForUser({
1775
+ userId: payload.userId,
1776
+ tokenHash: payload.deviceId,
1777
+ })
1778
+ .pipe(Effect.mapError(() => new AuthInternalError({
1779
+ code: "internal_error",
1780
+ message: "Failed to revoke admin trusted device",
1781
+ })));
1782
+ return HttpServerResponse.empty({ status: 204 });
1783
+ });
1361
1784
  export const makeEmailVerificationStartHandler = (dependencies) => Effect.fn("auth.http.email_verification.start")(function* ({ payload, }) {
1362
1785
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "email_verification.start");
1363
1786
  const input = emailVerificationStartInput(payload);
@@ -1458,6 +1881,133 @@ export const makeOAuthAuthorizationStartHandler = (dependencies) => Effect.fn("a
1458
1881
  }
1459
1882
  return oauthAuthorizationStartedHttpBody(started);
1460
1883
  });
1884
+ export const makeOAuthProviderAuthorizationHandler = (dependencies) => Effect.fn("auth.http.oauth.authorize")(function* ({ query, }) {
1885
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.authorize");
1886
+ const result = yield* dependencies.authorization
1887
+ .authorize({
1888
+ responseType: query.response_type,
1889
+ ...(query.client_id === undefined ? {} : { clientId: query.client_id }),
1890
+ ...(query.redirect_uri === undefined
1891
+ ? {}
1892
+ : { redirectUri: query.redirect_uri }),
1893
+ ...(query.scope === undefined ? {} : { scopes: query.scope }),
1894
+ ...(query.state === undefined ? {} : { state: query.state }),
1895
+ ...(query.code_challenge === undefined
1896
+ ? {}
1897
+ : { codeChallenge: query.code_challenge }),
1898
+ ...(query.code_challenge_method === undefined
1899
+ ? {}
1900
+ : { codeChallengeMethod: query.code_challenge_method }),
1901
+ })
1902
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to authorize OAuth request")));
1903
+ if (result.valid) {
1904
+ return oauthAuthorizationRedirect(result.redirectUri, {
1905
+ code: Redacted.value(result.code.code),
1906
+ state: result.state,
1907
+ });
1908
+ }
1909
+ if (result.redirectUri !== undefined) {
1910
+ return oauthAuthorizationRedirect(result.redirectUri, {
1911
+ error: result.reason,
1912
+ error_description: result.errorDescription,
1913
+ state: result.state,
1914
+ });
1915
+ }
1916
+ return yield* oauthAuthorizationEndpointError(result.reason, result.errorDescription);
1917
+ });
1918
+ export const makeOAuthAuthorizationCodeTokenHandler = (dependencies) => Effect.fn("auth.http.oauth.token")(function* ({ request, }) {
1919
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.token");
1920
+ const payload = yield* oauthTokenRequestBody(request);
1921
+ const { clientId, clientAuthenticated } = yield* oauthClientEndpointAuthentication({
1922
+ request,
1923
+ payload,
1924
+ authenticateClient: dependencies.authenticateClient,
1925
+ invalidRequest: () => oauthTokenEndpointError("invalid_request"),
1926
+ invalidClient: () => oauthTokenEndpointError("invalid_client"),
1927
+ });
1928
+ if (payload.grant_type === "refresh_token") {
1929
+ return yield* oauthRefreshTokenResponse({
1930
+ dependencies,
1931
+ payload,
1932
+ ...(clientId === undefined ? {} : { clientId }),
1933
+ clientAuthenticated,
1934
+ });
1935
+ }
1936
+ if (payload.grant_type === "client_credentials") {
1937
+ return yield* oauthClientCredentialsTokenResponse({
1938
+ dependencies,
1939
+ payload,
1940
+ ...(clientId === undefined ? {} : { clientId }),
1941
+ clientAuthenticated,
1942
+ });
1943
+ }
1944
+ return yield* oauthAuthorizationCodeTokenResponse({
1945
+ dependencies,
1946
+ payload,
1947
+ ...(clientId === undefined ? {} : { clientId }),
1948
+ clientAuthenticated,
1949
+ });
1950
+ });
1951
+ export const makeOAuthTokenRevocationHandler = (dependencies) => Effect.fn("auth.http.oauth.revoke")(function* ({ request, }) {
1952
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.revoke");
1953
+ const payload = yield* oauthTokenRevocationRequestBody(request);
1954
+ const { clientId, clientAuthenticated } = yield* oauthClientEndpointAuthentication({
1955
+ request,
1956
+ payload,
1957
+ authenticateClient: dependencies.authenticateClient,
1958
+ invalidRequest: () => oauthTokenRevocationEndpointError("invalid_request"),
1959
+ invalidClient: () => oauthTokenRevocationEndpointError("invalid_client"),
1960
+ });
1961
+ const token = oauthTokenGrantString(payload.token);
1962
+ const tokenTypeHint = oauthTokenGrantString(payload.token_type_hint);
1963
+ const result = yield* dependencies.tokenRevocation
1964
+ .revoke({
1965
+ ...(token === undefined ? {} : { token: Redacted.make(token) }),
1966
+ ...(tokenTypeHint === undefined ? {} : { tokenTypeHint }),
1967
+ ...(clientId === undefined ? {} : { clientId }),
1968
+ clientAuthenticated,
1969
+ })
1970
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to revoke OAuth token")));
1971
+ if (!result.valid) {
1972
+ return yield* oauthTokenRevocationEndpointError(result.reason);
1973
+ }
1974
+ return HttpServerResponse.empty({ status: 204 });
1975
+ });
1976
+ export const makeOAuthTokenIntrospectionHandler = (dependencies) => Effect.fn("auth.http.oauth.introspect")(function* ({ request, }) {
1977
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.introspect");
1978
+ const payload = yield* oauthTokenIntrospectionRequestBody(request);
1979
+ const { clientId, clientAuthenticated } = yield* oauthClientEndpointAuthentication({
1980
+ request,
1981
+ payload,
1982
+ authenticateClient: dependencies.authenticateClient,
1983
+ invalidRequest: () => oauthTokenIntrospectionEndpointError("invalid_request"),
1984
+ invalidClient: () => oauthTokenIntrospectionEndpointError("invalid_client"),
1985
+ });
1986
+ const token = oauthTokenGrantString(payload.token);
1987
+ const tokenTypeHint = oauthTokenGrantString(payload.token_type_hint);
1988
+ const result = yield* dependencies.tokenIntrospection
1989
+ .introspect({
1990
+ ...(token === undefined ? {} : { token: Redacted.make(token) }),
1991
+ ...(tokenTypeHint === undefined ? {} : { tokenTypeHint }),
1992
+ ...(clientId === undefined ? {} : { clientId }),
1993
+ clientAuthenticated,
1994
+ })
1995
+ .pipe(Effect.mapError(() => oauthInternalError("Failed to introspect OAuth token")));
1996
+ if (!result.valid) {
1997
+ return yield* oauthTokenIntrospectionEndpointError(result.reason);
1998
+ }
1999
+ return oauthTokenIntrospectionResponseHttpBody(result.introspection);
2000
+ });
2001
+ const makeOAuthTokenRevocationNotConfiguredHandler = () => Effect.fn("auth.http.oauth.revoke_not_configured")(function* ({ request, }) {
2002
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.revoke");
2003
+ yield* oauthTokenRevocationRequestBody(request);
2004
+ return yield* oauthTokenRevocationEndpointError("invalid_request");
2005
+ });
2006
+ const makeOAuthTokenIntrospectionNotConfiguredHandler = () => Effect.fn("auth.http.oauth.introspect_not_configured")(function* ({ request, }) {
2007
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.introspect");
2008
+ yield* oauthTokenIntrospectionRequestBody(request);
2009
+ return yield* oauthTokenIntrospectionEndpointError("invalid_request");
2010
+ });
1461
2011
  export const makeOAuthAccountUnlinkHandler = (dependencies) => Effect.fn("auth.http.oauth.account_unlink")(function* ({ payload, request, }) {
1462
2012
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oauth.account_unlink");
1463
2013
  const session = yield* readRequiredSession(dependencies, request);
@@ -1714,6 +2264,15 @@ export const makeJwtRevokeHandler = (dependencies) => Effect.fn("auth.http.jwt.r
1714
2264
  .pipe(Effect.mapError(jwtRevocationHttpError));
1715
2265
  return HttpServerResponse.empty({ status: 204 });
1716
2266
  });
2267
+ export const makeJwtJwksHandler = (dependencies) => Effect.fn("auth.http.jwt.jwks")(function* (_) {
2268
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "jwt.jwks");
2269
+ const document = yield* dependencies.jwks.document().pipe(Effect.mapError(() => jwtInternalError("Failed to load JWT JWKS document")));
2270
+ return jwtJwksHttpBody(document);
2271
+ });
2272
+ export const makeOidcDiscoveryMetadataHandler = (dependencies) => Effect.fn("auth.http.oidc.discovery_metadata")(function* (_) {
2273
+ yield* Effect.annotateCurrentSpan("auth.http.endpoint", "oidc.discovery_metadata");
2274
+ return yield* dependencies.metadata.load().pipe(Effect.mapError(() => jwtInternalError("Failed to load OIDC discovery metadata")));
2275
+ });
1717
2276
  export const makeMfaOptionsHandler = (dependencies) => Effect.fn("auth.http.mfa.options")(function* ({ payload, }) {
1718
2277
  yield* Effect.annotateCurrentSpan("auth.http.endpoint", "mfa.options");
1719
2278
  const pending = yield* dependencies.authFlowState
@@ -2051,6 +2610,14 @@ export class SessionApi extends HttpApiGroup.make("session")
2051
2610
  .add(currentSessionEndpoint, refreshSessionEndpoint.middleware(AuthOriginCheckMiddleware), logoutEndpoint.middleware(AuthOriginCheckMiddleware), listSessionsEndpoint, revokeSessionEndpoint.middleware(AuthOriginCheckMiddleware), revokeOtherSessionsEndpoint.middleware(AuthOriginCheckMiddleware))
2052
2611
  .prefix("/auth") {
2053
2612
  }
2613
+ export class AdminSessionApi extends HttpApiGroup.make("adminSession")
2614
+ .add(adminSessionListEndpoint, adminSessionRevokeEndpoint, adminSessionRevokeAllEndpoint)
2615
+ .prefix("/auth/admin/sessions")
2616
+ .middleware(AuthSchemaErrorMiddleware)
2617
+ .middleware(AuthOriginCheckMiddleware) {
2618
+ }
2619
+ export class AdminSessionHttpApi extends HttpApi.make("AdminSessionHttpApi").add(AdminSessionApi) {
2620
+ }
2054
2621
  export class EmailVerificationApi extends HttpApiGroup.make("emailVerification")
2055
2622
  .add(emailVerificationStartEndpoint.middleware(AuthRateLimitMiddleware), emailVerificationVerifyEndpoint)
2056
2623
  .prefix("/auth/email-verification")
@@ -2075,6 +2642,20 @@ export class OAuthApi extends HttpApiGroup.make("oauth")
2075
2642
  .middleware(AuthSchemaErrorMiddleware)
2076
2643
  .middleware(AuthOriginCheckMiddleware) {
2077
2644
  }
2645
+ export class OAuthTokenApi extends HttpApiGroup.make("oauthToken")
2646
+ .add(oauthTokenEndpoint, oauthTokenIntrospectionEndpoint, oauthTokenRevocationEndpoint)
2647
+ .prefix("/auth/oauth")
2648
+ .middleware(AuthSchemaErrorMiddleware) {
2649
+ }
2650
+ export class OAuthTokenHttpApi extends HttpApi.make("OAuthTokenHttpApi").add(OAuthTokenApi) {
2651
+ }
2652
+ export class OAuthProviderAuthorizationApi extends HttpApiGroup.make("oauthProviderAuthorization")
2653
+ .add(oauthProviderAuthorizeEndpoint)
2654
+ .prefix("/auth/oauth")
2655
+ .middleware(AuthSchemaErrorMiddleware) {
2656
+ }
2657
+ export class OAuthProviderAuthorizationHttpApi extends HttpApi.make("OAuthProviderAuthorizationHttpApi").add(OAuthProviderAuthorizationApi) {
2658
+ }
2078
2659
  export class LoginApprovalApi extends HttpApiGroup.make("loginApproval")
2079
2660
  .add(loginApprovalApproveEndpoint, loginApprovalStatusEndpoint, loginApprovalFinalizeEndpoint)
2080
2661
  .prefix("/auth/login-approval")
@@ -2109,6 +2690,20 @@ export class JwtApi extends HttpApiGroup.make("jwt")
2109
2690
  .prefix("/auth/jwt")
2110
2691
  .middleware(AuthSchemaErrorMiddleware) {
2111
2692
  }
2693
+ export class JwtDiscoveryApi extends HttpApiGroup.make("jwtDiscovery")
2694
+ .add(jwtJwksEndpoint)
2695
+ .prefix("/.well-known")
2696
+ .middleware(AuthSchemaErrorMiddleware) {
2697
+ }
2698
+ export class JwtDiscoveryHttpApi extends HttpApi.make("JwtDiscoveryHttpApi").add(JwtDiscoveryApi) {
2699
+ }
2700
+ export class OidcDiscoveryApi extends HttpApiGroup.make("oidcDiscovery")
2701
+ .add(oidcDiscoveryMetadataEndpoint)
2702
+ .prefix("/.well-known")
2703
+ .middleware(AuthSchemaErrorMiddleware) {
2704
+ }
2705
+ export class OidcDiscoveryHttpApi extends HttpApi.make("OidcDiscoveryHttpApi").add(OidcDiscoveryApi) {
2706
+ }
2112
2707
  export class SecurityApi extends HttpApiGroup.make("security")
2113
2708
  .add(loginNotificationReportEndpoint)
2114
2709
  .prefix("/auth/security")
@@ -2124,6 +2719,14 @@ export class SecurityTimelineApi extends HttpApiGroup.make("securityTimeline")
2124
2719
  export class SecurityTimelineHttpApi extends HttpApi.make("SecurityTimelineHttpApi")
2125
2720
  .add(SecurityTimelineApi) {
2126
2721
  }
2722
+ export class AdminSecurityTimelineApi extends HttpApiGroup.make("adminSecurityTimeline")
2723
+ .add(adminSecurityTimelineListEndpoint)
2724
+ .prefix("/auth/admin/security")
2725
+ .middleware(AuthSchemaErrorMiddleware)
2726
+ .middleware(AuthOriginCheckMiddleware) {
2727
+ }
2728
+ export class AdminSecurityTimelineHttpApi extends HttpApi.make("AdminSecurityTimelineHttpApi").add(AdminSecurityTimelineApi) {
2729
+ }
2127
2730
  export class TrustedDeviceApi extends HttpApiGroup.make("trustedDevice")
2128
2731
  .add(trustedDeviceListEndpoint, trustedDeviceRevokeEndpoint)
2129
2732
  .prefix("/auth/trusted-devices")
@@ -2133,6 +2736,14 @@ export class TrustedDeviceApi extends HttpApiGroup.make("trustedDevice")
2133
2736
  export class TrustedDeviceHttpApi extends HttpApi.make("TrustedDeviceHttpApi")
2134
2737
  .add(TrustedDeviceApi) {
2135
2738
  }
2739
+ export class AdminTrustedDeviceApi extends HttpApiGroup.make("adminTrustedDevice")
2740
+ .add(adminTrustedDeviceListEndpoint, adminTrustedDeviceRevokeEndpoint)
2741
+ .prefix("/auth/admin/trusted-devices")
2742
+ .middleware(AuthSchemaErrorMiddleware)
2743
+ .middleware(AuthOriginCheckMiddleware) {
2744
+ }
2745
+ export class AdminTrustedDeviceHttpApi extends HttpApi.make("AdminTrustedDeviceHttpApi").add(AdminTrustedDeviceApi) {
2746
+ }
2136
2747
  export class AuthApi extends HttpApi.make("AuthApi")
2137
2748
  .add(PasswordApi)
2138
2749
  .add(SessionApi)
@@ -2199,6 +2810,20 @@ export const SessionApiLive = HttpApiBuilder.group(AuthApi, "session", Effect.fn
2199
2810
  .handle("revoke", makeRevokeSessionHandler({ sessions, sessionCookie, authHttp }))
2200
2811
  .handle("revokeOthers", makeRevokeOtherSessionsHandler({ sessions, sessionCookie }));
2201
2812
  }));
2813
+ export const AdminSessionApiLive = HttpApiBuilder.group(AdminSessionHttpApi, "adminSession", Effect.fn("auth.http.admin_session.group")(function* (handlers) {
2814
+ const sessions = yield* Sessions;
2815
+ const sessionCookie = yield* SessionCookie;
2816
+ const authorization = yield* AdminSessionAuthorization;
2817
+ return handlers
2818
+ .handle("list", makeAdminSessionListHandler({ sessions, sessionCookie, authorization }))
2819
+ .handle("revoke", makeAdminSessionRevokeHandler({ sessions, sessionCookie, authorization }))
2820
+ .handle("revokeAll", makeAdminSessionRevokeAllHandler({
2821
+ sessions,
2822
+ sessionCookie,
2823
+ authorization,
2824
+ }));
2825
+ }));
2826
+ export const AdminSessionHttpApiLive = HttpApiBuilder.layer(AdminSessionHttpApi).pipe(Layer.provide(AdminSessionApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2202
2827
  export const EmailVerificationApiLive = HttpApiBuilder.group(AuthApi, "emailVerification", Effect.fn("auth.http.email_verification.group")(function* (handlers) {
2203
2828
  const emailVerificationFlow = yield* EmailVerificationFlow;
2204
2829
  const emailVerification = yield* EmailVerification;
@@ -2309,6 +2934,62 @@ export const SecurityTimelineApiLive = HttpApiBuilder.group(SecurityTimelineHttp
2309
2934
  }));
2310
2935
  }));
2311
2936
  export const SecurityTimelineHttpApiLive = HttpApiBuilder.layer(SecurityTimelineHttpApi).pipe(Layer.provide(SecurityTimelineApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2937
+ export const OAuthProviderAuthorizationApiLive = HttpApiBuilder.group(OAuthProviderAuthorizationHttpApi, "oauthProviderAuthorization", Effect.fn("auth.http.oauth_provider_authorization.group")(function* (handlers) {
2938
+ const authorization = yield* OAuthProviderAuthorization;
2939
+ return handlers.handle("authorize", makeOAuthProviderAuthorizationHandler({ authorization }));
2940
+ }));
2941
+ export const OAuthProviderAuthorizationHttpApiLive = HttpApiBuilder.layer(OAuthProviderAuthorizationHttpApi).pipe(Layer.provide(OAuthProviderAuthorizationApiLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2942
+ export const OAuthTokenApiLive = HttpApiBuilder.group(OAuthTokenHttpApi, "oauthToken", Effect.fn("auth.http.oauth_token.group")(function* (handlers) {
2943
+ const tokenGrant = yield* OAuthAuthorizationCodeTokenGrant;
2944
+ const refreshTokenGrant = yield* Effect.serviceOption(OAuthRefreshTokenGrant);
2945
+ const clientCredentialsGrant = yield* Effect.serviceOption(OAuthClientCredentialsGrant);
2946
+ const tokenIntrospection = yield* Effect.serviceOption(OAuthTokenIntrospection);
2947
+ const tokenRevocation = yield* Effect.serviceOption(OAuthTokenRevocation);
2948
+ return handlers
2949
+ .handle("token", makeOAuthAuthorizationCodeTokenHandler({
2950
+ tokenGrant,
2951
+ ...(Option.isNone(refreshTokenGrant)
2952
+ ? {}
2953
+ : { refreshTokenGrant: refreshTokenGrant.value }),
2954
+ ...(Option.isNone(clientCredentialsGrant)
2955
+ ? {}
2956
+ : { clientCredentialsGrant: clientCredentialsGrant.value }),
2957
+ }))
2958
+ .handle("introspect", Option.isNone(tokenIntrospection)
2959
+ ? makeOAuthTokenIntrospectionNotConfiguredHandler()
2960
+ : makeOAuthTokenIntrospectionHandler({
2961
+ tokenIntrospection: tokenIntrospection.value,
2962
+ }))
2963
+ .handle("revoke", Option.isNone(tokenRevocation)
2964
+ ? makeOAuthTokenRevocationNotConfiguredHandler()
2965
+ : makeOAuthTokenRevocationHandler({
2966
+ tokenRevocation: tokenRevocation.value,
2967
+ }));
2968
+ }));
2969
+ export const OAuthTokenHttpApiLive = HttpApiBuilder.layer(OAuthTokenHttpApi).pipe(Layer.provide(OAuthTokenApiLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2970
+ export const JwtDiscoveryApiLive = HttpApiBuilder.group(JwtDiscoveryHttpApi, "jwtDiscovery", Effect.fn("auth.http.jwt_discovery.group")(function* (handlers) {
2971
+ const jwks = yield* Jwks;
2972
+ return handlers.handle("jwks", makeJwtJwksHandler({ jwks }));
2973
+ }));
2974
+ export const JwtDiscoveryHttpApiLive = HttpApiBuilder.layer(JwtDiscoveryHttpApi).pipe(Layer.provide(JwtDiscoveryApiLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2975
+ export const OidcDiscoveryApiLive = HttpApiBuilder.group(OidcDiscoveryHttpApi, "oidcDiscovery", Effect.fn("auth.http.oidc_discovery.group")(function* (handlers) {
2976
+ const metadata = yield* OidcDiscoveryMetadata;
2977
+ return handlers.handle("openidConfiguration", makeOidcDiscoveryMetadataHandler({ metadata }));
2978
+ }));
2979
+ export const OidcDiscoveryHttpApiLive = HttpApiBuilder.layer(OidcDiscoveryHttpApi).pipe(Layer.provide(OidcDiscoveryApiLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2980
+ export const AdminSecurityTimelineApiLive = HttpApiBuilder.group(AdminSecurityTimelineHttpApi, "adminSecurityTimeline", Effect.fn("auth.http.admin_security_timeline.group")(function* (handlers) {
2981
+ const sessions = yield* Sessions;
2982
+ const sessionCookie = yield* SessionCookie;
2983
+ const securityTimeline = yield* SecurityTimelineStore;
2984
+ const authorization = yield* AdminSecurityTimelineAuthorization;
2985
+ return handlers.handle("list", makeAdminSecurityTimelineListHandler({
2986
+ sessions,
2987
+ sessionCookie,
2988
+ securityTimeline,
2989
+ authorization,
2990
+ }));
2991
+ }));
2992
+ export const AdminSecurityTimelineHttpApiLive = HttpApiBuilder.layer(AdminSecurityTimelineHttpApi).pipe(Layer.provide(AdminSecurityTimelineApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2312
2993
  export const TrustedDeviceApiLive = HttpApiBuilder.group(TrustedDeviceHttpApi, "trustedDevice", Effect.fn("auth.http.trusted_device.group")(function* (handlers) {
2313
2994
  const sessions = yield* Sessions;
2314
2995
  const sessionCookie = yield* SessionCookie;
@@ -2318,6 +2999,26 @@ export const TrustedDeviceApiLive = HttpApiBuilder.group(TrustedDeviceHttpApi, "
2318
2999
  .handle("revoke", makeTrustedDeviceRevokeHandler({ sessions, sessionCookie, trustedDevices }));
2319
3000
  }));
2320
3001
  export const TrustedDeviceHttpApiLive = HttpApiBuilder.layer(TrustedDeviceHttpApi).pipe(Layer.provide(TrustedDeviceApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
3002
+ export const AdminTrustedDeviceApiLive = HttpApiBuilder.group(AdminTrustedDeviceHttpApi, "adminTrustedDevice", Effect.fn("auth.http.admin_trusted_device.group")(function* (handlers) {
3003
+ const sessions = yield* Sessions;
3004
+ const sessionCookie = yield* SessionCookie;
3005
+ const trustedDevices = yield* TrustedDeviceStore;
3006
+ const authorization = yield* AdminTrustedDeviceAuthorization;
3007
+ return handlers
3008
+ .handle("list", makeAdminTrustedDeviceListHandler({
3009
+ sessions,
3010
+ sessionCookie,
3011
+ trustedDevices,
3012
+ authorization,
3013
+ }))
3014
+ .handle("revoke", makeAdminTrustedDeviceRevokeHandler({
3015
+ sessions,
3016
+ sessionCookie,
3017
+ trustedDevices,
3018
+ authorization,
3019
+ }));
3020
+ }));
3021
+ export const AdminTrustedDeviceHttpApiLive = HttpApiBuilder.layer(AdminTrustedDeviceHttpApi).pipe(Layer.provide(AdminTrustedDeviceApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2321
3022
  const AuthHttpApiBaseLive = HttpApiBuilder.layer(AuthApi).pipe(Layer.provide(PasswordApiLive), Layer.provide(SessionApiLive), Layer.provide(EmailVerificationApiLive), Layer.provide(EmailOtpApiLive), Layer.provide(MagicLinkApiLive), Layer.provide(LoginApprovalApiLive), Layer.provide(SecurityApiLive), Layer.provide(AuthOriginCheckMiddlewareConfigLive), Layer.provide(AuthSchemaErrorMiddlewareLive));
2322
3023
  export const AuthHttpApiBareLive = AuthHttpApiBaseLive.pipe(Layer.provide(AuthApiRateLimitNoopLive));
2323
3024
  export const AuthHttpApiLive = AuthHttpApiBaseLive.pipe(Layer.provide(AuthApiRateLimitLive));