@absolutejs/auth 0.50.0 → 0.52.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/index.d.ts CHANGED
@@ -12,7 +12,7 @@ import { AuthConfig } from './types';
12
12
  * `UserType`); the routes are reached by path at runtime / direct fetch. */
13
13
  type MergeAuthContext<Base, Extra> = Base extends Elysia<infer Path, infer Singleton, infer Definitions, infer Metadata, infer Routes, infer Ephemeral, infer Volatile> ? Extra extends Elysia<infer ExtraPath, infer ExtraSingleton, infer ExtraDefinitions, infer ExtraMetadata, infer ExtraRoutes, infer ExtraEphemeral, infer ExtraVolatile> ? Elysia<Path & ExtraPath, Singleton & ExtraSingleton, Definitions & ExtraDefinitions, Metadata & ExtraMetadata, Routes & ExtraRoutes, Ephemeral & ExtraEphemeral, Volatile & ExtraVolatile> : never : never;
14
14
  export type AuthInstance<UserType> = MergeAuthContext<ReturnType<typeof protectRoutePlugin<UserType>>, ReturnType<typeof stepUpPlugin<UserType>>>;
15
- export declare const auth: <UserType>({ providersConfiguration, authorizeRoute, cookieSecure, callbackRoute, profileRoute, signoutRoute, statusRoute, refreshRoute, revokeRoute, cleanupIntervalMs, maxSessions, sessionDurationMs, authSessionStore, audit, credentials, mfa, passwordless, lockout, sessions, sso, scim, apikeys, oidc, organizations, roles, portal, authorization, compliance, webauthn, webhooks, htmx, tracing, resolveAuthIntent, onAuthorizeSuccess, onAuthorizeError, onProfileSuccess, onProfileError, onCallbackSuccess, onLinkIdentity, onLinkIdentityConflict, onLinkConnector, onCallbackError, onStatus, onRefreshSuccess, onRefreshError, onSignOut, onRevocationSuccess, onRevocationError, onSessionCleanup }: AuthConfig<UserType>) => Promise<Elysia<"", {
15
+ export declare const auth: <UserType>({ providersConfiguration, authorizeRoute, cookieSecure, callbackRoute, profileRoute, signoutRoute, statusRoute, refreshRoute, revokeRoute, cleanupIntervalMs, maxSessions, sessionDurationMs, authSessionStore, audit, credentials, customProviders, mfa, passwordless, lockout, sessions, sso, scim, apikeys, oidc, organizations, roles, portal, authorization, compliance, webauthn, webhooks, htmx, tracing, resolveAuthIntent, onAuthorizeSuccess, onAuthorizeError, onProfileSuccess, onProfileError, onCallbackSuccess, onLinkIdentity, onLinkIdentityConflict, onLinkConnector, onCallbackError, onStatus, onRefreshSuccess, onRefreshError, onSignOut, onRevocationSuccess, onRevocationError, onSessionCleanup }: AuthConfig<UserType>) => Promise<Elysia<"", {
16
16
  decorator: {};
17
17
  store: {
18
18
  session: import("./types").SessionRecord<UserType>;
@@ -127,8 +127,8 @@ export { resolveAuthHtmxRenderers } from './htmx/renderers';
127
127
  export type { AuthHtmxConfig, AuthHtmxConnectorTarget, AuthHtmxProviderData, AuthHtmxProviderInfo, AuthHtmxRenderOverrides, AuthHtmxRenderersConfig, AuthHtmxUser, AuthIdentityPayload, LinkedProviderPayload } from './htmx/types';
128
128
  export * from './utils';
129
129
  export { buildClientProviders, resolveClientProviderEntry, resolveProviderClientConfiguration } from './providers/clients';
130
- export type { OAuth2TokenResponse, OAuth2Client, ProviderOption, PKCEProvider, OIDCProvider, RefreshableProvider, RevocableProvider, ScopeRequiredProvider, ProvidersMap, ProviderConfiguration, CredentialsFor } from 'citra';
131
- export { providers, providerOptions, refreshableProviderOptions, revocableProviderOptions, oidcProviderOptions, pkceProviderOptions, scopeRequiredProviderOptions, decodeJWT, extractPropFromIdentity, isValidProviderOption, isRefreshableOAuth2Client, isRefreshableProviderOption, isOIDCProviderOption, isPKCEProviderOption, isRevocableProviderOption, isRevocableOAuth2Client } from 'citra';
130
+ export type { OAuth2TokenResponse, OAuth2Client, OAuth2ClientForConfig, CustomProviderCredentials, ProviderConfig, ProviderOption, PKCEProvider, OIDCProvider, RefreshableProvider, RevocableProvider, ScopeRequiredProvider, ProvidersMap, ProviderConfiguration, CredentialsFor } from 'citra';
131
+ export { providers, providerOptions, createCustomOAuth2Client, defineProvider, refreshableProviderOptions, revocableProviderOptions, oidcProviderOptions, pkceProviderOptions, scopeRequiredProviderOptions, decodeJWT, extractPropFromIdentity, isValidProviderOption, isRefreshableOAuth2Client, isRefreshableProviderOption, isOIDCProviderOption, isPKCEProviderOption, isRevocableProviderOption, isRevocableOAuth2Client } from 'citra';
132
132
  export * from './crypto';
133
133
  export * from './tenancy';
134
134
  export * from './credentials/config';
package/dist/index.js CHANGED
@@ -262,6 +262,7 @@ var anilistProfileQuery = `query {
262
262
  }
263
263
  }`;
264
264
  var defineProviders = (providers) => providers;
265
+ var defineProvider = (config) => config;
265
266
  var providers = defineProviders({
266
267
  "42": {
267
268
  authorizationUrl: "https://api.intra.42.fr/oauth/authorize",
@@ -1347,6 +1348,37 @@ var providers = defineProviders({
1347
1348
  url: (config) => `https://${config.domain}/oauth2/default/v1/token`
1348
1349
  }
1349
1350
  },
1351
+ onspark: {
1352
+ authorizationUrl: "https://app.onspark.com/oauth2/authorize",
1353
+ email: ["email"],
1354
+ familyName: ["family_name"],
1355
+ fullName: ["name"],
1356
+ givenName: ["given_name"],
1357
+ isOIDC: true,
1358
+ isRefreshable: true,
1359
+ picture: ["picture"],
1360
+ PKCEMethod: "S256",
1361
+ profileRequest: {
1362
+ authIn: "header",
1363
+ encoding: "application/json",
1364
+ method: "GET",
1365
+ url: "https://app.onspark.com/oauth2/userinfo"
1366
+ },
1367
+ revocationRequest: {
1368
+ authIn: "body",
1369
+ encoding: "application/x-www-form-urlencoded",
1370
+ tokenParamName: "token",
1371
+ url: "https://app.onspark.com/oauth2/revoke"
1372
+ },
1373
+ scopeRequired: true,
1374
+ subject: ["sub"],
1375
+ subjectType: "string",
1376
+ tokenRequest: {
1377
+ authIn: "body",
1378
+ encoding: "application/x-www-form-urlencoded",
1379
+ url: "https://app.onspark.com/oauth2/token"
1380
+ }
1381
+ },
1350
1382
  osu: {
1351
1383
  authorizationUrl: "https://osu.ppy.sh/oauth/authorize",
1352
1384
  isOIDC: false,
@@ -1898,6 +1930,7 @@ var providers = defineProviders({
1898
1930
  method: "POST",
1899
1931
  url: "https://wbsapi.withings.net/v2/oauth2"
1900
1932
  },
1933
+ scopeDelimiter: ",",
1901
1934
  scopeRequired: true,
1902
1935
  subject: ["userid"],
1903
1936
  subjectType: "string",
@@ -2529,8 +2562,7 @@ var refreshableProviderOptions = Object.keys(providers).filter(isRefreshableProv
2529
2562
  var revocableProviderOptions = Object.keys(providers).filter(isRevocableProviderOption);
2530
2563
  var scopeRequiredProviderOptions = Object.keys(providers).filter(isScopeRequiredProviderOption);
2531
2564
  var readPath = (value, path) => path.reduce((cursor, key) => cursor && typeof cursor === "object" ? Reflect.get(cursor, key) : undefined, value);
2532
- var createOAuth2Client = async (providerName, config) => {
2533
- const meta = providers[providerName];
2565
+ var buildOAuth2Client = async (meta, config) => {
2534
2566
  const isConfigPropertyFunction = (cfgProp) => typeof cfgProp === "function";
2535
2567
  const resolveConfigProp = async (cfgProp) => {
2536
2568
  const result = isConfigPropertyFunction(cfgProp) ? cfgProp(config) : cfgProp;
@@ -2549,8 +2581,7 @@ var createOAuth2Client = async (providerName, config) => {
2549
2581
  if (state)
2550
2582
  url.searchParams.set("state", state);
2551
2583
  if (scope.length !== 0) {
2552
- const delimeter = providerName === "withings" ? "," : " ";
2553
- url.searchParams.set(meta.scopeParamName ?? "scope", scope.join(delimeter));
2584
+ url.searchParams.set(meta.scopeParamName ?? "scope", scope.join(meta.scopeDelimiter ?? " "));
2554
2585
  }
2555
2586
  if (meta.PKCEMethod !== undefined) {
2556
2587
  if (!codeVerifier) {
@@ -2732,6 +2763,8 @@ var createOAuth2Client = async (providerName, config) => {
2732
2763
  }
2733
2764
  };
2734
2765
  };
2766
+ var createCustomOAuth2Client = (providerConfig, credentials) => buildOAuth2Client(providerConfig, credentials);
2767
+ var createOAuth2Client = (providerName, config) => buildOAuth2Client(providers[providerName], config);
2735
2768
 
2736
2769
  // src/index.ts
2737
2770
  import { Elysia as Elysia40 } from "elysia";
@@ -3201,7 +3234,7 @@ var authIntentOption = t2.Optional(t2.Union([
3201
3234
  t2.Literal("link_identity"),
3202
3235
  t2.Literal("link_connector")
3203
3236
  ]));
3204
- var authProviderOption = t2.Enum(Object.fromEntries(Object.keys(providers).filter(isValidProviderOption).map((key) => [key, key])));
3237
+ var authProviderOption = t2.String({ minLength: 1 });
3205
3238
  var userSessionIdTypebox = t2.Optional(t2.TemplateLiteral("${string}-${string}-${string}-${string}-${string}"));
3206
3239
 
3207
3240
  // src/authorization/protectPermission.ts
@@ -3273,6 +3306,7 @@ var getStatus = async (session, user_session_id) => {
3273
3306
  };
3274
3307
  var instantiateUserSession = async ({
3275
3308
  authProvider,
3309
+ providerConfiguration,
3276
3310
  cookieSecure,
3277
3311
  session,
3278
3312
  user_session_id,
@@ -3287,6 +3321,7 @@ var instantiateUserSession = async ({
3287
3321
  }) => {
3288
3322
  const authorization = resolvedAuthorization ?? await resolveOAuthAuthorization({
3289
3323
  authProvider,
3324
+ providerConfiguration,
3290
3325
  providerInstance,
3291
3326
  tokenResponse
3292
3327
  });
@@ -3331,23 +3366,28 @@ var instantiateUserSession = async ({
3331
3366
  var resolveCookieSecure = (override) => override ?? false;
3332
3367
  var resolveOAuthAuthorization = async ({
3333
3368
  authProvider,
3369
+ providerConfiguration,
3334
3370
  providerInstance,
3335
3371
  tokenResponse,
3336
3372
  now = Date.now()
3337
3373
  }) => {
3374
+ const meta = providerConfiguration ?? (isValidProviderOption(authProvider) ? providers[authProvider] : undefined);
3375
+ if (!meta) {
3376
+ throw new Error(`No provider configuration for "${authProvider}" \u2014 pass providerConfiguration (custom provider) or resolvedAuthorization`);
3377
+ }
3338
3378
  let userIdentity;
3339
3379
  let accessToken = tokenResponse.access_token;
3340
3380
  let refreshToken = tokenResponse.refresh_token;
3341
3381
  if (tokenResponse.id_token) {
3342
3382
  userIdentity = normalizeProviderIdentity({
3343
3383
  identity: decodeJWT(tokenResponse.id_token),
3344
- providerConfiguration: providers[authProvider],
3384
+ providerConfiguration: meta,
3345
3385
  source: "idToken"
3346
3386
  });
3347
- } else if (providers[authProvider].subjectBySource?.tokenResponse) {
3387
+ } else if (meta.subjectBySource?.tokenResponse) {
3348
3388
  userIdentity = normalizeProviderIdentity({
3349
3389
  identity: tokenResponse,
3350
- providerConfiguration: providers[authProvider],
3390
+ providerConfiguration: meta,
3351
3391
  source: "tokenResponse"
3352
3392
  });
3353
3393
  } else if (authProvider === "withings") {
@@ -3357,7 +3397,7 @@ var resolveOAuthAuthorization = async ({
3357
3397
  } else {
3358
3398
  userIdentity = normalizeProviderIdentity({
3359
3399
  identity: await providerInstance.fetchUserProfile(tokenResponse.access_token),
3360
- providerConfiguration: providers[authProvider],
3400
+ providerConfiguration: meta,
3361
3401
  source: "profile"
3362
3402
  });
3363
3403
  }
@@ -6911,6 +6951,21 @@ var oidcProviderRoutes = (config) => {
6911
6951
  return loginUrl === undefined ? jsonResponse({ error: "login_required" }, HTTP_UNAUTHORIZED2) : redirectTo(`${loginUrl}?return_to=${encodeURIComponent(request.url)}`);
6912
6952
  }
6913
6953
  const requested = scope === undefined || scope.length === 0 ? client.scopes : scope.split(" ").filter((entry) => client.scopes.includes(entry));
6954
+ if (config.consentUrl !== undefined && await config.needsConsent?.({
6955
+ client: {
6956
+ clientId: client.clientId,
6957
+ name: client.name
6958
+ },
6959
+ requestedScopes: requested,
6960
+ user: userSession.user
6961
+ }) === true) {
6962
+ const consentTarget = new URL(config.consentUrl, issuer);
6963
+ consentTarget.searchParams.set("return_to", request.url);
6964
+ consentTarget.searchParams.set("client_id", client.clientId);
6965
+ consentTarget.searchParams.set("client_name", client.name);
6966
+ consentTarget.searchParams.set("scope", requested.join(" "));
6967
+ return redirectTo(consentTarget.toString());
6968
+ }
6914
6969
  const granted = getGrantedScopes === undefined ? requested : await getGrantedScopes({
6915
6970
  client: {
6916
6971
  clientId: client.clientId,
@@ -7409,7 +7464,7 @@ var oidcProviderRoutes = (config) => {
7409
7464
  headers: t13.Object({
7410
7465
  authorization: t13.Optional(t13.String())
7411
7466
  })
7412
- }).get(jwksRoute, () => ({ keys: [toPublicJwk(signingKey)] })).get("/.well-known/openid-configuration", () => discovery);
7467
+ }).get(jwksRoute, () => ({ keys: [toPublicJwk(signingKey)] })).get("/.well-known/openid-configuration", () => discovery).get("/.well-known/oauth-authorization-server", () => discovery);
7413
7468
  };
7414
7469
 
7415
7470
  // src/organizations/routes.ts
@@ -8267,7 +8322,52 @@ var roleRoutes = ({
8267
8322
  // src/providers/clients.ts
8268
8323
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
8269
8324
  var isProviderClientConfig = (value) => isRecord(value) && ("credentials" in value);
8270
- var buildClientProviders = async (providersConfiguration, createOAuth2ClientFn) => {
8325
+ var isCustomClientConfig = (value) => isRecord(value) && ("credentials" in value) && ("providerConfig" in value);
8326
+ var normalizeCustomClients = (providerName, providerConfig) => {
8327
+ if (isCustomClientConfig(providerConfig))
8328
+ return { "": providerConfig };
8329
+ if (!isRecord(providerConfig) || Object.keys(providerConfig).length === 0) {
8330
+ throw new Error(`Invalid custom provider configuration for ${providerName}`);
8331
+ }
8332
+ Object.entries(providerConfig).forEach(([clientName, clientConfig]) => {
8333
+ if (!isCustomClientConfig(clientConfig)) {
8334
+ throw new Error(`Invalid custom client configuration for ${providerName}.${clientName}`);
8335
+ }
8336
+ });
8337
+ return providerConfig;
8338
+ };
8339
+ var buildCustomProviderGroups = async (customProviders) => {
8340
+ const groups = await Promise.all(Object.entries(customProviders).map(async ([providerName, config]) => {
8341
+ if (isValidProviderOption(providerName)) {
8342
+ throw new Error(`Custom provider "${providerName}" collides with a built-in provider \u2014 configure it under providersConfiguration instead`);
8343
+ }
8344
+ const clients = normalizeCustomClients(providerName, config);
8345
+ const entries = await Promise.all(Object.entries(clients).map(async ([clientName, clientConfig]) => {
8346
+ const providerInstance = await createCustomOAuth2Client(clientConfig.providerConfig, clientConfig.credentials);
8347
+ return [
8348
+ clientName,
8349
+ {
8350
+ clientName: clientName.length > 0 ? clientName : undefined,
8351
+ providerConfiguration: clientConfig.providerConfig,
8352
+ providerInstance,
8353
+ requiresPKCE: clientConfig.providerConfig.PKCEMethod !== undefined,
8354
+ scope: clientConfig.scope,
8355
+ searchParams: clientConfig.searchParams
8356
+ }
8357
+ ];
8358
+ }));
8359
+ return [
8360
+ providerName,
8361
+ {
8362
+ entries: Object.fromEntries(entries),
8363
+ isSingleClient: entries.length === 1 && entries[0]?.[0] === ""
8364
+ }
8365
+ ];
8366
+ }));
8367
+ return Object.fromEntries(groups);
8368
+ };
8369
+ var buildClientProviders = async (providersConfiguration, createOAuth2ClientFn, customProviders) => {
8370
+ const customGroups = customProviders ? await buildCustomProviderGroups(customProviders) : {};
8271
8371
  const normalized = normalizeProvidersConfiguration(providersConfiguration);
8272
8372
  const groups = await Promise.all(Object.entries(normalized).map(async ([providerName, clients]) => {
8273
8373
  if (!isValidProviderOption(providerName)) {
@@ -8279,7 +8379,9 @@ var buildClientProviders = async (providersConfiguration, createOAuth2ClientFn)
8279
8379
  clientName,
8280
8380
  {
8281
8381
  clientName: clientName.length > 0 ? clientName : undefined,
8382
+ providerConfiguration: providers[providerName],
8282
8383
  providerInstance,
8384
+ requiresPKCE: providers[providerName].PKCEMethod !== undefined,
8283
8385
  scope: providerConfig.scope,
8284
8386
  searchParams: providerConfig.searchParams
8285
8387
  }
@@ -8293,7 +8395,7 @@ var buildClientProviders = async (providersConfiguration, createOAuth2ClientFn)
8293
8395
  }
8294
8396
  ];
8295
8397
  }));
8296
- return Object.fromEntries(groups);
8398
+ return { ...customGroups, ...Object.fromEntries(groups) };
8297
8399
  };
8298
8400
  var normalizeClientConfig = (providerName, clientName, clientConfig) => {
8299
8401
  if (!isProviderClientConfig(clientConfig)) {
@@ -8429,7 +8531,13 @@ var authorize = ({
8429
8531
  if ("error" in resolvedProvider) {
8430
8532
  return status("Unauthorized", resolvedProvider.error);
8431
8533
  }
8432
- const { clientName, providerInstance, scope, searchParams } = resolvedProvider.entry;
8534
+ const {
8535
+ clientName,
8536
+ providerInstance,
8537
+ requiresPKCE,
8538
+ scope,
8539
+ searchParams
8540
+ } = resolvedProvider.entry;
8433
8541
  const referer = parseReferer(headers["referer"]);
8434
8542
  const authIntent = isAuthIntent(intent) ? intent : undefined;
8435
8543
  origin_url.set({
@@ -8477,7 +8585,7 @@ var authorize = ({
8477
8585
  secure,
8478
8586
  value: currentState
8479
8587
  });
8480
- const codeVerifier = isPKCEProviderOption(provider) ? generateCodeVerifier() : undefined;
8588
+ const codeVerifier = requiresPKCE ? generateCodeVerifier() : undefined;
8481
8589
  if (codeVerifier) {
8482
8590
  code_verifier.set({
8483
8591
  httpOnly: true,
@@ -8589,9 +8697,13 @@ var callback = ({
8589
8697
  if ("error" in resolvedProvider) {
8590
8698
  return status("Unauthorized", resolvedProvider.error);
8591
8699
  }
8592
- const { clientName, providerInstance } = resolvedProvider.entry;
8700
+ const {
8701
+ clientName,
8702
+ providerConfiguration,
8703
+ providerInstance,
8704
+ requiresPKCE
8705
+ } = resolvedProvider.entry;
8593
8706
  stored_state.remove();
8594
- const requiresPKCE = isPKCEProviderOption(authProvider);
8595
8707
  const verifier = requiresPKCE ? code_verifier.value : undefined;
8596
8708
  if (requiresPKCE && verifier === undefined) {
8597
8709
  return status("Bad Request", "Code verifier not found and is required");
@@ -8639,6 +8751,7 @@ var callback = ({
8639
8751
  cookie,
8640
8752
  currentUser,
8641
8753
  originUrl,
8754
+ providerConfiguration,
8642
8755
  providerInstance,
8643
8756
  redirect,
8644
8757
  session: callbackSession,
@@ -27333,6 +27446,7 @@ var auth = async ({
27333
27446
  authSessionStore,
27334
27447
  audit,
27335
27448
  credentials,
27449
+ customProviders,
27336
27450
  mfa,
27337
27451
  passwordless,
27338
27452
  lockout,
@@ -27370,7 +27484,7 @@ var auth = async ({
27370
27484
  }) => {
27371
27485
  if (tracing !== undefined)
27372
27486
  await initTracing(tracing);
27373
- const clientProviders = await buildClientProviders(providersConfiguration, createOAuth2Client);
27487
+ const clientProviders = await buildClientProviders(providersConfiguration, createOAuth2Client, customProviders);
27374
27488
  const resolvedCookieSecure = resolveCookieSecure(cookieSecure);
27375
27489
  const webhookDispatch = webhooks ? createWebhookDispatcher(webhooks) : undefined;
27376
27490
  const auditEmit = audit || webhookDispatch ? createAuditEmitter({
@@ -27700,6 +27814,7 @@ export {
27700
27814
  deleteRegisteredClient,
27701
27815
  defineScimAttributeMap,
27702
27816
  defineProvidersConfiguration,
27817
+ defineProvider,
27703
27818
  defineAuthSettings,
27704
27819
  defineAuthHtmxConfig,
27705
27820
  defineAuthConfig,
@@ -27845,6 +27960,7 @@ export {
27845
27960
  createInMemoryAccessTokenStore,
27846
27961
  createFgaEngine,
27847
27962
  createFederatedTokenStore,
27963
+ createCustomOAuth2Client,
27848
27964
  createCredentialOffer,
27849
27965
  createAuthHtmxRoutes,
27850
27966
  createAuditRedactor,
@@ -27927,5 +28043,5 @@ export {
27927
28043
  AuthIdentityConflictError
27928
28044
  };
27929
28045
 
27930
- //# debugId=47718768D4F1D77064756E2164756E21
28046
+ //# debugId=1C44E17EB1F4FC5164756E2164756E21
27931
28047
  //# sourceMappingURL=index.js.map