nuxt-auth-utils 0.5.28 → 0.5.30

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 (58) hide show
  1. package/README.md +4 -2
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +12 -0
  4. package/dist/runtime/app/plugins/session.client.js +1 -1
  5. package/dist/runtime/server/lib/oauth/apple.js +12 -3
  6. package/dist/runtime/server/lib/oauth/atlassian.js +7 -4
  7. package/dist/runtime/server/lib/oauth/auth0.js +7 -2
  8. package/dist/runtime/server/lib/oauth/authentik.js +7 -2
  9. package/dist/runtime/server/lib/oauth/azureb2c.js +2 -2
  10. package/dist/runtime/server/lib/oauth/battledotnet.js +7 -5
  11. package/dist/runtime/server/lib/oauth/box.d.ts +95 -0
  12. package/dist/runtime/server/lib/oauth/box.js +69 -0
  13. package/dist/runtime/server/lib/oauth/cognito.js +7 -2
  14. package/dist/runtime/server/lib/oauth/discord.js +7 -2
  15. package/dist/runtime/server/lib/oauth/dropbox.js +7 -2
  16. package/dist/runtime/server/lib/oauth/facebook.js +7 -3
  17. package/dist/runtime/server/lib/oauth/gitea.js +8 -1
  18. package/dist/runtime/server/lib/oauth/github.js +2 -2
  19. package/dist/runtime/server/lib/oauth/gitlab.js +8 -1
  20. package/dist/runtime/server/lib/oauth/google.js +7 -3
  21. package/dist/runtime/server/lib/oauth/heroku.js +2 -2
  22. package/dist/runtime/server/lib/oauth/hubspot.js +8 -1
  23. package/dist/runtime/server/lib/oauth/instagram.js +7 -2
  24. package/dist/runtime/server/lib/oauth/keycloak.js +9 -2
  25. package/dist/runtime/server/lib/oauth/kick.js +6 -3
  26. package/dist/runtime/server/lib/oauth/line.js +7 -3
  27. package/dist/runtime/server/lib/oauth/linear.js +7 -2
  28. package/dist/runtime/server/lib/oauth/linkedin.js +7 -2
  29. package/dist/runtime/server/lib/oauth/livechat.js +9 -5
  30. package/dist/runtime/server/lib/oauth/microsoft.js +7 -3
  31. package/dist/runtime/server/lib/oauth/oidc.js +2 -2
  32. package/dist/runtime/server/lib/oauth/okta.js +3 -4
  33. package/dist/runtime/server/lib/oauth/ory.js +2 -2
  34. package/dist/runtime/server/lib/oauth/osu.js +2 -2
  35. package/dist/runtime/server/lib/oauth/paypal.js +7 -2
  36. package/dist/runtime/server/lib/oauth/polar.js +7 -2
  37. package/dist/runtime/server/lib/oauth/riotgames.d.ts +74 -0
  38. package/dist/runtime/server/lib/oauth/riotgames.js +83 -0
  39. package/dist/runtime/server/lib/oauth/roblox.js +7 -2
  40. package/dist/runtime/server/lib/oauth/salesforce.js +2 -2
  41. package/dist/runtime/server/lib/oauth/seznam.js +6 -2
  42. package/dist/runtime/server/lib/oauth/shopifyCustomer.js +4 -1
  43. package/dist/runtime/server/lib/oauth/slack.js +2 -2
  44. package/dist/runtime/server/lib/oauth/spotify.js +7 -2
  45. package/dist/runtime/server/lib/oauth/steam.js +6 -2
  46. package/dist/runtime/server/lib/oauth/strava.js +8 -1
  47. package/dist/runtime/server/lib/oauth/tiktok.js +7 -2
  48. package/dist/runtime/server/lib/oauth/twitch.js +7 -2
  49. package/dist/runtime/server/lib/oauth/vk.js +7 -1
  50. package/dist/runtime/server/lib/oauth/workos.js +7 -2
  51. package/dist/runtime/server/lib/oauth/xsuaa.js +7 -2
  52. package/dist/runtime/server/lib/oauth/yandex.js +8 -1
  53. package/dist/runtime/server/lib/oauth/zitadel.js +2 -2
  54. package/dist/runtime/server/lib/utils.d.ts +5 -1
  55. package/dist/runtime/server/lib/utils.js +11 -9
  56. package/dist/runtime/types/oauth-config.d.ts +1 -1
  57. package/dist/runtime/types/webauthn.d.ts +1 -1
  58. package/package.json +14 -14
package/README.md CHANGED
@@ -219,6 +219,7 @@ It can also be set using environment variables:
219
219
  - Azure B2C
220
220
  - Battle.net
221
221
  - Bluesky (AT Protocol)
222
+ - Box.com
222
223
  - Discord
223
224
  - Dropbox
224
225
  - Facebook
@@ -242,6 +243,7 @@ It can also be set using environment variables:
242
243
  - osu!
243
244
  - PayPal
244
245
  - Polar
246
+ - Riot Games
245
247
  - Salesforce
246
248
  - Seznam
247
249
  - Shopify Customer
@@ -304,7 +306,7 @@ if (await verifyPassword(hashedPassword, 'user_password')) {
304
306
  It also provides a `passwordNeedsRehash` function to check if a password needs to be rehashed. This is useful when the hash settings are changed, such as as increasing the scrypt cost parameters.
305
307
 
306
308
  ```ts
307
- const needsRehash = await passwordNeedsRehash(hashedPassword)
309
+ const needsRehash = passwordNeedsRehash(hashedPassword)
308
310
 
309
311
  if (needsRehash) {
310
312
  // Password needs to be rehashed
@@ -416,7 +418,7 @@ export default defineWebAuthnRegisterEventHandler({
416
418
  // And verify that the email is the same as the one in session
417
419
  const session = await getUserSession(event)
418
420
  if (session.user?.email && session.user.email !== userBody.userName) {
419
- throw createError({ statusCode: 400, message: 'Email not matching curent session' })
421
+ throw createError({ statusCode: 400, message: 'Email not matching current session' })
420
422
  }
421
423
 
422
424
  // If he registers a new account with credentials
package/dist/module.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.5.28",
4
+ "version": "0.5.30",
5
5
  "builder": {
6
6
  "@nuxt/module-builder": "1.0.2",
7
7
  "unbuild": "3.6.1"
package/dist/module.mjs CHANGED
@@ -115,6 +115,12 @@ const module$1 = defineNuxtModule({
115
115
  redirectURL: "",
116
116
  baseURL: ""
117
117
  });
118
+ runtimeConfig.oauth.box = defu(runtimeConfig.oauth.box, {
119
+ clientId: "",
120
+ clientSecret: "",
121
+ redirectURL: "",
122
+ scope: []
123
+ });
118
124
  runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
119
125
  clientId: "",
120
126
  clientSecret: "",
@@ -406,6 +412,12 @@ const module$1 = defineNuxtModule({
406
412
  redirectURL: "",
407
413
  scope: []
408
414
  });
415
+ runtimeConfig.oauth.riotgames = defu(runtimeConfig.oauth.riotgames, {
416
+ clientId: "",
417
+ clientSecret: "",
418
+ redirectURL: "",
419
+ scope: []
420
+ });
409
421
  }
410
422
  });
411
423
 
@@ -3,7 +3,7 @@ export default defineNuxtPlugin(async (nuxtApp) => {
3
3
  if (!nuxtApp.payload.serverRendered) {
4
4
  await useUserSession().fetch();
5
5
  } else if (Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached) || nuxtApp.$config.public.auth.loadStrategy === "client-only") {
6
- nuxtApp.hook("app:mounted", async () => {
6
+ nuxtApp.hook("app:suspense:resolve", async () => {
7
7
  await useUserSession().fetch();
8
8
  });
9
9
  }
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getRequestHeader, readBody, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken, signJwt, verifyJwt } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken, signJwt, verifyJwt } from "../utils.js";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthAppleEventHandler({
7
7
  config,
@@ -19,10 +19,15 @@ export function defineOAuthAppleEventHandler({
19
19
  }
20
20
  const isPost = getRequestHeader(event, "content-type") === "application/x-www-form-urlencoded";
21
21
  let code;
22
+ let state;
22
23
  let user;
23
24
  if (isPost) {
24
- ({ code, user } = await readBody(event));
25
+ ({ code, state, user } = await readBody(event));
25
26
  }
27
+ const storedState = await handleState(event, {
28
+ isCallback: isPost && Boolean(code),
29
+ sameSite: "none"
30
+ });
26
31
  if (!isPost || !code) {
27
32
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
28
33
  config.scope = Array.isArray(config.scope) ? config.scope.join(" ") : config.scope || "name email";
@@ -34,10 +39,14 @@ export function defineOAuthAppleEventHandler({
34
39
  client_id: config.clientId,
35
40
  redirect_uri: redirectURL,
36
41
  scope: config.scope,
37
- ...config.authorizationParams
42
+ ...config.authorizationParams,
43
+ state: storedState
38
44
  })
39
45
  );
40
46
  }
47
+ if (state !== storedState) {
48
+ return handleInvalidState(event, "apple", onError);
49
+ }
41
50
  try {
42
51
  const secret = await signJwt(
43
52
  {
@@ -1,8 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { randomUUID } from "uncrypto";
5
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
6
5
  import { useRuntimeConfig, createError } from "#imports";
7
6
  export function defineOAuthAtlassianEventHandler({
8
7
  config,
@@ -28,6 +27,7 @@ export function defineOAuthAtlassianEventHandler({
28
27
  }
29
28
  const query = getQuery(event);
30
29
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
30
+ const state = await handleState(event);
31
31
  if (!query.code) {
32
32
  return sendRedirect(
33
33
  event,
@@ -36,10 +36,10 @@ export function defineOAuthAtlassianEventHandler({
36
36
  client_id: config.clientId,
37
37
  scope: config.scope?.join(" "),
38
38
  redirect_uri: redirectURL,
39
- state: randomUUID(),
40
39
  response_type: "code",
41
40
  prompt: "consent",
42
- ...config.authorizationParams
41
+ ...config.authorizationParams,
42
+ state
43
43
  })
44
44
  );
45
45
  }
@@ -52,6 +52,9 @@ export function defineOAuthAtlassianEventHandler({
52
52
  if (!onError) throw error;
53
53
  return onError(event, error);
54
54
  }
55
+ if (query.state !== state) {
56
+ return handleInvalidState(event, "atlassian", onError);
57
+ }
55
58
  const tokens = await requestAccessToken(config.tokenURL, {
56
59
  headers: {
57
60
  "Content-Type": "application/json"
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthAuth0EventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
@@ -26,6 +26,7 @@ export function defineOAuthAuth0EventHandler({ config, onSuccess, onError }) {
26
26
  onError
27
27
  );
28
28
  }
29
+ const state = await handleState(event);
29
30
  if (!query.code) {
30
31
  config.scope = config.scope || ["openid", "offline_access"];
31
32
  if (config.emailRequired && !config.scope.includes("email")) {
@@ -41,10 +42,14 @@ export function defineOAuthAuth0EventHandler({ config, onSuccess, onError }) {
41
42
  audience: config.audience || "",
42
43
  max_age: config.maxAge || 0,
43
44
  connection: config.connection || "",
44
- ...config.authorizationParams
45
+ ...config.authorizationParams,
46
+ state
45
47
  })
46
48
  );
47
49
  }
50
+ if (query.state !== state) {
51
+ return handleInvalidState(event, "auth0", onError);
52
+ }
48
53
  const tokens = await requestAccessToken(tokenURL, {
49
54
  headers: {
50
55
  "Content-Type": "application/json"
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
5
5
  import { useRuntimeConfig, createError } from "#imports";
6
6
  export function defineOAuthAuthentikEventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
@@ -22,6 +22,7 @@ export function defineOAuthAuthentikEventHandler({ config, onSuccess, onError })
22
22
  const authorizationURL = `https://${config.domain}/application/o/authorize/`;
23
23
  const tokenURL = `https://${config.domain}/application/o/token/`;
24
24
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
25
+ const state = await handleState(event);
25
26
  if (!query.code) {
26
27
  return sendRedirect(
27
28
  event,
@@ -29,10 +30,14 @@ export function defineOAuthAuthentikEventHandler({ config, onSuccess, onError })
29
30
  response_type: "code",
30
31
  client_id: config.clientId,
31
32
  redirect_uri: redirectURL,
32
- scope: (config.scope || ["openid", "profile", "email"]).join(" ")
33
+ scope: (config.scope || ["openid", "profile", "email"]).join(" "),
34
+ state
33
35
  })
34
36
  );
35
37
  }
38
+ if (query.state !== state) {
39
+ return handleInvalidState(event, "authentik", onError);
40
+ }
36
41
  const tokens = await requestAccessToken(tokenURL, {
37
42
  headers: {
38
43
  "Authorization": `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`,
@@ -27,10 +27,10 @@ export function defineOAuthAzureB2CEventHandler({ config, onSuccess, onError })
27
27
  response_type: "code",
28
28
  redirect_uri: redirectURL,
29
29
  scope: config.scope.join(" "),
30
- state,
31
30
  code_challenge: verifier.code_challenge,
32
31
  code_challenge_method: verifier.code_challenge_method,
33
- ...config.authorizationParams
32
+ ...config.authorizationParams,
33
+ state
34
34
  })
35
35
  );
36
36
  }
@@ -1,8 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { randomUUID } from "uncrypto";
5
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
6
5
  import { useRuntimeConfig, createError } from "#imports";
7
6
  export function defineOAuthBattledotnetEventHandler({ config, onSuccess, onError }) {
8
7
  return eventHandler(async (event) => {
@@ -30,6 +29,7 @@ export function defineOAuthBattledotnetEventHandler({ config, onSuccess, onError
30
29
  );
31
30
  }
32
31
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
32
+ const state = await handleState(event);
33
33
  if (!query.code) {
34
34
  config.scope = config.scope || ["openid"];
35
35
  config.region = config.region || "EU";
@@ -43,13 +43,15 @@ export function defineOAuthBattledotnetEventHandler({ config, onSuccess, onError
43
43
  client_id: config.clientId,
44
44
  redirect_uri: redirectURL,
45
45
  scope: config.scope.join(" "),
46
- state: randomUUID(),
47
- // Todo: handle PKCE flow
48
46
  response_type: "code",
49
- ...config.authorizationParams
47
+ ...config.authorizationParams,
48
+ state
50
49
  })
51
50
  );
52
51
  }
52
+ if (query.state !== state) {
53
+ return handleInvalidState(event, "battledotnet", onError);
54
+ }
53
55
  config.scope = config.scope || [];
54
56
  if (!config.scope.includes("openid")) {
55
57
  config.scope.push("openid");
@@ -0,0 +1,95 @@
1
+ import type { OAuthConfig } from '#auth-utils';
2
+ export interface OAuthBoxConfig {
3
+ /**
4
+ * Box OAuth Client ID
5
+ * @default process.env.NUXT_OAUTH_BOX_CLIENT_ID
6
+ */
7
+ clientId?: string;
8
+ /**
9
+ * Box OAuth Client Secret
10
+ * @default process.env.NUXT_OAUTH_BOX_CLIENT_SECRET
11
+ */
12
+ clientSecret?: string;
13
+ /**
14
+ * Box OAuth Scope
15
+ * @default []
16
+ * @see https://developer.box.com/guides/api-calls/permissions-and-errors/scopes/
17
+ */
18
+ scope?: string[];
19
+ /**
20
+ * Box OAuth Authorization URL
21
+ * @default 'https://account.box.com/api/oauth2/authorize'
22
+ */
23
+ authorizationURL?: string;
24
+ /**
25
+ * Box OAuth Token URL
26
+ * @default 'https://api.box.com/oauth2/token'
27
+ */
28
+ tokenURL?: string;
29
+ /**
30
+ * Box User Info URL
31
+ * @default 'https://api.box.com/2.0/users/me'
32
+ */
33
+ userURL?: string;
34
+ /**
35
+ * Extra authorization parameters to provide to the authorization URL
36
+ * @see https://developer.box.com/guides/authentication/oauth2/oauth2-setup/
37
+ */
38
+ authorizationParams?: Record<string, string>;
39
+ /**
40
+ * Redirect URL to allow overriding for situations like prod failing to determine public hostname
41
+ * @default process.env.NUXT_OAUTH_BOX_REDIRECT_URL
42
+ */
43
+ redirectURL?: string;
44
+ }
45
+ /**
46
+ * Box User object returned from /2.0/users/me
47
+ * @see https://developer.box.com/reference/get-users-me/
48
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/example/8119550-f7344611-7834-4040-a4ae-d6b3ef95bfdb
49
+ */
50
+ interface OAuthBoxUser {
51
+ type: 'user';
52
+ id: string;
53
+ name: string;
54
+ login: string;
55
+ created_at: string;
56
+ modified_at: string;
57
+ language: string;
58
+ timezone: string;
59
+ space_amount: number;
60
+ space_used: number;
61
+ max_upload_size: number;
62
+ status: 'active' | 'inactive' | 'cannot_delete_edit' | 'cannot_delete_edit_upload';
63
+ job_title?: string;
64
+ phone?: string;
65
+ address?: string;
66
+ avatar_url?: string;
67
+ }
68
+ /**
69
+ * Box OAuth tokens
70
+ * @see https://developer.box.com/reference/post-oauth2-token/
71
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/example/8119550-70a8e5bd-4d25-494c-be2c-5409db9d1ace
72
+ */
73
+ interface OAuthBoxTokens {
74
+ access_token: string;
75
+ refresh_token: string;
76
+ expires_in: number;
77
+ token_type: 'bearer';
78
+ restricted_to?: Array<{
79
+ scope: string;
80
+ object?: {
81
+ type: string;
82
+ id: string;
83
+ };
84
+ }>;
85
+ }
86
+ /**
87
+ * Define an OAuth event handler for Box authentication.
88
+ * @see https://developer.box.com/guides/authentication/oauth2/
89
+ * @see https://www.postman.com/boxdev/box-s-public-workspace/collection/trhp912/box-platform-api
90
+ */
91
+ export declare function defineOAuthBoxEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBoxConfig, {
92
+ user: OAuthBoxUser;
93
+ tokens: OAuthBoxTokens;
94
+ }>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<any>>;
95
+ export {};
@@ -0,0 +1,69 @@
1
+ import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
+ import { withQuery } from "ufo";
3
+ import { defu } from "defu";
4
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handleState, requestAccessToken } from "../utils.js";
5
+ import { useRuntimeConfig } from "#imports";
6
+ export function defineOAuthBoxEventHandler({ config, onSuccess, onError }) {
7
+ return eventHandler(async (event) => {
8
+ config = defu(config, useRuntimeConfig(event).oauth?.box, {
9
+ authorizationURL: "https://account.box.com/api/oauth2/authorize",
10
+ tokenURL: "https://api.box.com/oauth2/token",
11
+ userURL: "https://api.box.com/2.0/users/me",
12
+ authorizationParams: {}
13
+ });
14
+ const query = getQuery(event);
15
+ if (query.error) {
16
+ const errorMessageParts = [query.error, query.error_description].filter(Boolean).join(": ");
17
+ const error = createError({
18
+ statusCode: 401,
19
+ message: `Box login failed: ${errorMessageParts || "Unknown error"}`,
20
+ data: query
21
+ });
22
+ if (!onError) throw error;
23
+ return onError(event, error);
24
+ }
25
+ if (!config.clientId || !config.clientSecret) {
26
+ return handleMissingConfiguration(event, "box", ["clientId", "clientSecret"], onError);
27
+ }
28
+ const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
29
+ const state = await handleState(event);
30
+ if (!query.code) {
31
+ const scope = config.scope || [];
32
+ return sendRedirect(
33
+ event,
34
+ withQuery(config.authorizationURL, {
35
+ response_type: "code",
36
+ client_id: config.clientId,
37
+ redirect_uri: redirectURL,
38
+ scope: scope.join(" "),
39
+ ...config.authorizationParams,
40
+ state
41
+ })
42
+ );
43
+ }
44
+ if (query.state !== state) {
45
+ return handleInvalidState(event, "box", onError);
46
+ }
47
+ const tokens = await requestAccessToken(config.tokenURL, {
48
+ body: {
49
+ grant_type: "authorization_code",
50
+ client_id: config.clientId,
51
+ client_secret: config.clientSecret,
52
+ redirect_uri: redirectURL,
53
+ code: query.code
54
+ }
55
+ });
56
+ if (tokens.error) {
57
+ return handleAccessTokenErrorResponse(event, "box", tokens, onError);
58
+ }
59
+ const user = await $fetch(config.userURL, {
60
+ headers: {
61
+ Authorization: `Bearer ${tokens.access_token}`
62
+ }
63
+ });
64
+ return onSuccess(event, {
65
+ user,
66
+ tokens
67
+ });
68
+ });
69
+ }
@@ -2,7 +2,7 @@ import { defu } from "defu";
2
2
  import { eventHandler, getQuery, sendRedirect } from "h3";
3
3
  import { discovery } from "openid-client";
4
4
  import { withQuery } from "ufo";
5
- import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleMissingConfiguration, requestAccessToken } from "../utils.js";
5
+ import { getOAuthRedirectURL, handleAccessTokenErrorResponse, handleInvalidState, handleMissingConfiguration, handleState, requestAccessToken } from "../utils.js";
6
6
  import { useRuntimeConfig } from "#imports";
7
7
  export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
8
8
  return eventHandler(async (event) => {
@@ -24,6 +24,7 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
24
24
  } = issuer.serverMetadata();
25
25
  const query = getQuery(event);
26
26
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
27
+ const state = await handleState(event);
27
28
  if (!query.code) {
28
29
  config.scope = config.scope || ["openid", "profile"];
29
30
  return sendRedirect(
@@ -33,10 +34,14 @@ export function defineOAuthCognitoEventHandler({ config, onSuccess, onError }) {
33
34
  redirect_uri: redirectURL,
34
35
  response_type: "code",
35
36
  scope: config.scope.join(" "),
36
- ...config.authorizationParams
37
+ ...config.authorizationParams,
38
+ state
37
39
  })
38
40
  );
39
41
  }
42
+ if (query.state !== state) {
43
+ return handleInvalidState(event, "cognito", onError);
44
+ }
40
45
  const tokens = await requestAccessToken(
41
46
  tokenURL,
42
47
  {
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthDiscordEventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
@@ -19,6 +19,7 @@ export function defineOAuthDiscordEventHandler({ config, onSuccess, onError }) {
19
19
  return handleAccessTokenErrorResponse(event, "discord", query, onError);
20
20
  }
21
21
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
22
+ const state = await handleState(event);
22
23
  if (!query.code) {
23
24
  config.scope = config.scope || [];
24
25
  if (config.emailRequired && !config.scope.includes("email")) {
@@ -34,10 +35,14 @@ export function defineOAuthDiscordEventHandler({ config, onSuccess, onError }) {
34
35
  client_id: config.clientId,
35
36
  redirect_uri: redirectURL,
36
37
  scope: config.scope.join(" "),
37
- ...config.authorizationParams
38
+ ...config.authorizationParams,
39
+ state
38
40
  })
39
41
  );
40
42
  }
43
+ if (query.state !== state) {
44
+ return handleInvalidState(event, "discord", onError);
45
+ }
41
46
  const tokens = await requestAccessToken(config.tokenURL, {
42
47
  body: {
43
48
  client_id: config.clientId,
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
5
5
  import { useRuntimeConfig, createError } from "#imports";
6
6
  export function defineOAuthDropboxEventHandler({ config, onSuccess, onError }) {
7
7
  return eventHandler(async (event) => {
@@ -15,6 +15,7 @@ export function defineOAuthDropboxEventHandler({ config, onSuccess, onError }) {
15
15
  return handleMissingConfiguration(event, "dropbox", ["clientId", "clientSecret"], onError);
16
16
  }
17
17
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
18
+ const state = await handleState(event);
18
19
  if (!query.code) {
19
20
  config.scope = config.scope || [];
20
21
  if (!config.scope.includes("openid")) {
@@ -30,10 +31,14 @@ export function defineOAuthDropboxEventHandler({ config, onSuccess, onError }) {
30
31
  client_id: config.clientId,
31
32
  redirect_uri: redirectURL,
32
33
  scope: config.scope.join(" "),
33
- ...config.authorizationParams
34
+ ...config.authorizationParams,
35
+ state
34
36
  })
35
37
  );
36
38
  }
39
+ if (query.state !== state) {
40
+ return handleInvalidState(event, "dropbox", onError);
41
+ }
37
42
  const tokens = await requestAccessToken(config.tokenURL, {
38
43
  headers: {
39
44
  Authorization: `Basic ${Buffer.from(`${config.clientId}:${config.clientSecret}`).toString("base64")}`
@@ -1,7 +1,7 @@
1
1
  import { eventHandler, getQuery, sendRedirect, createError } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
- import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, requestAccessToken } from "../utils.js";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse, getOAuthRedirectURL, handleInvalidState, handleState, requestAccessToken } from "../utils.js";
5
5
  import { useRuntimeConfig } from "#imports";
6
6
  export function defineOAuthFacebookEventHandler({
7
7
  config,
@@ -28,6 +28,7 @@ export function defineOAuthFacebookEventHandler({
28
28
  return handleMissingConfiguration(event, "facebook", ["clientId", "clientSecret"], onError);
29
29
  }
30
30
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
31
+ const state = await handleState(event);
31
32
  if (!query.code) {
32
33
  config.scope = [...new Set(config.scope)];
33
34
  return sendRedirect(
@@ -36,11 +37,14 @@ export function defineOAuthFacebookEventHandler({
36
37
  client_id: config.clientId,
37
38
  redirect_uri: redirectURL,
38
39
  scope: config.scope.join(" "),
39
- state: query.state || "",
40
- ...config.authorizationParams
40
+ ...config.authorizationParams,
41
+ state
41
42
  })
42
43
  );
43
44
  }
45
+ if (query.state !== state) {
46
+ return handleInvalidState(event, "facebook", onError);
47
+ }
44
48
  const tokens = await requestAccessToken(config.tokenURL, {
45
49
  body: {
46
50
  client_id: config.clientId,
@@ -5,6 +5,8 @@ import {
5
5
  handleMissingConfiguration,
6
6
  handleAccessTokenErrorResponse,
7
7
  getOAuthRedirectURL,
8
+ handleInvalidState,
9
+ handleState,
8
10
  requestAccessToken
9
11
  } from "../utils.js";
10
12
  import { useRuntimeConfig, createError } from "#imports";
@@ -40,6 +42,7 @@ export function defineOAuthGiteaEventHandler({
40
42
  );
41
43
  }
42
44
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
45
+ const state = await handleState(event);
43
46
  if (!query.code) {
44
47
  config.scope = config.scope || [];
45
48
  if (!config.scope.length) {
@@ -55,10 +58,14 @@ export function defineOAuthGiteaEventHandler({
55
58
  client_id: config.clientId,
56
59
  redirect_uri: redirectURL,
57
60
  scope: config.scope.join(" "),
58
- ...config.authorizationParams
61
+ ...config.authorizationParams,
62
+ state
59
63
  })
60
64
  );
61
65
  }
66
+ if (query.state !== state) {
67
+ return handleInvalidState(event, "gitea", onError);
68
+ }
62
69
  const tokens = await requestAccessToken(config.tokenURL, {
63
70
  body: {
64
71
  grant_type: "authorization_code",
@@ -37,8 +37,8 @@ export function defineOAuthGitHubEventHandler({ config, onSuccess, onError }) {
37
37
  client_id: config.clientId,
38
38
  redirect_uri: redirectURL,
39
39
  scope: config.scope.join(" "),
40
- state,
41
- ...config.authorizationParams
40
+ ...config.authorizationParams,
41
+ state
42
42
  })
43
43
  );
44
44
  }
@@ -5,6 +5,8 @@ import {
5
5
  handleMissingConfiguration,
6
6
  handleAccessTokenErrorResponse,
7
7
  getOAuthRedirectURL,
8
+ handleInvalidState,
9
+ handleState,
8
10
  requestAccessToken
9
11
  } from "../utils.js";
10
12
  import { useRuntimeConfig, createError } from "#imports";
@@ -40,6 +42,7 @@ export function defineOAuthGitLabEventHandler({
40
42
  );
41
43
  }
42
44
  const redirectURL = config.redirectURL || getOAuthRedirectURL(event);
45
+ const state = await handleState(event);
43
46
  if (!query.code) {
44
47
  config.scope = config.scope || [];
45
48
  if (!config.scope.length) {
@@ -55,10 +58,14 @@ export function defineOAuthGitLabEventHandler({
55
58
  client_id: config.clientId,
56
59
  redirect_uri: redirectURL,
57
60
  scope: config.scope.join(" "),
58
- ...config.authorizationParams
61
+ ...config.authorizationParams,
62
+ state
59
63
  })
60
64
  );
61
65
  }
66
+ if (query.state !== state) {
67
+ return handleInvalidState(event, "gitlab", onError);
68
+ }
62
69
  const tokens = await requestAccessToken(config.tokenURL, {
63
70
  body: {
64
71
  grant_type: "authorization_code",