nuxt-auth-utils 0.5.7 → 0.5.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -23,6 +23,7 @@ Add Authentication to Nuxt applications with secured & sealed cookies sessions.
23
23
  - [Tree-shakable server utils](#server-utils)
24
24
  - [`<AuthState>` component](#authstate-component)
25
25
  - [Extendable with hooks](#extend-session)
26
+ - [WebSocket support](#websockets-support)
26
27
 
27
28
  It has few dependencies (only from [UnJS](https://github.com/unjs)), run on multiple JS environments (Node, Deno, Workers) and is fully typed with TypeScript.
28
29
 
@@ -204,6 +205,7 @@ It can also be set using environment variables:
204
205
 
205
206
  #### Supported OAuth Providers
206
207
 
208
+ - Atlassian
207
209
  - Auth0
208
210
  - Authentik
209
211
  - AWS Cognito
@@ -217,6 +219,7 @@ It can also be set using environment variables:
217
219
  - Hubspot
218
220
  - Instagram
219
221
  - Keycloak
222
+ - Line
220
223
  - Linear
221
224
  - LinkedIn
222
225
  - Microsoft
@@ -594,6 +597,65 @@ You can use the `placeholder` slot to show a placeholder on server-side and whil
594
597
 
595
598
  If you are caching your routes with `routeRules`, please make sure to use [Nitro](https://github.com/unjs/nitro) >= `2.9.7` to support the client-side fetching of the user session.
596
599
 
600
+ ## WebSockets Support
601
+
602
+ Nuxt Auth Utils is compatible with [Nitro WebSockets](https://nitro.build/guide/websocket).
603
+
604
+ Make sure to enable the `experimental.websocket` option in your `nuxt.config.ts`:
605
+
606
+ ```ts
607
+ export default defineNuxtConfig({
608
+ nitro: {
609
+ experimental: {
610
+ websocket: true
611
+ }
612
+ }
613
+ })
614
+ ```
615
+
616
+ You can use the `requireUserSession` function in the `upgrade` function to check if the user is authenticated before upgrading the WebSocket connection.
617
+
618
+ ```ts
619
+ // server/routes/ws.ts
620
+ export default defineWebSocketHandler({
621
+ async upgrade(request) {
622
+ // Make sure the user is authenticated before upgrading the WebSocket connection
623
+ await requireUserSession(request)
624
+ },
625
+ async open(peer) {
626
+ const { user } = await requireUserSession(peer)
627
+ const username = Object.values(user).filter(Boolean).join(' ')
628
+ peer.send(`Hello, ${username}!`)
629
+ },
630
+ message(peer, message) {
631
+ peer.send(`Echo: ${message}`)
632
+ },
633
+ })
634
+ ```
635
+
636
+ Then, in your application, you can use the [useWebSocket](https://vueuse.org/core/useWebSocket/) composable to connect to the WebSocket:
637
+
638
+ ```vue
639
+ <script setup>
640
+ const { status, data, send, open, close } = useWebSocket('/ws', { immediate: false })
641
+
642
+ // Only open the websocket after the page is hydrated (client-only)
643
+ onMounted(open)
644
+ </script>
645
+
646
+ <template>
647
+ <div>
648
+ <p>Status: {{ status }}</p>
649
+ <p>Data: {{ data }}</p>
650
+ <p>
651
+ <button @click="open">Open</button>
652
+ <button @click="close(1000, 'Closing')">Close</button>
653
+ <button @click="send('hello')">Send hello</button>
654
+ </p>
655
+ </div>
656
+ </template>
657
+ ```
658
+
597
659
  ## Configuration
598
660
 
599
661
  We leverage `runtimeConfig.session` to give the defaults option to [h3 `useSession`](https://h3.unjs.io/examples/handle-session).
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.7",
4
+ "version": "0.5.9",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -33,6 +33,9 @@ const module = defineNuxtModule({
33
33
  addPlugin(resolver.resolve("./runtime/app/plugins/session.client"));
34
34
  addServerPlugin(resolver.resolve("./runtime/server/plugins/oauth"));
35
35
  addServerImportsDir(resolver.resolve("./runtime/server/lib/oauth"));
36
+ if (nuxt.options.nitro?.experimental?.websocket) {
37
+ addServerPlugin(resolver.resolve("./runtime/server/plugins/ws"));
38
+ }
36
39
  if (options.webAuthn) {
37
40
  const missingDeps = [];
38
41
  const peerDeps = ["@simplewebauthn/server", "@simplewebauthn/browser"];
@@ -102,7 +105,8 @@ const module = defineNuxtModule({
102
105
  runtimeConfig.oauth.gitlab = defu(runtimeConfig.oauth.gitlab, {
103
106
  clientId: "",
104
107
  clientSecret: "",
105
- redirectURL: ""
108
+ redirectURL: "",
109
+ baseURL: "https://gitlab.com"
106
110
  });
107
111
  runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
108
112
  clientId: "",
@@ -260,6 +264,16 @@ const module = defineNuxtModule({
260
264
  clientSecret: "",
261
265
  redirectURL: ""
262
266
  });
267
+ runtimeConfig.oauth.line = defu(runtimeConfig.oauth.line, {
268
+ clientId: "",
269
+ clientSecret: "",
270
+ redirectURL: ""
271
+ });
272
+ runtimeConfig.oauth.atlassian = defu(runtimeConfig.oauth.atlassian, {
273
+ clientId: "",
274
+ clientSecret: "",
275
+ redirectURL: ""
276
+ });
263
277
  }
264
278
  });
265
279
 
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
2
  export default _default;
@@ -1,2 +1,2 @@
1
- declare const _default: any;
1
+ declare const _default: import("nuxt/app").Plugin<Record<string, unknown>> & import("nuxt/app").ObjectPlugin<Record<string, unknown>>;
2
2
  export default _default;
@@ -0,0 +1,66 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ /**
3
+ * @see https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps
4
+ */
5
+ export interface OAuthAtlassianConfig {
6
+ /**
7
+ * Atlassian OAuth Client ID
8
+ * @default process.env.NUXT_OAUTH_ATLASSIAN_CLIENT_ID
9
+ * @see https://developer.atlassian.com/console/myapps
10
+ */
11
+ clientId?: string;
12
+ /**
13
+ * Atlassian OAuth Client Secret
14
+ * @default process.env.NUXT_OAUTH_ATLASSIAN_CLIENT_SECRET
15
+ * @see https://developer.atlassian.com/console/myapps
16
+ */
17
+ clientSecret?: string;
18
+ /**
19
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
20
+ * @default process.env.NUXT_OAUTH_ATLASSIAN_REDIRECT_URL or current URL
21
+ * @see https://developer.atlassian.com/console/myapps
22
+ */
23
+ redirectURL?: string;
24
+ /**
25
+ * Atlassian OAuth Scope
26
+ * @default ['read:me', 'read:account']
27
+ * @see [Jira scopes](https://developer.atlassian.com/cloud/jira/platform/scopes-for-oauth-2-3LO-and-forge-apps) | [Confluence scopes](https://developer.atlassian.com/cloud/confluence/scopes-for-oauth-2-3LO-and-forge-apps)
28
+ *
29
+ * @example
30
+ * User identity API: ['read:me', 'read:account']
31
+ * Confluence API: ['read:confluence-user']
32
+ * BRIE API: ['read:account:brie]
33
+ * Jira platform REST API: ['read:jira-user']
34
+ * Personal data reporting API: ['report:personal-data']
35
+ */
36
+ scope?: string[];
37
+ /**
38
+ * Atlassian OAuth Audience URL
39
+ * @default 'https://api.atlassian.com'
40
+ */
41
+ audienceURL?: string;
42
+ /**
43
+ * Atlassian OAuth Authorization URL
44
+ * @default 'https://auth.atlassian.com/authorize'
45
+ */
46
+ authorizationURL?: string;
47
+ /**
48
+ * Atlassian OAuth Token URL
49
+ * @default 'https://auth.atlassian.com/oauth/token'
50
+ */
51
+ tokenURL?: string;
52
+ /**
53
+ * Require email from user, adds the ['read:me'] scope if not present
54
+ * @default false
55
+ */
56
+ emailHasToBeVerified?: boolean;
57
+ /**
58
+ * Extra authorization parameters to provide to the authorization URL
59
+ * @default {}
60
+ */
61
+ authorizationParams?: Record<string, string>;
62
+ }
63
+ /**
64
+ * Atlassian User identity, Confluence, BRIE, Jira platform, Atlassian Personal data reporting
65
+ */
66
+ export declare function defineOAuthAtlassianEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthAtlassianConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,99 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { randomUUID } from "uncrypto";
5
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
6
+ import { useRuntimeConfig, createError } from "#imports";
7
+ export function defineOAuthAtlassianEventHandler({
8
+ config,
9
+ onSuccess,
10
+ onError
11
+ }) {
12
+ return eventHandler(async (event) => {
13
+ config = defu(config, useRuntimeConfig().oauth?.atlassian, {
14
+ authorizationURL: "https://auth.atlassian.com/authorize",
15
+ tokenURL: "https://auth.atlassian.com/oauth/token",
16
+ audienceURL: "https://api.atlassian.com",
17
+ scope: ["read:me", "read:account"],
18
+ authorizationParams: {}
19
+ });
20
+ if (!config.clientId || !config.clientSecret) {
21
+ return handleMissingConfiguration(event, "atlassian", ["clientId", "clientSecret"], onError);
22
+ }
23
+ if (config.scope?.length === 0) {
24
+ config.scope = ["read:me"];
25
+ }
26
+ if (config.emailHasToBeVerified && !config.scope?.includes("read:me")) {
27
+ config.scope?.push("read:me");
28
+ }
29
+ const query = getQuery(event);
30
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
31
+ if (!query.code) {
32
+ return sendRedirect(
33
+ event,
34
+ withQuery(config.authorizationURL, {
35
+ audience: config.audienceURL,
36
+ client_id: config.clientId,
37
+ scope: config.scope?.join(" "),
38
+ redirect_uri: redirectURL,
39
+ state: randomUUID(),
40
+ response_type: "code",
41
+ prompt: "consent",
42
+ ...config.authorizationParams
43
+ })
44
+ );
45
+ }
46
+ if (query.error) {
47
+ const error = createError({
48
+ statusCode: 401,
49
+ message: `Atlassian login failed: ${query.error || "Unknown error"}`,
50
+ data: query
51
+ });
52
+ if (!onError) throw error;
53
+ return onError(event, error);
54
+ }
55
+ const tokens = await requestAccessToken(config.tokenURL, {
56
+ headers: {
57
+ "Content-Type": "application/json"
58
+ },
59
+ body: {
60
+ grant_type: "authorization_code",
61
+ client_id: config.clientId,
62
+ client_secret: config.clientSecret,
63
+ code: query.code,
64
+ redirect_uri: redirectURL
65
+ }
66
+ });
67
+ if (tokens.error || !tokens.access_token) {
68
+ return handleAccessTokenErrorResponse(event, "atlassian", tokens, onError);
69
+ }
70
+ const user = await $fetch("https://api.atlassian.com/me", {
71
+ headers: {
72
+ "Authorization": `Bearer ${tokens.access_token}`,
73
+ "Content-Type": "application/json"
74
+ }
75
+ });
76
+ if (user.account_status === "inactive") {
77
+ const error = createError({
78
+ statusCode: 403,
79
+ statusMessage: "Atlassian account is inactive",
80
+ data: { accountStatus: user.account_status }
81
+ });
82
+ if (!onError) throw error;
83
+ return onError(event, error);
84
+ }
85
+ if (!user.email_verified) {
86
+ const error = createError({
87
+ statusCode: 400,
88
+ statusMessage: "Email address is not verified",
89
+ data: { email: user.email }
90
+ });
91
+ if (!onError) throw error;
92
+ return onError(event, error);
93
+ }
94
+ return onSuccess(event, {
95
+ user,
96
+ tokens
97
+ });
98
+ });
99
+ }
@@ -15,6 +15,9 @@ export function defineOAuthDiscordEventHandler({ config, onSuccess, onError }) {
15
15
  if (!config.clientId || !config.clientSecret) {
16
16
  return handleMissingConfiguration(event, "discord", ["clientId", "clientSecret"], onError);
17
17
  }
18
+ if (query.error) {
19
+ return handleAccessTokenErrorResponse(event, "discord", query, onError);
20
+ }
18
21
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
19
22
  if (!query.code) {
20
23
  config.scope = config.scope || [];
@@ -1,8 +1,8 @@
1
- import { eventHandler, getQuery, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
4
  import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
- import { useRuntimeConfig, createError } from "#imports";
5
+ import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthFacebookEventHandler({
7
7
  config,
8
8
  onSuccess,
@@ -58,7 +58,13 @@ export function defineOAuthFacebookEventHandler({
58
58
  `https://graph.facebook.com/v19.0/me?fields=${fields}&access_token=${accessToken}`
59
59
  );
60
60
  if (!user) {
61
- throw new Error("Facebook login failed: no user found");
61
+ const error = createError({
62
+ statusCode: 500,
63
+ message: "Could not get Facebook user",
64
+ data: tokens
65
+ });
66
+ if (!onError) throw error;
67
+ return onError(event, error);
62
68
  }
63
69
  return onSuccess(event, {
64
70
  user,
@@ -1,8 +1,8 @@
1
- import { eventHandler, getQuery, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
4
  import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
- import { useRuntimeConfig, createError } from "#imports";
5
+ import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
8
8
  config = defu(config, useRuntimeConfig(event).oauth?.github, {
@@ -67,7 +67,13 @@ export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
67
67
  });
68
68
  const primaryEmail = emails.find((email) => email.primary);
69
69
  if (!primaryEmail) {
70
- throw new Error("GitHub login failed: no user email found");
70
+ const error = createError({
71
+ statusCode: 500,
72
+ message: "Could not get GitHub user email",
73
+ data: tokens
74
+ });
75
+ if (!onError) throw error;
76
+ return onError(event, error);
71
77
  }
72
78
  user.email = primaryEmail.email;
73
79
  }
@@ -43,5 +43,10 @@ export interface OAuthGitLabConfig {
43
43
  * @default process.env.NUXT_OAUTH_GITLAB_REDIRECT_URL
44
44
  */
45
45
  redirectURL?: string;
46
+ /**
47
+ * URL of your instance, if you are self-hosting
48
+ * @default https://gitlab.com
49
+ */
50
+ baseURL?: string;
46
51
  }
47
52
  export declare function defineOAuthGitLabEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthGitLabConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -14,9 +14,11 @@ export function defineOAuthGitLabEventHandler({
14
14
  onError
15
15
  }) {
16
16
  return eventHandler(async (event) => {
17
- config = defu(config, useRuntimeConfig(event).oauth?.gitlab, {
18
- authorizationURL: "https://gitlab.com/oauth/authorize",
19
- tokenURL: "https://gitlab.com/oauth/token",
17
+ const runtimeConfig = useRuntimeConfig(event).oauth?.gitlab;
18
+ const baseURL = config?.baseURL ?? runtimeConfig.baseURL ?? "https://gitlab.com";
19
+ config = defu(config, runtimeConfig, {
20
+ authorizationURL: `${baseURL}/oauth/authorize`,
21
+ tokenURL: `${baseURL}/oauth/token`,
20
22
  authorizationParams: {}
21
23
  });
22
24
  const query = getQuery(event);
@@ -70,7 +72,7 @@ export function defineOAuthGitLabEventHandler({
70
72
  return handleAccessTokenErrorResponse(event, "gitlab", tokens, onError);
71
73
  }
72
74
  const accessToken = tokens.access_token;
73
- const user = await $fetch("https://gitlab.com/api/v4/user", {
75
+ const user = await $fetch(`${baseURL}/api/v4/user`, {
74
76
  headers: {
75
77
  Authorization: `Bearer ${accessToken}`
76
78
  }
@@ -1,8 +1,8 @@
1
- import { eventHandler, getQuery, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
4
  import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
- import { useRuntimeConfig, createError } from "#imports";
5
+ import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthInstagramEventHandler({
7
7
  config,
8
8
  onSuccess,
@@ -64,7 +64,13 @@ export function defineOAuthInstagramEventHandler({
64
64
  `https://graph.instagram.com/v21.0/me?fields=${fields}&access_token=${accessToken}`
65
65
  );
66
66
  if (!user) {
67
- throw new Error("Instagram login failed: no user found");
67
+ const error = createError({
68
+ statusCode: 500,
69
+ message: "Could not get Instagram user",
70
+ data: tokens
71
+ });
72
+ if (!onError) throw error;
73
+ return onError(event, error);
68
74
  }
69
75
  return onSuccess(event, {
70
76
  user,
@@ -0,0 +1,45 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthLineConfig {
3
+ /**
4
+ * Line OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_LINE_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Line OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_LINE_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Line OAuth Scope
15
+ * @default ['profile', 'openid']
16
+ * @see https://developers.line.biz/en/docs/line-login/integrate-line-login/
17
+ */
18
+ scope?: string[];
19
+ /**
20
+ * Line OAuth Authorization URL
21
+ * @default 'https://access.line.me/oauth2/v2.1/authorize'
22
+ */
23
+ authorizationURL?: string;
24
+ /**
25
+ * Line OAuth Token URL
26
+ * @default 'https://api.line.me/oauth2/v2.1/token'
27
+ */
28
+ tokenURL?: string;
29
+ /**
30
+ * Line OAuth User Info URL
31
+ * @default 'https://api.line.me/v2/profile'
32
+ */
33
+ userURL?: string;
34
+ /**
35
+ * Extra authorization parameters to provide to the authorization URL
36
+ * @example { bot_prompt: 'normal' }
37
+ */
38
+ authorizationParams?: Record<string, string>;
39
+ /**
40
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
41
+ * @default process.env.NUXT_OAUTH_LINE_REDIRECT_URL or current URL
42
+ */
43
+ redirectURL?: string;
44
+ }
45
+ export declare function defineOAuthLineEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthLineConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,75 @@
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthLineEventHandler({
7
+ config,
8
+ onSuccess,
9
+ onError
10
+ }) {
11
+ return eventHandler(async (event) => {
12
+ config = defu(config, useRuntimeConfig(event).oauth?.line, {
13
+ authorizationURL: "https://access.line.me/oauth2/v2.1/authorize",
14
+ tokenURL: "https://api.line.me/oauth2/v2.1/token",
15
+ userURL: "https://api.line.me/v2/profile",
16
+ authorizationParams: {}
17
+ });
18
+ const query = getQuery(event);
19
+ if (query.error) {
20
+ return onError(
21
+ event,
22
+ new Error(`Line login failed: ${query.error || "Unknown error"}`)
23
+ );
24
+ }
25
+ if (!config.clientId || !config.clientSecret) {
26
+ return handleMissingConfiguration(event, "line", ["clientId", "clientSecret"], onError);
27
+ }
28
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
29
+ if (!query.code) {
30
+ config.scope = config.scope || ["profile", "openid"];
31
+ return sendRedirect(
32
+ event,
33
+ withQuery(config.authorizationURL, {
34
+ response_type: "code",
35
+ client_id: config.clientId,
36
+ redirect_uri: redirectURL,
37
+ scope: config.scope.join(" "),
38
+ state: query.state || "",
39
+ ...config.authorizationParams
40
+ })
41
+ );
42
+ }
43
+ const tokens = await requestAccessToken(config.tokenURL, {
44
+ body: {
45
+ grant_type: "authorization_code",
46
+ code: query.code,
47
+ client_id: config.clientId,
48
+ client_secret: config.clientSecret,
49
+ redirect_uri: redirectURL
50
+ }
51
+ });
52
+ if (tokens.error) {
53
+ return handleAccessTokenErrorResponse(event, "line", tokens, onError);
54
+ }
55
+ const accessToken = tokens.access_token;
56
+ const user = await $fetch(config.userURL, {
57
+ headers: {
58
+ Authorization: `Bearer ${accessToken}`
59
+ }
60
+ });
61
+ if (!user) {
62
+ const error = createError({
63
+ statusCode: 500,
64
+ message: "Could not get Line user",
65
+ data: tokens
66
+ });
67
+ if (!onError) throw error;
68
+ return onError(event, error);
69
+ }
70
+ return onSuccess(event, {
71
+ tokens,
72
+ user
73
+ });
74
+ });
75
+ }
@@ -1,5 +0,0 @@
1
- import type { WebAuthnAuthenticateEventHandlerOptions, WebAuthnCredential } from '#auth-utils';
2
- export declare function defineWebAuthnAuthenticateEventHandler<T extends WebAuthnCredential>({ storeChallenge, getChallenge, getCredential, allowCredentials, getOptions, onSuccess, onError, }: WebAuthnAuthenticateEventHandlerOptions<T>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void | import("@simplewebauthn/server").VerifiedAuthenticationResponse | {
3
- requestOptions: import("@simplewebauthn/types").PublicKeyCredentialRequestOptionsJSON;
4
- attemptId: string;
5
- }>>;
@@ -1,8 +0,0 @@
1
- import type { H3Event } from 'h3';
2
- import type { WebAuthnUser, WebAuthnRegisterEventHandlerOptions } from '#auth-utils';
3
- import type { ValidateUserFunction } from '~/src/runtime/types/webauthn';
4
- export declare function defineWebAuthnRegisterEventHandler<T extends WebAuthnUser>({ storeChallenge, getChallenge, getOptions, validateUser, excludeCredentials, onSuccess, onError, }: WebAuthnRegisterEventHandlerOptions<T>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void | import("@simplewebauthn/server").VerifiedRegistrationResponse | {
5
- creationOptions: import("@simplewebauthn/types").PublicKeyCredentialCreationOptionsJSON;
6
- attemptId: string;
7
- }>>;
8
- export declare function validateUserData<T>(userBody: WebAuthnUser, event: H3Event, fn: ValidateUserFunction<T>): Promise<T>;
@@ -1,2 +1,2 @@
1
- declare const _default: import("nitropack").NitroAppPlugin;
1
+ declare const _default: import("nitropack/types").NitroAppPlugin;
2
2
  export default _default;
@@ -0,0 +1,2 @@
1
+ declare const _default: import("nitropack/types").NitroAppPlugin;
2
+ export default _default;
@@ -0,0 +1,7 @@
1
+ import { defineNitroPlugin } from "nitropack/runtime";
2
+ import { getUserSession } from "../utils/session.js";
3
+ export default defineNitroPlugin((nitroApp) => {
4
+ nitroApp.hooks.hook("request", async (event) => {
5
+ await getUserSession(event);
6
+ });
7
+ });
@@ -1,5 +1,7 @@
1
1
  import type { H3Event, SessionConfig } from 'h3';
2
+ import { useSession } from 'h3';
2
3
  import type { UserSession, UserSessionRequired } from '#auth-utils';
4
+ type UseSessionEvent = Parameters<typeof useSession>[0];
3
5
  export interface SessionHooks {
4
6
  /**
5
7
  * Called when fetching the session from the API
@@ -18,7 +20,7 @@ export declare const sessionHooks: import("hookable").Hookable<SessionHooks, imp
18
20
  * @param event The Request (h3) event
19
21
  * @returns The user session
20
22
  */
21
- export declare function getUserSession(event: H3Event): Promise<UserSession>;
23
+ export declare function getUserSession(event: UseSessionEvent): Promise<UserSession>;
22
24
  /**
23
25
  * Set a user session
24
26
  * @param event The Request (h3) event
@@ -46,7 +48,8 @@ export declare function clearUserSession(event: H3Event, config?: Partial<Sessio
46
48
  * @param opts.message The message to use for the error (defaults to Unauthorized)
47
49
  * @see https://github.com/atinux/nuxt-auth-utils
48
50
  */
49
- export declare function requireUserSession(event: H3Event, opts?: {
51
+ export declare function requireUserSession(event: UseSessionEvent, opts?: {
50
52
  statusCode?: number;
51
53
  message?: string;
52
54
  }): Promise<UserSessionRequired>;
55
+ export {};
@@ -1,4 +1,4 @@
1
- import { useSession, createError } from "h3";
1
+ import { useSession, createError, isEvent } from "h3";
2
2
  import { defu } from "defu";
3
3
  import { createHooks } from "hookable";
4
4
  import { useRuntimeConfig } from "#imports";
@@ -26,17 +26,23 @@ export async function clearUserSession(event, config) {
26
26
  export async function requireUserSession(event, opts = {}) {
27
27
  const userSession = await getUserSession(event);
28
28
  if (!userSession.user) {
29
- throw createError({
30
- statusCode: opts.statusCode || 401,
31
- message: opts.message || "Unauthorized"
32
- });
29
+ if (isEvent(event)) {
30
+ throw createError({
31
+ statusCode: opts.statusCode || 401,
32
+ message: opts.message || "Unauthorized"
33
+ });
34
+ } else {
35
+ throw new Response(opts.message || "Unauthorized", {
36
+ status: opts.statusCode || 401
37
+ });
38
+ }
33
39
  }
34
40
  return userSession;
35
41
  }
36
42
  let sessionConfig;
37
43
  function _useSession(event, config = {}) {
38
44
  if (!sessionConfig) {
39
- const runtimeConfig = useRuntimeConfig(event);
45
+ const runtimeConfig = useRuntimeConfig(isEvent(event) ? event : void 0);
40
46
  const envSessionPassword = `${runtimeConfig.nitro?.envPrefix || "NUXT_"}SESSION_PASSWORD`;
41
47
  sessionConfig = defu({ password: process.env[envSessionPassword] }, runtimeConfig.session);
42
48
  }
@@ -1,5 +1,5 @@
1
1
  import type { H3Event, H3Error } from 'h3';
2
- export type OAuthProvider = 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'keycloak' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | (string & {});
2
+ export type OAuthProvider = 'atlassian' | 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'github' | 'gitlab' | 'google' | 'hubspot' | 'instagram' | 'keycloak' | 'line' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'seznam' | 'steam' | 'strava' | 'tiktok' | 'twitch' | 'vk' | 'workos' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | (string & {});
3
3
  export type OnError = (event: H3Event, error: H3Error) => Promise<void> | void;
4
4
  export interface OAuthConfig<TConfig, TUser = any, TTokens = any> {
5
5
  config?: TConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
4
4
  "description": "Add Authentication to Nuxt applications with secured & sealed cookies sessions.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "license": "MIT",
10
10
  "type": "module",
11
- "packageManager": "pnpm@9.15.0",
11
+ "packageManager": "pnpm@9.15.4",
12
12
  "exports": {
13
13
  ".": {
14
14
  "types": "./dist/types.d.ts",
@@ -36,14 +36,14 @@
36
36
  },
37
37
  "dependencies": {
38
38
  "@adonisjs/hash": "^9.0.5",
39
- "@nuxt/kit": "^3.14.1592",
39
+ "@nuxt/kit": "^3.15.2",
40
40
  "defu": "^6.1.4",
41
- "h3": "^1.13.0",
41
+ "h3": "^1.14.0",
42
42
  "hookable": "^5.5.3",
43
43
  "ofetch": "^1.4.1",
44
44
  "ohash": "^1.1.4",
45
45
  "openid-client": "^6.1.7",
46
- "pathe": "^1.1.2",
46
+ "pathe": "^2.0.2",
47
47
  "scule": "^1.3.0",
48
48
  "uncrypto": "^0.1.3"
49
49
  },
@@ -60,20 +60,20 @@
60
60
  }
61
61
  },
62
62
  "devDependencies": {
63
- "@iconify-json/simple-icons": "^1.2.15",
63
+ "@iconify-json/simple-icons": "^1.2.21",
64
64
  "@nuxt/devtools": "latest",
65
- "@nuxt/eslint-config": "^0.7.3",
65
+ "@nuxt/eslint-config": "^0.7.5",
66
66
  "@nuxt/module-builder": "^0.8.4",
67
- "@nuxt/schema": "^3.14.1592",
68
- "@nuxt/test-utils": "^3.15.1",
69
- "@nuxt/ui": "^2.20.0",
70
- "@nuxt/ui-pro": "^1.6.0",
71
- "@simplewebauthn/types": "^11.0.0",
67
+ "@nuxt/schema": "^3.15.2",
68
+ "@nuxt/test-utils": "^3.15.4",
69
+ "@nuxt/ui": "^2.21.0",
70
+ "@nuxt/ui-pro": "^1.7.0",
71
+ "@simplewebauthn/types": "^12.0.0",
72
72
  "changelogen": "^0.5.7",
73
- "eslint": "^9.16.0",
74
- "nuxt": "^3.14.1592",
75
- "typescript": "^5.6.3",
76
- "vitest": "^2.1.8",
77
- "vue-tsc": "^2.1.10"
73
+ "eslint": "^9.18.0",
74
+ "nuxt": "^3.15.2",
75
+ "typescript": "5.6.3",
76
+ "vitest": "^3.0.3",
77
+ "vue-tsc": "^2.2.0"
78
78
  }
79
79
  }