nuxt-auth-utils 0.6.0 → 0.6.2

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 (31) hide show
  1. package/README.md +95 -3
  2. package/dist/module.d.mts +36 -2
  3. package/dist/module.json +1 -1
  4. package/dist/module.mjs +8 -5
  5. package/dist/runtime/server/api/session.get.d.ts +1 -1
  6. package/dist/runtime/server/api/session.get.js +5 -1
  7. package/dist/runtime/server/lib/atproto/bluesky.js +14 -1
  8. package/dist/runtime/server/lib/oauth/heroku.js +1 -1
  9. package/dist/runtime/server/lib/oauth/hubspot.js +25 -4
  10. package/dist/runtime/server/lib/oauth/mastodon.js +2 -2
  11. package/dist/runtime/server/lib/oauth/salesforce.js +1 -1
  12. package/dist/runtime/server/lib/oauth/slack.js +1 -1
  13. package/dist/runtime/server/lib/oauth/steam.js +2 -2
  14. package/dist/runtime/server/lib/session.d.ts +11 -0
  15. package/dist/runtime/server/lib/session.js +33 -0
  16. package/dist/runtime/server/lib/utils.d.ts +1 -0
  17. package/dist/runtime/server/lib/utils.js +8 -4
  18. package/dist/runtime/server/lib/webauthn/authenticate.js +5 -4
  19. package/dist/runtime/server/lib/webauthn/register.js +6 -5
  20. package/dist/runtime/server/utils/atproto.js +9 -8
  21. package/dist/runtime/server/utils/session.d.ts +1 -5
  22. package/dist/runtime/server/utils/session.js +4 -27
  23. package/dist/runtime/types/atproto.d.ts +6 -4
  24. package/dist/runtime/types/index.d.ts +1 -1
  25. package/dist/runtime/types/oauth-config.d.ts +14 -0
  26. package/dist/runtime/types/oauth-runtime-config.d.ts +119 -0
  27. package/dist/runtime/types/oauth-runtime-config.js +0 -0
  28. package/dist/runtime/types/session.d.ts +5 -2
  29. package/dist/runtime/utils/atproto.js +1 -2
  30. package/dist/types.d.mts +1 -1
  31. package/package.json +1 -1
package/README.md CHANGED
@@ -410,6 +410,38 @@ export default defineNuxtConfig({
410
410
  })
411
411
  ```
412
412
 
413
+ #### Callback path
414
+
415
+ The authorization server compares the redirect URI of the login request with the list published in the client metadata document. That document is served from `bluesky/client-metadata.json` under your app base URL (`app.baseURL`, `/` by default), so it cannot know where your login route lives.
416
+
417
+ The module fills in `/auth/<provider>`, the path its playground uses, under that base URL. The login route is served under the base URL too, so a deployment that sets `app.baseURL` publishes the path its login route answers on:
418
+
419
+ ```text
420
+ app.baseURL: '/my-app/' -> redirect_uris: ["https://example.com/my-app/auth/bluesky"]
421
+ ```
422
+
423
+ Set `redirectUris` when your login route lives elsewhere; until you do, the module logs a warning in development. Both requests then use it:
424
+
425
+ ```ts
426
+ export default defineNuxtConfig({
427
+ runtimeConfig: {
428
+ oauth: {
429
+ bluesky: {
430
+ redirectUris: ['/login/bluesky']
431
+ }
432
+ }
433
+ }
434
+ })
435
+ ```
436
+
437
+ Declare paths only, without the origin or the app base URL. The module prepends the base URL, like it does for `/auth/<provider>`, and leaves a path that already starts with it as is. It then prepends the origin of the request, because the URL origin must match the client ID:
438
+
439
+ ```text
440
+ app.baseURL: '/my-app/', redirectUris: ['/login/bluesky'] -> redirect_uris: ["https://example.com/my-app/login/bluesky"]
441
+ ```
442
+
443
+ The `redirectUrl` option of `defineOAuthBlueskyEventHandler()` applies to the login request alone, also under the base URL. The metadata document cannot see it, so use `redirectUris` for a custom callback path.
444
+
413
445
  ### WebAuthn (passkey)
414
446
 
415
447
  WebAuthn (Web Authentication) is a web standard that enhances security by replacing passwords with passkeys using public key cryptography. Users can authenticate with biometric data (like fingerprints or facial recognition) or physical devices (like USB keys), reducing the risk of phishing and password breaches. This approach offers a more secure and user-friendly authentication method, supported by major browsers and platforms.
@@ -432,6 +464,37 @@ export default defineNuxtConfig({
432
464
  })
433
465
  ```
434
466
 
467
+ #### Relying Party ID
468
+
469
+ A passkey is bound to the domain that created it. By default, a passkey created on `example.com` cannot be used on `app.example.com`. Set `rpID` to the registrable parent domain to share passkeys between a domain and its subdomains:
470
+
471
+ ```ts
472
+ export default defineNuxtConfig({
473
+ auth: {
474
+ webAuthn: {
475
+ rpID: 'example.com', // passkeys created on `example.com` also work on `app.example.com`
476
+ rpName: 'My App' // shown by the authenticator when creating a passkey
477
+ }
478
+ }
479
+ })
480
+ ```
481
+
482
+ Both options default to the hostname of the request. The module uses `rpID` to generate the options and to verify the response, and `rpName` only to label the passkey during registration. Both are read from `runtimeConfig`, which helps when one build serves several domains:
483
+
484
+ ```ts
485
+ export default defineNuxtConfig({
486
+ runtimeConfig: {
487
+ webauthn: {
488
+ rpID: 'example.com'
489
+ }
490
+ }
491
+ })
492
+ ```
493
+
494
+ `NUXT_WEBAUTHN_RP_ID` and `NUXT_WEBAUTHN_RP_NAME` override both at runtime.
495
+
496
+ When you define `getOptions`, the module calls it while generating the options and again while verifying the response. Return the same `rpID` in both calls, or verification fails with an `Unexpected RP ID hash` error. The expected origin is always the origin of the request, because that is what the browser sends back.
497
+
435
498
  #### Example
436
499
 
437
500
  In this example we will implement the very basic steps to register and authenticate a credential.
@@ -638,6 +701,7 @@ We leverage hooks to let you extend the session data with your own data or log w
638
701
  export default () => {
639
702
  // Called when the session is fetched during SSR for the Vue composable (/api/_auth/session)
640
703
  // Or when we call useUserSession().fetch()
704
+ // Not called for a visitor without a session cookie: they get an empty session and no cookie is set
641
705
  sessionHooks.hook('fetch', async (session, event) => {
642
706
  // extend User Session by calling your database
643
707
  // or
@@ -899,6 +963,34 @@ export default defineNuxtConfig({
899
963
 
900
964
  The browser then sends the session cookie to every subdomain of `example.com`, and each application can decrypt it.
901
965
 
966
+ ## Serve over plain HTTP
967
+
968
+ The OAuth event handlers set short-lived cookies for the state, nonce and PKCE verifier. They are `Secure` except in development. Browsers ignore `Secure` cookies set over plain HTTP, except on `localhost`, so logging in to a built app served over plain HTTP, for example in an end-to-end test stack, fails with `state mismatch`.
969
+
970
+ When `runtimeConfig.session.cookie.secure` is `false`, both the session cookie and these cookies are set without `Secure`. Nuxt only overrides declared options from environment variables, so declare it in your `nuxt.config.ts` to switch it at runtime:
971
+
972
+ ```ts
973
+ export default defineNuxtConfig({
974
+ modules: ['nuxt-auth-utils'],
975
+ runtimeConfig: {
976
+ session: {
977
+ cookie: {
978
+ secure: true
979
+ }
980
+ }
981
+ }
982
+ })
983
+ ```
984
+
985
+ Then start the built app with `NUXT_SESSION_COOKIE_SECURE=false`:
986
+
987
+ ```bash
988
+ NUXT_SESSION_COOKIE_SECURE=false node .output/server/index.mjs
989
+ ```
990
+
991
+ > [!WARNING]
992
+ > Only do this when the app is served over plain HTTP: without `Secure`, the cookies can be read or overwritten on the network. A proxy that terminates TLS is not a reason to disable it, as `Secure` applies to the browser's connection.
993
+
902
994
  ## More
903
995
 
904
996
  - [nuxt-authorization](https://github.com/barbapapazes/nuxt-authorization): Authorization module for managing permissions inside a Nuxt app, compatible with `nuxt-auth-utils`
@@ -936,13 +1028,13 @@ Releases are automated with [uppt](https://github.com/danielroe/uppt): every pus
936
1028
 
937
1029
  <!-- Badges -->
938
1030
  [npm-version-src]: https://img.shields.io/npm/v/nuxt-auth-utils/latest.svg?style=flat&colorA=020420&colorB=00DC82
939
- [npm-version-href]: https://npmjs.com/package/nuxt-auth-utils
1031
+ [npm-version-href]: https://npmx.dev/package/nuxt-auth-utils
940
1032
 
941
1033
  [npm-downloads-src]: https://img.shields.io/npm/dm/nuxt-auth-utils.svg?style=flat&colorA=020420&colorB=00DC82
942
- [npm-downloads-href]: https://npm.chart.dev/nuxt-auth-utils
1034
+ [npm-downloads-href]: https://npmx.dev/package-stats/nuxt-auth-utils/v/latest?granularity=monthly
943
1035
 
944
1036
  [license-src]: https://img.shields.io/npm/l/nuxt-auth-utils.svg?style=flat&colorA=020420&colorB=00DC82
945
- [license-href]: https://npmjs.com/package/nuxt-auth-utils
1037
+ [license-href]: https://github.com/atinux/nuxt-auth-utils/blob/main/LICENSE
946
1038
 
947
1039
  [nuxt-src]: https://img.shields.io/badge/Nuxt-020420?logo=nuxt.js
948
1040
  [nuxt-href]: https://nuxt.com
package/dist/module.d.mts CHANGED
@@ -1,12 +1,28 @@
1
1
  import { ScryptConfig } from '@adonisjs/hash/types';
2
+ import { GenerateRegistrationOptionsOpts, GenerateAuthenticationOptionsOpts } from '@simplewebauthn/server';
2
3
  import { SessionConfig } from 'nuxt/server';
4
+ import { OAuthRuntimeConfig } from '../dist/runtime/types/oauth-runtime-config.js';
3
5
 
6
+ interface WebAuthnOptions {
7
+ /**
8
+ * Relying Party ID the passkeys are bound to.
9
+ *
10
+ * Set the registrable parent domain (such as `example.com`) to create and use passkeys across subdomains.
11
+ * @default the request hostname
12
+ */
13
+ rpID?: string;
14
+ /**
15
+ * Relying Party name shown by the authenticator when creating a passkey
16
+ * @default the request hostname
17
+ */
18
+ rpName?: string;
19
+ }
4
20
  interface ModuleOptions {
5
21
  /**
6
22
  * Enable WebAuthn (Passkeys)
7
23
  * @default false
8
24
  */
9
- webAuthn?: boolean;
25
+ webAuthn?: boolean | WebAuthnOptions;
10
26
  /**
11
27
  * Enable atproto OAuth (Bluesky, etc.)
12
28
  * @default false
@@ -36,6 +52,24 @@ declare module 'nuxt/schema' {
36
52
  * Session configuration, the session is sealed with a secret derived from `appSecret` (`NUXT_APP_SECRET`)
37
53
  */
38
54
  session: Omit<SessionConfig, 'password'>;
55
+ /**
56
+ * WebAuthn settings, `rpID` and `rpName` are set from the `webAuthn` module option.
57
+ *
58
+ * Both can be overridden at runtime with `NUXT_WEBAUTHN_RP_ID` and `NUXT_WEBAUTHN_RP_NAME`.
59
+ *
60
+ * `register` and `authenticate` are merged into the options passed to
61
+ * `generateRegistrationOptions()` and `generateAuthenticationOptions()`.
62
+ */
63
+ webauthn: {
64
+ rpID: string;
65
+ rpName: string;
66
+ register: Partial<GenerateRegistrationOptionsOpts>;
67
+ authenticate: Partial<GenerateAuthenticationOptionsOpts>;
68
+ };
69
+ /**
70
+ * OAuth providers configuration, one key per provider (`github`, `google`, `keycloak`, ...).
71
+ */
72
+ oauth: OAuthRuntimeConfig;
39
73
  }
40
74
  interface PublicRuntimeConfig {
41
75
  auth: {
@@ -46,4 +80,4 @@ declare module 'nuxt/schema' {
46
80
  declare const _default: any;
47
81
 
48
82
  export { _default as default };
49
- export type { ModuleOptions };
83
+ export type { ModuleOptions, WebAuthnOptions };
package/dist/module.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": ">=4.6.0"
6
6
  },
7
- "version": "0.6.0",
7
+ "version": "0.6.2",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.3",
10
10
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -91,7 +91,10 @@ const module$1 = defineNuxtModule({
91
91
  runtimeConfig.public.auth = defu(runtimeConfig.public.auth, {
92
92
  loadStrategy: options.loadStrategy ?? "server-first"
93
93
  });
94
+ const webAuthnOptions = typeof options.webAuthn === "object" ? options.webAuthn : void 0;
94
95
  runtimeConfig.webauthn = defu(runtimeConfig.webauthn, {
96
+ rpID: webAuthnOptions?.rpID ?? "",
97
+ rpName: webAuthnOptions?.rpName ?? "",
95
98
  register: {},
96
99
  authenticate: {}
97
100
  });
@@ -145,7 +148,7 @@ const module$1 = defineNuxtModule({
145
148
  clientId: "",
146
149
  clientSecret: "",
147
150
  connectionId: "",
148
- screenHint: "",
151
+ screenHint: "sign-in",
149
152
  redirectURL: ""
150
153
  });
151
154
  runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
@@ -360,25 +363,25 @@ const module$1 = defineNuxtModule({
360
363
  redirectURL: "",
361
364
  baseURL: "",
362
365
  userURL: "",
363
- scope: ""
366
+ scope: []
364
367
  });
365
368
  runtimeConfig.oauth.slack = defu(runtimeConfig.oauth.slack, {
366
369
  clientId: "",
367
370
  clientSecret: "",
368
371
  redirectURL: "",
369
- scope: ""
372
+ scope: []
370
373
  });
371
374
  runtimeConfig.oauth.heroku = defu(runtimeConfig.oauth.heroku, {
372
375
  clientId: "",
373
376
  clientSecret: "",
374
377
  redirectURL: "",
375
- scope: ""
378
+ scope: []
376
379
  });
377
380
  runtimeConfig.oauth.roblox = defu(runtimeConfig.oauth.roblox, {
378
381
  clientId: "",
379
382
  clientSecret: "",
380
383
  redirectURL: "",
381
- scope: ""
384
+ scope: []
382
385
  });
383
386
  runtimeConfig.oauth.okta = defu(runtimeConfig.oauth.okta, {
384
387
  clientId: "",
@@ -1,2 +1,2 @@
1
- declare const _default: import("nuxt/server").EventHandler<Promise<UserSession>>;
1
+ declare const _default: import("nuxt/server").EventHandler<Promise<any>>;
2
2
  export default _default;
@@ -1,6 +1,10 @@
1
- import { defineEventHandler } from "nuxt/server";
1
+ import { defineEventHandler, getCookie } from "nuxt/server";
2
2
  import { getUserSession, sessionHooks } from "../utils/session.js";
3
+ import { resolveSessionConfig } from "../lib/session.js";
3
4
  export default defineEventHandler(async (event) => {
5
+ if (!getCookie(event, resolveSessionConfig().name)) {
6
+ return {};
7
+ }
4
8
  const session = await getUserSession(event);
5
9
  if (Object.keys(session).length > 0) {
6
10
  await sessionHooks.callHookParallel("fetch", session, event);
@@ -1,10 +1,12 @@
1
- import { defineEventHandler, createError, deleteCookie, getCookie, getQuery, sendRedirect, setCookie } from "nuxt/server";
1
+ import { defineEventHandler, createError, deleteCookie, getCookie, getQuery, getRequestURL, sendRedirect, setCookie, useRuntimeConfig } from "nuxt/server";
2
2
  import { NodeOAuthClient, OAuthCallbackError, OAuthResolverError, OAuthResponseError } from "@atproto/oauth-client-node";
3
3
  import { Agent } from "@atproto/api";
4
+ import { withoutBase } from "ufo";
4
5
  import { getAtprotoClientMetadata } from "../../utils/atproto.js";
5
6
  export function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }) {
6
7
  return defineEventHandler(async (event) => {
7
8
  const clientMetadata = getAtprotoClientMetadata(event, "bluesky", config);
9
+ if (import.meta.dev && !config?.redirectUrl) warnUnpublishedLoginPath(event, clientMetadata.redirect_uris[0]);
8
10
  const scopes = clientMetadata.scope?.split(" ") ?? [];
9
11
  const sessionStore = new SessionStore();
10
12
  const stateStore = new StateStore(event);
@@ -65,6 +67,17 @@ export function defineOAuthBlueskyEventHandler({ config, onSuccess, onError }) {
65
67
  }
66
68
  });
67
69
  }
70
+ let warnedUnpublishedLoginPath = false;
71
+ function warnUnpublishedLoginPath(event, redirectUri) {
72
+ if (warnedUnpublishedLoginPath) return;
73
+ const runtimeConfig = useRuntimeConfig();
74
+ if (runtimeConfig.oauth.bluesky.redirectUris?.length) return;
75
+ const loginPath = getRequestURL(event).pathname;
76
+ const callbackPath = new URL(redirectUri).pathname;
77
+ if (loginPath === callbackPath) return;
78
+ warnedUnpublishedLoginPath = true;
79
+ console.warn(`[nuxt-auth-utils] The Bluesky login route is served from \`${loginPath}\`, but Bluesky redirects back to \`${callbackPath}\`. Set \`runtimeConfig.oauth.bluesky.redirectUris\` to \`['${withoutBase(loginPath, runtimeConfig.app.baseURL)}']\`.`);
80
+ }
68
81
  export class StateStore {
69
82
  constructor(event) {
70
83
  this.event = event;
@@ -32,7 +32,7 @@ export function defineOAuthHerokuEventHandler({
32
32
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
33
33
  const state = await handleState(event);
34
34
  if (!query.code) {
35
- config.scope = config.scope || ["identity"];
35
+ config.scope = config.scope?.length ? config.scope : ["identity"];
36
36
  return sendRedirect(
37
37
  event,
38
38
  withQuery(config.authorizationURL, {
@@ -1,5 +1,4 @@
1
- import { defineEventHandler, getQuery, sendRedirect, useRuntimeConfig } from "nuxt/server";
2
- import { $fetch } from "ofetch";
1
+ import { createError, defineEventHandler, getQuery, sendRedirect, useRuntimeConfig } from "nuxt/server";
3
2
  import { withQuery } from "ufo";
4
3
  import defu from "defu";
5
4
  import {
@@ -37,7 +36,7 @@ export function defineOAuthHubspotEventHandler({ config, onSuccess, onError }) {
37
36
  return handleInvalidState(event, "hubspot", onError);
38
37
  }
39
38
  const tokens = await requestAccessToken(
40
- "https://api.hubapi.com/oauth/v1/token",
39
+ "https://api.hubapi.com/oauth/2026-09/token",
41
40
  {
42
41
  body: {
43
42
  client_id: config.clientId,
@@ -51,7 +50,29 @@ export function defineOAuthHubspotEventHandler({ config, onSuccess, onError }) {
51
50
  if (tokens.error) {
52
51
  return handleAccessTokenErrorResponse(event, "hubspot", tokens, onError);
53
52
  }
54
- const info = await $fetch("https://api.hubapi.com/oauth/v1/access-tokens/" + tokens.access_token);
53
+ const info = await requestAccessToken(
54
+ "https://api.hubapi.com/oauth/2026-09/token/introspect",
55
+ {
56
+ params: {
57
+ client_id: config.clientId,
58
+ client_secret: config.clientSecret,
59
+ token: tokens.access_token,
60
+ token_type_hint: "access_token"
61
+ }
62
+ }
63
+ );
64
+ if (info.error) {
65
+ return handleAccessTokenErrorResponse(event, "hubspot", info, onError);
66
+ }
67
+ if (!info.active) {
68
+ const error = createError({
69
+ status: 401,
70
+ message: "Hubspot login failed: the access token is not active",
71
+ data: info
72
+ });
73
+ if (!onError) throw error;
74
+ return onError(event, error);
75
+ }
55
76
  return onSuccess(event, {
56
77
  user: {
57
78
  id: info.user_id,
@@ -2,7 +2,7 @@ import { createError, defineEventHandler, deleteCookie, getCookie, getQuery, sen
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
4
  import { $fetch } from "ofetch";
5
- import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleState, isDevelopment, requestAccessToken } from "../utils.js";
5
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleState, isOAuthCookieSecure, requestAccessToken } from "../utils.js";
6
6
  const FLOW_COOKIE_NAME = "nuxt-auth-mastodon-flow";
7
7
  const appRegistry = /* @__PURE__ */ new Map();
8
8
  const pendingRegistrations = /* @__PURE__ */ new Map();
@@ -114,7 +114,7 @@ export function defineOAuthMastodonEventHandler({ config: userConfig, onSuccess,
114
114
  const flowState2 = { instance: instance2, clientId: app.client_id, clientSecret: app.client_secret };
115
115
  setCookie(event, FLOW_COOKIE_NAME, btoa(JSON.stringify(flowState2)), {
116
116
  httpOnly: true,
117
- secure: !isDevelopment,
117
+ secure: isOAuthCookieSecure(),
118
118
  sameSite: "lax",
119
119
  maxAge: 60 * 10,
120
120
  path: "/"
@@ -33,7 +33,7 @@ export function defineOAuthSalesforceEventHandler({
33
33
  const pkce = await handlePkceVerifier(event);
34
34
  const state = await handleState(event);
35
35
  if (!query.code) {
36
- config.scope = config.scope || ["id"];
36
+ config.scope = config.scope?.length ? config.scope : ["id"];
37
37
  return sendRedirect(
38
38
  event,
39
39
  withQuery(config.authorizationURL, {
@@ -32,7 +32,7 @@ export function defineOAuthSlackEventHandler({
32
32
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
33
33
  const state = await handleState(event);
34
34
  if (!query.code) {
35
- config.scope = config.scope || ["openid", "email", "profile"];
35
+ config.scope = config.scope?.length ? config.scope : ["openid", "email", "profile"];
36
36
  return sendRedirect(
37
37
  event,
38
38
  withQuery(config.authorizationURL, {
@@ -12,10 +12,10 @@ export function defineOAuthSteamEventHandler({ config, onSuccess, onError }) {
12
12
  if (!config.apiKey) {
13
13
  return handleMissingConfiguration(event, "steam", ["apiKey"], onError);
14
14
  }
15
- const url = getRequestURL(event);
15
+ const url = getRequestURL(event, { xForwardedProto: true });
16
16
  const state = await handleState(event, { isCallback: Boolean(query["openid.claimed_id"]) });
17
17
  if (!query["openid.claimed_id"]) {
18
- const redirectURL = withQuery(config.redirectURL || getRequestURL(event).href, { state });
18
+ const redirectURL = withQuery(config.redirectURL || url.href, { state });
19
19
  const realm = url.port ? `${url.protocol}//${url.hostname}:${url.port}` : `${url.protocol}//${url.hostname}`;
20
20
  const steamOpenIdParams = {
21
21
  "openid.ns": "http://specs.openid.net/auth/2.0",
@@ -0,0 +1,11 @@
1
+ import type { SessionConfig } from 'nuxt/server';
2
+ /**
3
+ * Name of the cookie the user session is sealed into.
4
+ */
5
+ export declare const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
6
+ /**
7
+ * The session config of the user session: the runtime config merged under `config`, without `password`.
8
+ */
9
+ export declare function resolveSessionConfig(config?: Omit<SessionConfig, 'password'>): SessionConfig & {
10
+ name: string;
11
+ };
@@ -0,0 +1,33 @@
1
+ import { useRuntimeConfig } from "nuxt/server";
2
+ import { defu } from "defu";
3
+ export const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
4
+ const APP_SESSION_COOKIE_NAME = "nuxt-session";
5
+ export function resolveSessionConfig(config = {}) {
6
+ const runtimeConfig = useRuntimeConfig();
7
+ warnLegacySessionPassword(runtimeConfig);
8
+ const { password: _password, ...finalConfig } = defu(config, runtimeConfig.session, { name: USER_SESSION_COOKIE_NAME });
9
+ if (finalConfig.name === APP_SESSION_COOKIE_NAME) {
10
+ warnAppSessionName();
11
+ finalConfig.name = USER_SESSION_COOKIE_NAME;
12
+ }
13
+ if (finalConfig.cookie?.secure === "false") {
14
+ finalConfig.cookie = { ...finalConfig.cookie, secure: false };
15
+ }
16
+ return finalConfig;
17
+ }
18
+ let warnedAppSessionName = false;
19
+ function warnAppSessionName() {
20
+ if (warnedAppSessionName) return;
21
+ warnedAppSessionName = true;
22
+ console.warn(`[nuxt-auth-utils] The session name \`${APP_SESSION_COOKIE_NAME}\` is the cookie of \`useSession()\` from \`nuxt/server\`, using \`${USER_SESSION_COOKIE_NAME}\` instead. Set another \`runtimeConfig.session.name\` (\`NUXT_SESSION_NAME\`) to rename the user session cookie.`);
23
+ }
24
+ let checkedLegacySessionPassword = false;
25
+ function warnLegacySessionPassword(runtimeConfig) {
26
+ if (checkedLegacySessionPassword) return;
27
+ checkedLegacySessionPassword = true;
28
+ const envPrefix = runtimeConfig.nitro?.envPrefix || "NUXT_";
29
+ const envKey = `${envPrefix}SESSION_PASSWORD`;
30
+ if (globalThis.process?.env?.[envKey] || runtimeConfig.session?.password) {
31
+ console.warn(`[nuxt-auth-utils] \`${envKey}\` (\`runtimeConfig.session.password\`) is not used anymore. Set \`NUXT_APP_SECRET\` instead, for example with \`openssl rand -base64 32\`. Sessions are now sealed with a secret derived from \`appSecret\`.`);
32
+ }
33
+ }
@@ -2,6 +2,7 @@ import type { RequestEvent } from 'nuxt/server';
2
2
  import * as jose from 'jose';
3
3
  import type { OAuthProvider, OnError } from '#auth-utils';
4
4
  export declare const isDevelopment: boolean;
5
+ export declare function isOAuthCookieSecure(): boolean;
5
6
  export declare function getOAuthRedirectURL(event: RequestEvent): string;
6
7
  /**
7
8
  * Request an access token body.
@@ -3,10 +3,14 @@ import { $fetch, FetchError } from "ofetch";
3
3
  import { snakeCase, upperFirst } from "scule";
4
4
  import * as jose from "jose";
5
5
  import { subtle, getRandomValues } from "uncrypto";
6
+ import { resolveSessionConfig } from "./session.js";
6
7
  export const isDevelopment = process.env.NODE_ENV === "development";
8
+ export function isOAuthCookieSecure() {
9
+ return !isDevelopment && resolveSessionConfig().cookie?.secure !== false;
10
+ }
7
11
  const OAUTH_COOKIE_MAX_AGE = 60 * 10;
8
12
  export function getOAuthRedirectURL(event) {
9
- const requestURL = getRequestURL(event);
13
+ const requestURL = getRequestURL(event, { xForwardedProto: true });
10
14
  return `${requestURL.protocol}//${requestURL.host}${requestURL.pathname}`;
11
15
  }
12
16
  export async function requestAccessToken(url, options) {
@@ -84,7 +88,7 @@ export async function handlePkceVerifier(event) {
84
88
  const verifier2 = encodeBase64Url(getRandomBytes());
85
89
  setCookie(event, "nuxt-auth-pkce", verifier2, {
86
90
  httpOnly: true,
87
- secure: !isDevelopment,
91
+ secure: isOAuthCookieSecure(),
88
92
  sameSite: "lax",
89
93
  maxAge: OAUTH_COOKIE_MAX_AGE,
90
94
  path: "/"
@@ -114,7 +118,7 @@ export async function handleState(event, options = {}) {
114
118
  const sameSite = options.sameSite || "lax";
115
119
  setCookie(event, "nuxt-auth-state", state, {
116
120
  httpOnly: true,
117
- secure: sameSite === "none" || !isDevelopment,
121
+ secure: sameSite === "none" || isOAuthCookieSecure(),
118
122
  sameSite,
119
123
  maxAge: OAUTH_COOKIE_MAX_AGE,
120
124
  path: "/"
@@ -134,7 +138,7 @@ export function handleNonce(event) {
134
138
  const nonce = encodeBase64Url(getRandomBytes());
135
139
  setCookie(event, "nuxt-auth-nonce", nonce, {
136
140
  httpOnly: true,
137
- secure: !isDevelopment,
141
+ secure: isOAuthCookieSecure(),
138
142
  sameSite: "lax",
139
143
  maxAge: OAUTH_COOKIE_MAX_AGE,
140
144
  path: "/"
@@ -13,10 +13,11 @@ export function defineWebAuthnAuthenticateEventHandler({
13
13
  onError
14
14
  }) {
15
15
  return defineEventHandler(async (event) => {
16
- const url = getRequestURL(event);
16
+ const url = getRequestURL(event, { xForwardedProto: true });
17
17
  const body = await readBody(event);
18
- const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig().webauthn.authenticate, {
19
- rpID: url.hostname
18
+ const runtimeConfig = useRuntimeConfig();
19
+ const _config = defu(await getOptions?.(event, body) ?? {}, runtimeConfig.webauthn.authenticate, {
20
+ rpID: runtimeConfig.webauthn.rpID || url.hostname
20
21
  });
21
22
  if (!storeChallenge) {
22
23
  _config.challenge = "";
@@ -47,7 +48,7 @@ export function defineWebAuthnAuthenticateEventHandler({
47
48
  response: body.response,
48
49
  expectedChallenge,
49
50
  expectedOrigin: url.origin,
50
- expectedRPID: url.hostname,
51
+ expectedRPID: _config.rpID,
51
52
  requireUserVerification: false,
52
53
  // TODO: make configurable https://simplewebauthn.dev/docs/advanced/passkeys#verifyauthenticationresponse
53
54
  credential: {
@@ -13,7 +13,7 @@ export function defineWebAuthnRegisterEventHandler({
13
13
  onError
14
14
  }) {
15
15
  return defineEventHandler(async (event) => {
16
- const url = getRequestURL(event);
16
+ const url = getRequestURL(event, { xForwardedProto: true });
17
17
  const body = await readBody(event);
18
18
  if (body.verify === void 0 || !body.user?.userName)
19
19
  throw createError({
@@ -24,9 +24,10 @@ export function defineWebAuthnRegisterEventHandler({
24
24
  if (validateUser) {
25
25
  user = await validateUserData(body.user, event, validateUser);
26
26
  }
27
- const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig().webauthn.register, {
28
- rpID: url.hostname,
29
- rpName: url.hostname,
27
+ const runtimeConfig = useRuntimeConfig();
28
+ const _config = defu(await getOptions?.(event, body) ?? {}, runtimeConfig.webauthn.register, {
29
+ rpID: runtimeConfig.webauthn.rpID || url.hostname,
30
+ rpName: runtimeConfig.webauthn.rpName || url.hostname,
30
31
  userName: user.userName,
31
32
  userDisplayName: user.displayName,
32
33
  authenticatorSelection: {
@@ -65,7 +66,7 @@ export function defineWebAuthnRegisterEventHandler({
65
66
  response: body.response,
66
67
  expectedChallenge,
67
68
  expectedOrigin: url.origin,
68
- expectedRPID: url.hostname,
69
+ expectedRPID: _config.rpID,
69
70
  requireUserVerification: false,
70
71
  // TODO: make configurable https://simplewebauthn.dev/docs/advanced/passkeys#verifyregistrationresponse
71
72
  supportedAlgorithmIDs: _config.supportedAlgorithmIDs
@@ -1,22 +1,23 @@
1
1
  import { getRequestURL, useRuntimeConfig } from "nuxt/server";
2
- import { getOAuthRedirectURL } from "../lib/utils.js";
2
+ import { withBase } from "ufo";
3
3
  import { getClientMetadataFilename } from "../../utils/atproto.js";
4
4
  export function getAtprotoClientMetadata(event, provider, config) {
5
- const providerRuntimeConfig = useRuntimeConfig().oauth[provider];
5
+ const runtimeConfig = useRuntimeConfig();
6
+ const providerRuntimeConfig = runtimeConfig.oauth[provider];
6
7
  const scopes = [.../* @__PURE__ */ new Set(["atproto", ...config?.scope ?? [], ...providerRuntimeConfig.scope ?? []])];
7
8
  const scope = scopes.join(" ");
8
9
  const grantTypes = [.../* @__PURE__ */ new Set(["authorization_code", ...providerRuntimeConfig.grantTypes ?? []])];
9
- const requestURL = getRequestURL(event);
10
+ const requestURL = getRequestURL(event, { xForwardedProto: true });
10
11
  const baseUrl = `${requestURL.protocol}//${requestURL.host}`;
11
- const redirectURL = new URL(
12
- config?.redirectUrl && baseUrl + config.redirectUrl || providerRuntimeConfig.redirectUris[0] && baseUrl + providerRuntimeConfig.redirectUris[0] || getOAuthRedirectURL(event)
13
- );
12
+ const withAppBase = (path) => withBase(path, runtimeConfig.app.baseURL);
13
+ const callbackPaths = (providerRuntimeConfig.redirectUris?.length ? providerRuntimeConfig.redirectUris : [`/auth/${provider}`]).map(withAppBase);
14
+ const redirectURL = new URL(baseUrl + (config?.redirectUrl ? withAppBase(config.redirectUrl) : callbackPaths[0]));
14
15
  const dev = import.meta.dev;
15
16
  if (dev && redirectURL.hostname === "localhost") {
16
17
  redirectURL.hostname = "127.0.0.1";
17
18
  }
18
- const redirectUris = (providerRuntimeConfig.redirectUris.length ? providerRuntimeConfig.redirectUris : [requestURL.pathname]).map((uri) => new URL(`${redirectURL.protocol}//${redirectURL.host}${uri}`).toString());
19
- const clientId = dev ? `http://localhost?redirect_uri=${encodeURIComponent(redirectURL.toString())}&scope=${encodeURIComponent(scope)}` : `${baseUrl}/${getClientMetadataFilename("bluesky", providerRuntimeConfig)}`;
19
+ const redirectUris = callbackPaths.map((uri) => new URL(`${redirectURL.protocol}//${redirectURL.host}${uri}`).toString());
20
+ const clientId = dev ? `http://localhost?redirect_uri=${encodeURIComponent(redirectURL.toString())}&scope=${encodeURIComponent(scope)}` : `${baseUrl}${withBase(`/${getClientMetadataFilename(provider, providerRuntimeConfig)}`, runtimeConfig.app.baseURL)}`;
20
21
  const clientMetadata = {
21
22
  client_name: providerRuntimeConfig.clientName || void 0,
22
23
  client_uri: providerRuntimeConfig.clientUri || void 0,
@@ -1,10 +1,7 @@
1
1
  import type { RequestEvent, SessionConfig } from 'nuxt/server';
2
2
  import type { OmitWithIndexSignature } from '../../types/utils.js';
3
3
  import type { LegacyH3Event, SessionEvent, UserSession, UserSessionRequired } from '#auth-utils';
4
- /**
5
- * Name of the cookie the user session is sealed into.
6
- */
7
- export declare const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
4
+ export { USER_SESSION_COOKIE_NAME } from '../lib/session.js';
8
5
  type UserSessionData = OmitWithIndexSignature<UserSession, 'id'>;
9
6
  /**
10
7
  * Session options, the session is always sealed with a secret derived from `appSecret`.
@@ -66,4 +63,3 @@ export declare function requireUserSession(event: SessionEvent, opts?: {
66
63
  statusCode?: number;
67
64
  message?: string;
68
65
  }): Promise<UserSessionRequired>;
69
- export {};
@@ -1,7 +1,8 @@
1
- import { createError, defineEventHandler, useRuntimeConfig, useSession } from "nuxt/server";
1
+ import { createError, defineEventHandler, useSession } from "nuxt/server";
2
2
  import { defu } from "defu";
3
3
  import { createHooks } from "hookable";
4
- export const USER_SESSION_COOKIE_NAME = "nuxt-auth-session";
4
+ import { resolveSessionConfig } from "../lib/session.js";
5
+ export { USER_SESSION_COOKIE_NAME } from "../lib/session.js";
5
6
  export const sessionHooks = createHooks();
6
7
  export async function getUserSession(event) {
7
8
  const session = await _useSession(event);
@@ -51,25 +52,11 @@ async function _useSession(event, config = {}, write = false) {
51
52
  message: "[nuxt-auth-utils] The user session is read-only in WebSocket handlers: `setUserSession`, `replaceUserSession` and `clearUserSession` need the event of an HTTP request."
52
53
  });
53
54
  }
54
- const runtimeConfig = useRuntimeConfig();
55
- warnLegacySessionPassword(runtimeConfig);
56
- const { password: _password, ...finalConfig } = defu(config, runtimeConfig.session, { name: USER_SESSION_COOKIE_NAME });
57
- if (finalConfig.name === APP_SESSION_COOKIE_NAME) {
58
- warnAppSessionName();
59
- finalConfig.name = USER_SESSION_COOKIE_NAME;
60
- }
61
- return useSession(sessionEvent, finalConfig);
55
+ return useSession(sessionEvent, resolveSessionConfig(config));
62
56
  }
63
57
  function toJSONData(data) {
64
58
  return JSON.parse(JSON.stringify(data));
65
59
  }
66
- const APP_SESSION_COOKIE_NAME = "nuxt-session";
67
- let warnedAppSessionName = false;
68
- function warnAppSessionName() {
69
- if (warnedAppSessionName) return;
70
- warnedAppSessionName = true;
71
- console.warn(`[nuxt-auth-utils] The session name \`${APP_SESSION_COOKIE_NAME}\` is the cookie of \`useSession()\` from \`nuxt/server\`, using \`${USER_SESSION_COOKIE_NAME}\` instead. Set another \`runtimeConfig.session.name\` (\`NUXT_SESSION_NAME\`) to rename the user session cookie.`);
72
- }
73
60
  const toPortableEvent = defineEventHandler((event) => event);
74
61
  function toSessionEvent(event) {
75
62
  const input = event;
@@ -94,13 +81,3 @@ function toSessionEvent(event) {
94
81
  message: "[nuxt-auth-utils] The session utils need a request event, a WebSocket peer or a WebSocket upgrade request."
95
82
  });
96
83
  }
97
- let checkedLegacySessionPassword = false;
98
- function warnLegacySessionPassword(runtimeConfig) {
99
- if (checkedLegacySessionPassword) return;
100
- checkedLegacySessionPassword = true;
101
- const envPrefix = runtimeConfig.nitro?.envPrefix || "NUXT_";
102
- const envKey = `${envPrefix}SESSION_PASSWORD`;
103
- if (globalThis.process?.env?.[envKey] || runtimeConfig.session?.password) {
104
- console.warn(`[nuxt-auth-utils] \`${envKey}\` (\`runtimeConfig.session.password\`) is not used anymore. Set \`NUXT_APP_SECRET\` instead, for example with \`openssl rand -base64 32\`. Sessions are now sealed with a secret derived from \`appSecret\`.`);
105
- }
106
- }
@@ -1,5 +1,5 @@
1
1
  import type { OAuthClientMetadata } from '@atproto/oauth-client-node';
2
- export interface AtprotoProviderClientMetadata {
2
+ export type AtprotoProviderClientMetadata = {
3
3
  /**
4
4
  * The name of the client metadata file. This is used to construct the client ID.
5
5
  * @example 'client-metadata.json'
@@ -46,10 +46,12 @@ export interface AtprotoProviderClientMetadata {
46
46
  */
47
47
  responseTypes?: OAuthClientMetadata['response_types'];
48
48
  /**
49
- * At least one redirect URI is required. The URL origin must match that of the `clientId`, so declare only the path.
49
+ * At least one redirect URI is required. The URL origin must match that of the `clientId`, so declare only the path. Paths are resolved under the app base URL.
50
+ *
51
+ * Empty by default, in which case `/auth/<provider>` under the app base URL is used.
50
52
  * @example ['/auth/callback']
51
53
  */
52
- redirectUris: OAuthClientMetadata['redirect_uris'];
54
+ redirectUris?: `/${string}`[];
53
55
  /**
54
56
  * The token endpoint authentication method. `none` is the default, and the only supported value at this time.
55
57
  * @default 'none'
@@ -59,4 +61,4 @@ export interface AtprotoProviderClientMetadata {
59
61
  * DPoP is mandatory for all clients, so this must be present and true.
60
62
  */
61
63
  dpopBoundAccessTokens?: OAuthClientMetadata['dpop_bound_access_tokens'];
62
- }
64
+ };
@@ -1,4 +1,4 @@
1
1
  export type { User, UserSession, UserSessionRequired, UserSessionComposable, SecureSessionData } from './session.js';
2
2
  export type { RequestEvent, SessionEvent, LegacyH3Event, WebSocketSessionEvent } from './event.js';
3
- export type { OAuthConfig, OAuthProvider, ATProtoProvider, OnError } from './oauth-config.js';
3
+ export type { OAuthConfig, OAuthProvider, ATProtoProvider, OAuthRuntimeConfigExtension, OnError } from './oauth-config.js';
4
4
  export type { WebAuthnCredential, WebAuthnRegisterEventHandlerOptions, WebAuthnAuthenticateEventHandlerOptions, WebAuthnComposable, WebAuthnUser, } from './webauthn.js';
@@ -2,6 +2,20 @@ import type { NuxtError, RequestEvent } from 'nuxt/server';
2
2
  export type ATProtoProvider = 'bluesky';
3
3
  export type OAuthProvider = ATProtoProvider | 'atlassian' | 'auth0' | 'authentik' | 'azureb2c' | 'battledotnet' | 'cloudflare' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'gitea' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'kick' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'mastodon' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'wechat' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | 'apple' | 'livechat' | 'salesforce' | 'slack' | 'heroku' | 'roblox' | 'okta' | 'ory' | 'shopifyCustomer' | 'oidc' | 'osu' | 'riotgames' | 'box' | 'zendesk' | (string & {});
4
4
  export type OnError = (event: RequestEvent, error: NuxtError) => unknown;
5
+ /**
6
+ * Augment this interface to type the runtime configuration of OAuth providers that are not
7
+ * shipped with the module, such as providers added by another module.
8
+ *
9
+ * ```ts
10
+ * declare module '#auth-utils' {
11
+ * interface OAuthRuntimeConfigExtension {
12
+ * myProvider: { clientId: string, clientSecret: string }
13
+ * }
14
+ * }
15
+ * ```
16
+ */
17
+ export interface OAuthRuntimeConfigExtension {
18
+ }
5
19
  export interface OAuthConfig<TConfig, TResult = {
6
20
  user: any;
7
21
  tokens: any;
@@ -0,0 +1,119 @@
1
+ import type { AtprotoProviderClientMetadata } from './atproto.js';
2
+ import type { OAuthRuntimeConfigExtension } from './oauth-config.js';
3
+ import type { LiveChatConfig } from '../server/lib/oauth/livechat.js';
4
+ import type { OAuthAppleConfig } from '../server/lib/oauth/apple.js';
5
+ import type { OAuthAtlassianConfig } from '../server/lib/oauth/atlassian.js';
6
+ import type { OAuthAuth0Config } from '../server/lib/oauth/auth0.js';
7
+ import type { OAuthAuthentikConfig } from '../server/lib/oauth/authentik.js';
8
+ import type { OAuthAzureB2CConfig } from '../server/lib/oauth/azureb2c.js';
9
+ import type { OAuthBattledotnetConfig } from '../server/lib/oauth/battledotnet.js';
10
+ import type { OAuthBoxConfig } from '../server/lib/oauth/box.js';
11
+ import type { OAuthCloudflareConfig } from '../server/lib/oauth/cloudflare.js';
12
+ import type { OAuthCognitoConfig } from '../server/lib/oauth/cognito.js';
13
+ import type { OAuthDiscordConfig } from '../server/lib/oauth/discord.js';
14
+ import type { OAuthDropboxConfig } from '../server/lib/oauth/dropbox.js';
15
+ import type { OAuthFacebookConfig } from '../server/lib/oauth/facebook.js';
16
+ import type { OAuthGitHubConfig } from '../server/lib/oauth/github.js';
17
+ import type { OAuthGitLabConfig } from '../server/lib/oauth/gitlab.js';
18
+ import type { OAuthGiteaConfig } from '../server/lib/oauth/gitea.js';
19
+ import type { OAuthGoogleConfig } from '../server/lib/oauth/google.js';
20
+ import type { OAuthHerokuConfig } from '../server/lib/oauth/heroku.js';
21
+ import type { OAuthHubspotConfig } from '../server/lib/oauth/hubspot.js';
22
+ import type { OAuthInstagramConfig } from '../server/lib/oauth/instagram.js';
23
+ import type { OAuthKeycloakConfig } from '../server/lib/oauth/keycloak.js';
24
+ import type { OAuthKickConfig } from '../server/lib/oauth/kick.js';
25
+ import type { OAuthLineConfig } from '../server/lib/oauth/line.js';
26
+ import type { OAuthLinearConfig } from '../server/lib/oauth/linear.js';
27
+ import type { OAuthLinkedInConfig } from '../server/lib/oauth/linkedin.js';
28
+ import type { OAuthMastodonConfig } from '../server/lib/oauth/mastodon.js';
29
+ import type { OAuthMicrosoftConfig } from '../server/lib/oauth/microsoft.js';
30
+ import type { OAuthOidcConfig } from '../server/lib/oauth/oidc.js';
31
+ import type { OAuthOktaConfig } from '../server/lib/oauth/okta.js';
32
+ import type { OAuthOryConfig } from '../server/lib/oauth/ory.js';
33
+ import type { OAuthOsuConfig } from '../server/lib/oauth/osu.js';
34
+ import type { OAuthPaypalConfig } from '../server/lib/oauth/paypal.js';
35
+ import type { OAuthPolarConfig } from '../server/lib/oauth/polar.js';
36
+ import type { OAuthRiotGamesConfig } from '../server/lib/oauth/riotgames.js';
37
+ import type { OAuthRobloxConfig } from '../server/lib/oauth/roblox.js';
38
+ import type { OAuthSalesforceConfig } from '../server/lib/oauth/salesforce.js';
39
+ import type { OAuthSeznamConfig } from '../server/lib/oauth/seznam.js';
40
+ import type { OAuthShopifyCustomerConfig } from '../server/lib/oauth/shopifyCustomer.js';
41
+ import type { OAuthSlackConfig } from '../server/lib/oauth/slack.js';
42
+ import type { OAuthSpotifyConfig } from '../server/lib/oauth/spotify.js';
43
+ import type { OAuthSteamConfig } from '../server/lib/oauth/steam.js';
44
+ import type { OAuthStravaConfig } from '../server/lib/oauth/strava.js';
45
+ import type { OAuthTikTokConfig } from '../server/lib/oauth/tiktok.js';
46
+ import type { OAuthTwitchConfig } from '../server/lib/oauth/twitch.js';
47
+ import type { OAuthVKConfig } from '../server/lib/oauth/vk.js';
48
+ import type { OAuthWechatConfig } from '../server/lib/oauth/wechat.js';
49
+ import type { OAuthWorkOSConfig } from '../server/lib/oauth/workos.js';
50
+ import type { OAuthXConfig } from '../server/lib/oauth/x.js';
51
+ import type { OAuthXSUAAConfig } from '../server/lib/oauth/xsuaa.js';
52
+ import type { OAuthYandexConfig } from '../server/lib/oauth/yandex.js';
53
+ import type { OAuthZendeskConfig } from '../server/lib/oauth/zendesk.js';
54
+ import type { OAuthZitadelConfig } from '../server/lib/oauth/zitadel.js';
55
+ /**
56
+ * Runtime configuration of every OAuth provider supported by the module, exposed as `runtimeConfig.oauth`.
57
+ *
58
+ * Keys are the provider names accepted by `defineOAuth<Provider>EventHandler()`, and they are always
59
+ * set by the module, so server code can read them without checking for `undefined`. In `nuxt.config`,
60
+ * `runtimeConfig` is deep-partial, so you only write the providers you use.
61
+ * Providers contributed by other modules can add themselves through the `OAuthRuntimeConfigExtension`
62
+ * interface, and any other key stays `unknown`.
63
+ */
64
+ export interface OAuthRuntimeConfig extends OAuthRuntimeConfigExtension {
65
+ [provider: string]: unknown;
66
+ apple: OAuthAppleConfig;
67
+ atlassian: OAuthAtlassianConfig;
68
+ auth0: OAuthAuth0Config;
69
+ authentik: OAuthAuthentikConfig;
70
+ azureb2c: OAuthAzureB2CConfig;
71
+ battledotnet: OAuthBattledotnetConfig;
72
+ bluesky: AtprotoProviderClientMetadata;
73
+ box: OAuthBoxConfig;
74
+ cloudflare: OAuthCloudflareConfig;
75
+ cognito: OAuthCognitoConfig;
76
+ discord: OAuthDiscordConfig;
77
+ dropbox: OAuthDropboxConfig;
78
+ facebook: OAuthFacebookConfig;
79
+ gitea: OAuthGiteaConfig;
80
+ github: OAuthGitHubConfig;
81
+ gitlab: OAuthGitLabConfig;
82
+ google: OAuthGoogleConfig;
83
+ heroku: OAuthHerokuConfig;
84
+ hubspot: OAuthHubspotConfig;
85
+ instagram: OAuthInstagramConfig;
86
+ keycloak: OAuthKeycloakConfig;
87
+ kick: OAuthKickConfig;
88
+ line: OAuthLineConfig;
89
+ linear: OAuthLinearConfig;
90
+ linkedin: OAuthLinkedInConfig;
91
+ livechat: LiveChatConfig;
92
+ mastodon: OAuthMastodonConfig;
93
+ microsoft: OAuthMicrosoftConfig;
94
+ oidc: OAuthOidcConfig;
95
+ okta: OAuthOktaConfig;
96
+ ory: OAuthOryConfig;
97
+ osu: OAuthOsuConfig;
98
+ paypal: OAuthPaypalConfig;
99
+ polar: OAuthPolarConfig;
100
+ riotgames: OAuthRiotGamesConfig;
101
+ roblox: OAuthRobloxConfig;
102
+ salesforce: OAuthSalesforceConfig;
103
+ seznam: OAuthSeznamConfig;
104
+ shopifyCustomer: OAuthShopifyCustomerConfig;
105
+ slack: OAuthSlackConfig;
106
+ spotify: OAuthSpotifyConfig;
107
+ steam: OAuthSteamConfig;
108
+ strava: OAuthStravaConfig;
109
+ tiktok: OAuthTikTokConfig;
110
+ twitch: OAuthTwitchConfig;
111
+ vk: OAuthVKConfig;
112
+ wechat: OAuthWechatConfig;
113
+ workos: OAuthWorkOSConfig;
114
+ x: OAuthXConfig;
115
+ xsuaa: OAuthXSUAAConfig;
116
+ yandex: OAuthYandexConfig;
117
+ zendesk: OAuthZendeskConfig;
118
+ zitadel: OAuthZitadelConfig;
119
+ }
File without changes
@@ -1,4 +1,5 @@
1
1
  import type { ComputedRef, Ref } from 'vue';
2
+ import type { OmitWithIndexSignature } from './utils.js';
2
3
  export interface User {
3
4
  }
4
5
  export interface SecureSessionData {
@@ -38,9 +39,11 @@ export interface UserSessionComposable {
38
39
  */
39
40
  user: ComputedRef<User | null>;
40
41
  /**
41
- * The session object.
42
+ * The session object, without `id` for a visitor who has no session yet.
42
43
  */
43
- session: Ref<UserSession | null>;
44
+ session: Ref<(OmitWithIndexSignature<UserSession, 'id'> & {
45
+ id?: string;
46
+ }) | null>;
44
47
  /**
45
48
  * Fetch the user session from the server.
46
49
  */
@@ -10,8 +10,7 @@ export const atprotoProviderDefaultClientMetadata = {
10
10
  grantTypes: ["authorization_code"],
11
11
  responseTypes: ["code"],
12
12
  applicationType: "web",
13
- // @ts-expect-error TypeScript is too smart for its own good
14
- redirectUris: void 0,
13
+ redirectUris: [],
15
14
  dpopBoundAccessTokens: true,
16
15
  tokenEndpointAuthMethod: "none"
17
16
  };
package/dist/types.d.mts CHANGED
@@ -1,3 +1,3 @@
1
1
  export { default } from './module.mjs'
2
2
 
3
- export { type ModuleOptions } from './module.mjs'
3
+ export { type ModuleOptions, type WebAuthnOptions } from './module.mjs'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.6.0",
3
+ "version": "0.6.2",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",