nuxt-auth-utils 0.5.4 → 0.5.6

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
@@ -221,14 +221,18 @@ It can also be set using environment variables:
221
221
  - Microsoft
222
222
  - PayPal
223
223
  - Polar
224
+ - Seznam
224
225
  - Spotify
225
226
  - Steam
227
+ - Strava
226
228
  - TikTok
227
229
  - Twitch
228
230
  - VK
231
+ - WorkOS
229
232
  - X (Twitter)
230
233
  - XSUAA
231
234
  - Yandex
235
+ - Zitadel
232
236
 
233
237
  You can add your favorite provider by creating a new file in [src/runtime/server/lib/oauth/](./src/runtime/server/lib/oauth/).
234
238
 
@@ -349,10 +353,21 @@ The following code does not include the actual database queries, but shows the g
349
353
  import { z } from 'zod'
350
354
  export default defineWebAuthnRegisterEventHandler({
351
355
  // optional
352
- validateUser: z.object({
353
- // we want the userName to be a valid email
354
- userName: z.string().email()
355
- }).parse,
356
+ async validateUser(userBody, event) {
357
+ // bonus: check if the user is already authenticated to link a credential to his account
358
+ // We first check if the user is already authenticated by getting the session
359
+ // And verify that the email is the same as the one in session
360
+ const session = await getUserSession(event)
361
+ if (session.user?.email && session.user.email !== body.userName) {
362
+ throw createError({ statusCode: 400, message: 'Email not matching curent session' })
363
+ }
364
+
365
+ // If he registers a new account with credentials
366
+ return z.object({
367
+ // we want the userName to be a valid email
368
+ userName: z.string().email()
369
+ }).parse(userBody)
370
+ },
356
371
  async onSuccess(event, { credential, user }) {
357
372
  // The credential creation has been successful
358
373
  // We need to create a user if it does not exist
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.4",
4
+ "version": "0.5.6",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "0.8.4",
7
7
  "unbuild": "2.0.0"
package/dist/module.mjs CHANGED
@@ -126,6 +126,13 @@ const module = defineNuxtModule({
126
126
  audience: "",
127
127
  redirectURL: ""
128
128
  });
129
+ runtimeConfig.oauth.workos = defu(runtimeConfig.oauth.workos, {
130
+ clientId: "",
131
+ clientSecret: "",
132
+ connectionId: "",
133
+ screenHint: "",
134
+ redirectURL: ""
135
+ });
129
136
  runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
130
137
  clientId: "",
131
138
  clientSecret: "",
@@ -237,6 +244,16 @@ const module = defineNuxtModule({
237
244
  domain: "",
238
245
  redirectURL: ""
239
246
  });
247
+ runtimeConfig.oauth.seznam = defu(runtimeConfig.oauth.seznam, {
248
+ clientId: "",
249
+ clientSecret: "",
250
+ redirectURL: ""
251
+ });
252
+ runtimeConfig.oauth.strava = defu(runtimeConfig.oauth.strava, {
253
+ clientId: "",
254
+ clientSecret: "",
255
+ redirectURL: ""
256
+ });
240
257
  }
241
258
  });
242
259
 
@@ -12,21 +12,21 @@ export interface OAuthInstagramConfig {
12
12
  clientSecret?: string;
13
13
  /**
14
14
  * Instagram OAuth Scope
15
- * @default [ 'user_profile' ]
15
+ * @required [ 'business_basic' ]
16
16
  * @see https://developers.facebook.com/docs/instagram-basic-display-api/overview#permissions
17
- * @example [ 'user_profile', 'user_media' ],
17
+ * @example [ 'business_basic', 'business_manage_messages' ],
18
18
  */
19
- scope?: string[];
19
+ scope?: ('business_basic' | 'business_content_publish' | 'business_manage_comments' | 'business_manage_messages')[];
20
20
  /**
21
21
  * Instagram OAuth User Fields
22
22
  * @default [ 'id', 'username'],
23
23
  * @see https://developers.facebook.com/docs/instagram-basic-display-api/reference/user#fields
24
- * @example [ 'id', 'username', 'account_type', 'media_count' ],
24
+ * @example [ 'id', 'username', 'user_id', 'account_type', 'profile_picture_url' ],
25
25
  */
26
- fields?: string[];
26
+ fields?: ('id' | 'user_id' | 'username' | 'name' | 'account_type' | 'profile_picture_url' | 'followers_count' | 'follows_count' | 'media_count')[];
27
27
  /**
28
28
  * Instagram OAuth Authorization URL
29
- * @default 'https://api.instagram.com/oauth/authorize'
29
+ * @default 'https://www.instagram.com/oauth/authorize'
30
30
  */
31
31
  authorizationURL?: string;
32
32
  /**
@@ -10,8 +10,8 @@ export function defineOAuthInstagramEventHandler({
10
10
  }) {
11
11
  return eventHandler(async (event) => {
12
12
  config = defu(config, useRuntimeConfig(event).oauth?.instagram, {
13
- scope: ["user_profile"],
14
- authorizationURL: "https://api.instagram.com/oauth/authorize",
13
+ scope: ["business_basic"],
14
+ authorizationURL: "https://www.instagram.com/oauth/authorize",
15
15
  tokenURL: "https://api.instagram.com/oauth/access_token",
16
16
  authorizationParams: {}
17
17
  });
@@ -20,7 +20,11 @@ export function defineOAuthInstagramEventHandler({
20
20
  const error = createError({
21
21
  statusCode: 401,
22
22
  message: `Instagram login failed: ${query.error || "Unknown error"}`,
23
- data: query
23
+ data: {
24
+ error: query.error,
25
+ error_reason: query.error_reason,
26
+ error_description: query.error_description
27
+ }
24
28
  });
25
29
  if (!onError) throw error;
26
30
  return onError(event, error);
@@ -36,7 +40,7 @@ export function defineOAuthInstagramEventHandler({
36
40
  withQuery(config.authorizationURL, {
37
41
  client_id: config.clientId,
38
42
  redirect_uri: redirectURL,
39
- scope: config.scope.join(" "),
43
+ scope: config.scope.join(),
40
44
  response_type: "code"
41
45
  })
42
46
  );
@@ -55,9 +59,9 @@ export function defineOAuthInstagramEventHandler({
55
59
  }
56
60
  const accessToken = tokens.access_token;
57
61
  config.fields = config.fields || ["id", "username"];
58
- const fields = config.fields.join(",");
62
+ const fields = config.fields.join();
59
63
  const user = await $fetch(
60
- `https://graph.instagram.com/v20.0/me?fields=${fields}&access_token=${accessToken}`
64
+ `https://graph.instagram.com/v21.0/me?fields=${fields}&access_token=${accessToken}`
61
65
  );
62
66
  if (!user) {
63
67
  throw new Error("Instagram login failed: no user found");
@@ -0,0 +1,95 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthSeznamConfig {
3
+ /**
4
+ * Seznam OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_SEZNAM_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Seznam OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_SEZNAM_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Seznam OAuth Scope
15
+ * @default ['identity']
16
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
17
+ * @example ['identity', 'avatar']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
22
+ * Redirect URL has to be set as well in the Seznam OAuth settings https://vyvojari.seznam.cz/oauth/admin in order for it to work
23
+ * @see https://vyvojari.seznam.cz/oauth/admin
24
+ * @default process.env.NUXT_OAUTH_SEZNAM_REDIRECT_URL
25
+ */
26
+ redirectURL?: string;
27
+ /**
28
+ * Seznam OAuth Authorization URL
29
+ * @default 'https://login.szn.cz/api/v1/oauth/auth'
30
+ */
31
+ authorizationURL?: string;
32
+ /**
33
+ * Seznam OAuth Token URL
34
+ * @default 'https://login.szn.cz/api/v1/oauth/token'
35
+ */
36
+ tokenURL?: string;
37
+ /**
38
+ * Seznam OAuth User URL
39
+ * @default 'https://login.szn.cz/api/v1/user'
40
+ */
41
+ userURL?: string;
42
+ }
43
+ export interface OAuthSeznamUser {
44
+ /**
45
+ * Unique persistent user account identifier
46
+ */
47
+ oauth_user_id: string;
48
+ /**
49
+ * The user's e-mail address or null for users without an e-mail
50
+ */
51
+ email: string | null;
52
+ /**
53
+ * Given name (when available)
54
+ */
55
+ firstname: string;
56
+ /**
57
+ * Family name (when available)
58
+ */
59
+ lastname: string;
60
+ /**
61
+ * User account identifier suitable for usage within the Seznam ad infrastructure
62
+ */
63
+ advert_user_id: string;
64
+ /**
65
+ * (only when provided and validated by the user; null otherwise)
66
+ * Available only when you use the contact-phone scope
67
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
68
+ */
69
+ contact_phone?: string | null;
70
+ /**
71
+ * the image's URL
72
+ * Available only when you use the avatar scope
73
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
74
+ */
75
+ avatar_url?: string | null;
76
+ /**
77
+ * true/false value corresponding to the user's adult status
78
+ * Available only when you use the avatar scope
79
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
80
+ */
81
+ adulthood?: boolean;
82
+ /**
83
+ * date of birth in the ISO 8601 format (only when present; null otherwise)
84
+ * Available only when you use the birthday scope
85
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
86
+ */
87
+ birthday?: string | null;
88
+ /**
89
+ * one of the allowed strings "Male" / "Female" / "Other" (null if not set)
90
+ * Available only when you use the gender scope
91
+ * @see https://vyvojari.seznam.cz/oauth/scopes?lang=en
92
+ */
93
+ gender?: string | null;
94
+ }
95
+ export declare function defineOAuthSeznamEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthSeznamConfig, OAuthSeznamUser>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,57 @@
1
+ import defu from "defu";
2
+ import { eventHandler, getQuery, sendRedirect } from "h3";
3
+ import { withQuery } from "ufo";
4
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleMissingConfiguration, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthSeznamEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.seznam, {
9
+ authorizationURL: "https://login.szn.cz/api/v1/oauth/auth",
10
+ tokenURL: "https://login.szn.cz/api/v1/oauth/token",
11
+ userURL: "https://login.szn.cz/api/v1/user"
12
+ });
13
+ const query = getQuery(event);
14
+ if (!config.clientId) {
15
+ return handleMissingConfiguration(event, "seznam", ["clientId"], onError);
16
+ }
17
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
18
+ if (!query.code) {
19
+ config.scope = config.scope || ["identity"];
20
+ return sendRedirect(
21
+ event,
22
+ withQuery(config.authorizationURL, {
23
+ response_type: "code",
24
+ client_id: config.clientId,
25
+ redirect_uri: redirectURL,
26
+ scope: config.scope.join(","),
27
+ state: query.state || ""
28
+ })
29
+ );
30
+ }
31
+ const tokens = await requestAccessToken(config.tokenURL, {
32
+ body: {
33
+ grant_type: "authorization_code",
34
+ code: query.code,
35
+ client_id: config.clientId,
36
+ client_secret: config.clientSecret,
37
+ redirect_uri: redirectURL
38
+ }
39
+ });
40
+ if (tokens.error) {
41
+ return handleAccessTokenErrorResponse(event, "seznam", tokens, onError);
42
+ }
43
+ const accessToken = tokens.access_token;
44
+ const user = await $fetch(
45
+ config.userURL,
46
+ {
47
+ headers: {
48
+ Authorization: `bearer ${accessToken}`
49
+ }
50
+ }
51
+ );
52
+ return onSuccess(event, {
53
+ tokens,
54
+ user
55
+ });
56
+ });
57
+ }
@@ -0,0 +1,116 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthStravaConfig {
3
+ /**
4
+ * Strava OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_STRAVA_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Strava OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_STRAVA_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Strava OAuth Scope
15
+ * @default []
16
+ * @see https://developers.strava.com/docs/authentication/ # Details About Requesting Access
17
+ * @example ['read', 'read_all', 'profile:read_all', 'profile:write', 'activity:read', 'activity:read_all', 'activity:write']
18
+ */
19
+ scope?: string[];
20
+ /**
21
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
22
+ * @default process.env.NUXT_OAUTH_STRAVA_REDIRECT_URL or current URL
23
+ */
24
+ redirectURL?: string;
25
+ /**
26
+ * To show the authorization prompt to the user, 'force' will always show the prompt
27
+ * @default 'auto'
28
+ * @see https://developers.strava.com/docs/authentication/ # Details About Requesting Access
29
+ */
30
+ approvalPrompt?: 'auto' | 'force';
31
+ }
32
+ export interface OAuthStravaUser {
33
+ /**
34
+ * The unique identifier of the athlete
35
+ */
36
+ id: number;
37
+ /**
38
+ * The username of the athlete
39
+ */
40
+ username: string;
41
+ /**
42
+ * Resource state, indicates level of detail.
43
+ * - Meta (1): Basic information
44
+ * - Summary (2): Summary information
45
+ * - Detail (3): Detailed information
46
+ * @see https://developers.strava.com/docs/reference/#api-models-DetailedAthlete
47
+ */
48
+ resource_state: 1 | 2 | 3;
49
+ /**
50
+ * The athlete's first name
51
+ */
52
+ firstname: string;
53
+ /**
54
+ * The athlete's last name
55
+ */
56
+ lastname: string;
57
+ /**
58
+ * The athlete's bio
59
+ */
60
+ bio: string;
61
+ /**
62
+ * The athlete's city
63
+ */
64
+ city: string;
65
+ /**
66
+ * The athlete's state or geographical region
67
+ */
68
+ state: string;
69
+ /**
70
+ * The athlete's country
71
+ */
72
+ country: string;
73
+ /**
74
+ * The athlete's sex
75
+ */
76
+ sex: string;
77
+ /**
78
+ * Whether the athlete has any Summit subscription
79
+ * @see https://developers.strava.com/docs/reference/#api-models-DetailedAthlete
80
+ */
81
+ summit: boolean;
82
+ /**
83
+ * The time at which the athlete was created
84
+ */
85
+ created_at: Date;
86
+ /**
87
+ * The time at which the athlete was last updated
88
+ */
89
+ updated_at: Date;
90
+ /**
91
+ * The athlete's weight
92
+ */
93
+ weight: number;
94
+ /**
95
+ * URL to a 124x124 pixel profile picture
96
+ */
97
+ profile_medium: string;
98
+ /**
99
+ * URL to a 62x62 pixel profile picture
100
+ */
101
+ profile: string;
102
+ /**
103
+ * The athlete's timezone
104
+ */
105
+ timezone: string;
106
+ }
107
+ export interface OAuthStravaTokens {
108
+ token_type: 'Bearer';
109
+ expires_at: number;
110
+ expires_in: number;
111
+ access_token: string;
112
+ refresh_token: string;
113
+ athlete: OAuthStravaUser;
114
+ error?: string;
115
+ }
116
+ export declare function defineOAuthStravaEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthStravaConfig, OAuthStravaUser>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,73 @@
1
+ import { eventHandler, getQuery, sendRedirect } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import {
5
+ getOAuthRedirectURL,
6
+ handleAccessTokenErrorResponse,
7
+ handleMissingConfiguration,
8
+ requestAccessToken
9
+ } from "../utils.js";
10
+ import { useRuntimeConfig, createError } from "#imports";
11
+ export function defineOAuthStravaEventHandler({
12
+ config,
13
+ onSuccess,
14
+ onError
15
+ }) {
16
+ return eventHandler(async (event) => {
17
+ config = defu(config, useRuntimeConfig(event).oauth?.strava);
18
+ const query = getQuery(event);
19
+ if (query.error) {
20
+ const error = createError({
21
+ statusCode: 401,
22
+ message: `Strava login failed: ${query.error || "Unknown error"}`,
23
+ data: query
24
+ });
25
+ if (!onError) throw error;
26
+ return onError(event, error);
27
+ }
28
+ if (!config.clientId || !config.clientSecret) {
29
+ return handleMissingConfiguration(
30
+ event,
31
+ "strava",
32
+ ["clientId", "clientSecret"],
33
+ onError
34
+ );
35
+ }
36
+ const authorizationURL = "https://www.strava.com/oauth/authorize";
37
+ const tokenURL = "https://www.strava.com/oauth/token";
38
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
39
+ if (!query.code) {
40
+ return sendRedirect(
41
+ event,
42
+ withQuery(authorizationURL, {
43
+ client_id: config.clientId,
44
+ redirect_uri: redirectURL,
45
+ response_type: "code",
46
+ approval_prompt: config.approvalPrompt || "auto",
47
+ scope: config.scope
48
+ })
49
+ );
50
+ }
51
+ const tokens = await requestAccessToken(tokenURL, {
52
+ body: {
53
+ client_id: config.clientId,
54
+ client_secret: config.clientSecret,
55
+ code: query.code,
56
+ grant_type: "authorization_code",
57
+ redirect_uri: redirectURL
58
+ }
59
+ });
60
+ if (tokens.error) {
61
+ return handleAccessTokenErrorResponse(event, "strava", tokens, onError);
62
+ }
63
+ const user = await $fetch("https://www.strava.com/api/v3/athlete", {
64
+ headers: {
65
+ Authorization: `Bearer ${tokens.access_token}`
66
+ }
67
+ });
68
+ return onSuccess(event, {
69
+ user,
70
+ tokens
71
+ });
72
+ });
73
+ }
@@ -0,0 +1,63 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ /**
3
+ * WorkOS OAuth Configuration
4
+ * @see https://workos.com/docs/reference/user-management/authentication
5
+ */
6
+ export interface OAuthWorkOSConfig {
7
+ /**
8
+ * WorkOS OAuth Client ID *required
9
+ * @default process.env.NUXT_OAUTH_WORKOS_CLIENT_ID
10
+ */
11
+ clientId?: string;
12
+ /**
13
+ * WorkOS OAuth Client Secret (API Key) *required
14
+ * @default process.env.NUXT_OAUTH_WORKOS_CLIENT_SECRET
15
+ */
16
+ clientSecret?: string;
17
+ /**
18
+ * WorkOS OAuth Organization ID *not required
19
+ * @default process.env.NUXT_OAUTH_WORKOS_ORGANIZATION_ID
20
+ */
21
+ organizationId?: string;
22
+ /**
23
+ * WorkOS OAuth Connection ID *not required
24
+ * @default process.env.NUXT_OAUTH_WORKOS_CONNECTION_ID
25
+ */
26
+ connectionId?: string;
27
+ /**
28
+ * WorkOS OAuth screen hint *not required
29
+ * @default 'sign-in'
30
+ */
31
+ screenHint?: 'sign-in' | 'sign-up';
32
+ /**
33
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
34
+ * @default process.env.NUXT_OAUTH_WORKOS_REDIRECT_URL or current URL
35
+ */
36
+ redirectURL?: string;
37
+ }
38
+ export interface OAuthWorkOSUser {
39
+ object: 'user';
40
+ id: string;
41
+ email: string;
42
+ first_name: string | null;
43
+ last_name: string | null;
44
+ email_verified: boolean;
45
+ profile_picture_url: string | null;
46
+ created_at: string;
47
+ updated_at: string;
48
+ }
49
+ export type OAuthWorkOSAuthenticationMethod = 'SSO' | 'Password' | 'AppleOAuth' | 'GitHubOAuth' | 'GoogleOAuth' | 'MicrosoftOAuth' | 'MagicAuth' | 'Impersonation';
50
+ export interface OAuthWorkOSAuthenticateResponse {
51
+ user: OAuthWorkOSUser;
52
+ organization_id: string | null;
53
+ access_token: string;
54
+ refresh_token: string;
55
+ error: string | null;
56
+ error_description: string | null;
57
+ authentication_method: OAuthWorkOSAuthenticationMethod;
58
+ }
59
+ export interface OAuthWorkOSTokens {
60
+ access_token: string;
61
+ refresh_token: string;
62
+ }
63
+ export declare function defineOAuthWorkOSEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthWorkOSConfig, OAuthWorkOSUser, OAuthWorkOSTokens>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -0,0 +1,53 @@
1
+ import { eventHandler, getQuery, sendRedirect, getRequestIP, getRequestHeader } 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 defineOAuthWorkOSEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.workos, { screen_hint: "sign-in" });
9
+ if (!config.clientId || !config.clientSecret) {
10
+ return handleMissingConfiguration(event, "workos", ["clientId", "clientSecret"], onError);
11
+ }
12
+ const query = getQuery(event);
13
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
14
+ if (query.error) {
15
+ return handleAccessTokenErrorResponse(event, "workos", query, onError);
16
+ }
17
+ if (!query.code) {
18
+ return sendRedirect(
19
+ event,
20
+ withQuery("https://api.workos.com/user_management/authorize", {
21
+ response_type: "code",
22
+ provider: "authkit",
23
+ client_id: config.clientId,
24
+ redirect_uri: redirectURL || void 0,
25
+ connection_id: config.connectionId || void 0,
26
+ screen_hint: config.screenHint || "sign-in",
27
+ organization_id: config.organizationId || void 0
28
+ })
29
+ );
30
+ }
31
+ const authenticateResponse = await requestAccessToken("https://api.workos.com/user_management/authenticate", {
32
+ headers: {
33
+ "Content-Type": "application/json"
34
+ },
35
+ body: {
36
+ grant_type: "authorization_code",
37
+ client_id: config.clientId,
38
+ client_secret: config.clientSecret,
39
+ redirect_uri: redirectURL,
40
+ ip_address: getRequestIP(event),
41
+ user_agent: getRequestHeader(event, "user-agent"),
42
+ code: query.code
43
+ }
44
+ });
45
+ if (authenticateResponse.error) {
46
+ return handleAccessTokenErrorResponse(event, "workos", authenticateResponse, onError);
47
+ }
48
+ return onSuccess(event, {
49
+ tokens: { access_token: authenticateResponse.access_token, refresh_token: authenticateResponse.refresh_token },
50
+ user: authenticateResponse.user
51
+ });
52
+ });
53
+ }
@@ -35,7 +35,7 @@ export function defineOAuthXSUAAEventHandler({ config, onSuccess, onError }) {
35
35
  }
36
36
  });
37
37
  if (tokens.error) {
38
- return handleAccessTokenErrorResponse(event, "auth0", tokens, onError);
38
+ return handleAccessTokenErrorResponse(event, "xsuaa", tokens, onError);
39
39
  }
40
40
  const tokenType = tokens.token_type;
41
41
  const accessToken = tokens.access_token;
@@ -59,7 +59,6 @@ export function defineOAuthZitadelEventHandler({ config, onSuccess, onError }) {
59
59
  Accept: "application/json"
60
60
  }
61
61
  });
62
- console.log("Zitadel user:", user);
63
62
  if (!user) {
64
63
  const error = createError({
65
64
  statusCode: 500,
@@ -12,6 +12,7 @@ export interface RequestAccessTokenBody {
12
12
  redirect_uri: string;
13
13
  client_id: string;
14
14
  client_secret?: string;
15
+ [key: string]: string | undefined;
15
16
  }
16
17
  interface RequestAccessTokenOptions {
17
18
  body?: RequestAccessTokenBody;
@@ -1,7 +1,8 @@
1
- import type { ValidateFunction } from 'h3';
1
+ import type { H3Event } from 'h3';
2
2
  import type { WebAuthnUser, WebAuthnRegisterEventHandlerOptions } from '#auth-utils';
3
+ import type { ValidateUserFunction } from '~/src/runtime/types/webauthn';
3
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 | {
4
5
  creationOptions: import("@simplewebauthn/types").PublicKeyCredentialCreationOptionsJSON;
5
6
  attemptId: string;
6
7
  }>>;
7
- export declare function validateUserData<T>(data: unknown, fn: ValidateFunction<T>): Promise<T>;
8
+ export declare function validateUserData<T>(userBody: WebAuthnUser, event: H3Event, fn: ValidateUserFunction<T>): Promise<T>;
@@ -23,7 +23,7 @@ export function defineWebAuthnRegisterEventHandler({
23
23
  });
24
24
  let user = body.user;
25
25
  if (validateUser) {
26
- user = await validateUserData(body.user, validateUser);
26
+ user = await validateUserData(body.user, event, validateUser);
27
27
  }
28
28
  const _config = defu(await getOptions?.(event, body) ?? {}, useRuntimeConfig(event).webauthn.register, {
29
29
  rpID: url.hostname,
@@ -97,24 +97,24 @@ export function defineWebAuthnRegisterEventHandler({
97
97
  }
98
98
  });
99
99
  }
100
- export async function validateUserData(data, fn) {
100
+ export async function validateUserData(userBody, event, fn) {
101
101
  try {
102
- const res = await fn(data);
102
+ const res = await fn(userBody, event);
103
103
  if (res === false) {
104
104
  throw createUserValidationError();
105
105
  }
106
106
  if (res === true) {
107
- return data;
107
+ return userBody;
108
108
  }
109
- return res ?? data;
109
+ return res ?? userBody;
110
110
  } catch (error) {
111
111
  throw createUserValidationError(error);
112
112
  }
113
113
  }
114
114
  function createUserValidationError(validateError) {
115
115
  throw createError({
116
- status: 400,
117
- message: "User Validation Error",
116
+ status: validateError?.statusCode || 400,
117
+ message: validateError?.message || "User Validation Error",
118
118
  data: validateError
119
119
  });
120
120
  }
@@ -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' | 'instagram' | 'keycloak' | 'linear' | 'linkedin' | 'microsoft' | 'paypal' | 'polar' | 'spotify' | 'steam' | 'tiktok' | 'twitch' | 'vk' | 'x' | 'xsuaa' | 'yandex' | 'zitadel' | (string & {});
2
+ export type OAuthProvider = 'auth0' | 'authentik' | 'battledotnet' | 'cognito' | 'discord' | 'dropbox' | 'facebook' | 'github' | 'gitlab' | 'google' | 'instagram' | 'keycloak' | '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;
@@ -1,6 +1,6 @@
1
1
  import type { AuthenticationResponseJSON, AuthenticatorTransportFuture, RegistrationResponseJSON } from '@simplewebauthn/types';
2
2
  import type { Ref } from 'vue';
3
- import type { H3Event, H3Error, ValidateFunction } from 'h3';
3
+ import type { H3Event, H3Error, ValidateResult } from 'h3';
4
4
  import type { GenerateAuthenticationOptionsOpts, GenerateRegistrationOptionsOpts, VerifiedAuthenticationResponse, VerifiedRegistrationResponse } from '@simplewebauthn/server';
5
5
  export interface WebAuthnCredential {
6
6
  id: string;
@@ -36,13 +36,14 @@ export type RegistrationBody<T extends WebAuthnUser> = {
36
36
  attemptId: string;
37
37
  response: RegistrationResponseJSON;
38
38
  };
39
+ export type ValidateUserFunction<T> = (userBody: WebAuthnUser, event: H3Event) => ValidateResult<T> | Promise<ValidateResult<T>>;
39
40
  export type WebAuthnRegisterEventHandlerOptions<T extends WebAuthnUser> = WebAuthnEventHandlerBase<{
40
41
  user: T;
41
42
  credential: WebAuthnCredential;
42
43
  registrationInfo: Exclude<VerifiedRegistrationResponse['registrationInfo'], undefined>;
43
44
  }> & {
44
45
  getOptions?: (event: H3Event, body: RegistrationBody<T>) => Partial<GenerateRegistrationOptionsOpts> | Promise<Partial<GenerateRegistrationOptionsOpts>>;
45
- validateUser?: ValidateFunction<T>;
46
+ validateUser?: ValidateUserFunction<T>;
46
47
  excludeCredentials?: (event: H3Event, userName: string) => CredentialsList | Promise<CredentialsList>;
47
48
  };
48
49
  export type AuthenticationBody = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nuxt-auth-utils",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
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.13.0",
11
+ "packageManager": "pnpm@9.13.2",
12
12
  "exports": {
13
13
  ".": {
14
14
  "types": "./dist/types.d.ts",