nuxt-auth-utils 0.3.3 → 0.3.5

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 (45) hide show
  1. package/README.md +2 -0
  2. package/dist/module.d.mts +1 -1
  3. package/dist/module.d.ts +1 -1
  4. package/dist/module.json +2 -2
  5. package/dist/module.mjs +33 -17
  6. package/dist/runtime/app/plugins/session.client.js +3 -1
  7. package/dist/runtime/server/lib/oauth/auth0.d.ts +5 -0
  8. package/dist/runtime/server/lib/oauth/auth0.js +7 -17
  9. package/dist/runtime/server/lib/oauth/battledotnet.d.ts +5 -0
  10. package/dist/runtime/server/lib/oauth/battledotnet.js +11 -17
  11. package/dist/runtime/server/lib/oauth/cognito.d.ts +10 -0
  12. package/dist/runtime/server/lib/oauth/cognito.js +11 -20
  13. package/dist/runtime/server/lib/oauth/discord.d.ts +5 -0
  14. package/dist/runtime/server/lib/oauth/discord.js +7 -17
  15. package/dist/runtime/server/lib/oauth/facebook.d.ts +5 -0
  16. package/dist/runtime/server/lib/oauth/facebook.js +6 -16
  17. package/dist/runtime/server/lib/oauth/github.d.ts +6 -0
  18. package/dist/runtime/server/lib/oauth/github.js +5 -15
  19. package/dist/runtime/server/lib/oauth/google.d.ts +5 -0
  20. package/dist/runtime/server/lib/oauth/google.js +6 -17
  21. package/dist/runtime/server/lib/oauth/keycloak.d.ts +5 -0
  22. package/dist/runtime/server/lib/oauth/keycloak.js +6 -16
  23. package/dist/runtime/server/lib/oauth/linkedin.d.ts +5 -0
  24. package/dist/runtime/server/lib/oauth/linkedin.js +7 -17
  25. package/dist/runtime/server/lib/oauth/microsoft.d.ts +1 -1
  26. package/dist/runtime/server/lib/oauth/microsoft.js +6 -16
  27. package/dist/runtime/server/lib/oauth/paypal.d.ts +5 -0
  28. package/dist/runtime/server/lib/oauth/paypal.js +6 -16
  29. package/dist/runtime/server/lib/oauth/spotify.d.ts +5 -0
  30. package/dist/runtime/server/lib/oauth/spotify.js +7 -17
  31. package/dist/runtime/server/lib/oauth/steam.d.ts +5 -0
  32. package/dist/runtime/server/lib/oauth/steam.js +4 -8
  33. package/dist/runtime/server/lib/oauth/twitch.d.ts +5 -0
  34. package/dist/runtime/server/lib/oauth/twitch.js +6 -16
  35. package/dist/runtime/server/lib/oauth/x.d.ts +6 -6
  36. package/dist/runtime/server/lib/oauth/x.js +10 -44
  37. package/dist/runtime/server/lib/oauth/xsuaa.d.ts +5 -0
  38. package/dist/runtime/server/lib/oauth/xsuaa.js +7 -17
  39. package/dist/runtime/server/lib/oauth/yandex.d.ts +5 -0
  40. package/dist/runtime/server/lib/oauth/yandex.js +6 -17
  41. package/dist/runtime/server/lib/utils.d.ts +9 -0
  42. package/dist/runtime/server/lib/utils.js +21 -0
  43. package/dist/runtime/types/index.d.ts +1 -1
  44. package/dist/runtime/types/oauth-config.d.ts +3 -1
  45. package/package.json +11 -10
package/README.md CHANGED
@@ -233,6 +233,8 @@ export default oauthGitHubEventHandler({
233
233
 
234
234
  Make sure to set the callback URL in your OAuth app settings as `<your-domain>/auth/github`.
235
235
 
236
+ If the redirect URL mismatch in production, this means that the module cannot guess the right redirect URL. You can set the `NUXT_OAUTH_<PROVIDER>_REDIRECT_URL` env variable to overwrite the default one.
237
+
236
238
  ### Extend Session
237
239
 
238
240
  We leverage hooks to let you extend the session data with your own data or log when the user clears the session.
package/dist/module.d.mts CHANGED
@@ -2,6 +2,6 @@ import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  interface ModuleOptions {
4
4
  }
5
- declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
5
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
6
6
 
7
7
  export { type ModuleOptions, _default as default };
package/dist/module.d.ts CHANGED
@@ -2,6 +2,6 @@ import * as _nuxt_schema from '@nuxt/schema';
2
2
 
3
3
  interface ModuleOptions {
4
4
  }
5
- declare const _default: _nuxt_schema.NuxtModule<ModuleOptions>;
5
+ declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
6
6
 
7
7
  export { type ModuleOptions, _default as default };
package/dist/module.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "auth-utils",
3
3
  "configKey": "auth",
4
- "version": "0.3.3",
4
+ "version": "0.3.5",
5
5
  "builder": {
6
- "@nuxt/module-builder": "0.8.1",
6
+ "@nuxt/module-builder": "0.8.3",
7
7
  "unbuild": "2.0.0"
8
8
  }
9
9
  }
package/dist/module.mjs CHANGED
@@ -55,25 +55,30 @@ const module = defineNuxtModule({
55
55
  runtimeConfig.oauth = defu(runtimeConfig.oauth, {});
56
56
  runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
57
57
  clientId: "",
58
- clientSecret: ""
58
+ clientSecret: "",
59
+ redirectURL: ""
59
60
  });
60
61
  runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
61
62
  clientId: "",
62
- clientSecret: ""
63
+ clientSecret: "",
64
+ redirectURL: ""
63
65
  });
64
66
  runtimeConfig.oauth.google = defu(runtimeConfig.oauth.google, {
65
67
  clientId: "",
66
- clientSecret: ""
68
+ clientSecret: "",
69
+ redirectURL: ""
67
70
  });
68
71
  runtimeConfig.oauth.twitch = defu(runtimeConfig.oauth.twitch, {
69
72
  clientId: "",
70
- clientSecret: ""
73
+ clientSecret: "",
74
+ redirectURL: ""
71
75
  });
72
76
  runtimeConfig.oauth.auth0 = defu(runtimeConfig.oauth.auth0, {
73
77
  clientId: "",
74
78
  clientSecret: "",
75
79
  domain: "",
76
- audience: ""
80
+ audience: "",
81
+ redirectURL: ""
77
82
  });
78
83
  runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
79
84
  clientId: "",
@@ -83,55 +88,66 @@ const module = defineNuxtModule({
83
88
  authorizationURL: "",
84
89
  tokenURL: "",
85
90
  userURL: "",
86
- redirectUrl: ""
91
+ redirectURL: ""
87
92
  });
88
93
  runtimeConfig.oauth.discord = defu(runtimeConfig.oauth.discord, {
89
94
  clientId: "",
90
- clientSecret: ""
95
+ clientSecret: "",
96
+ redirectURL: ""
91
97
  });
92
98
  runtimeConfig.oauth.battledotnet = defu(runtimeConfig.oauth.battledotnet, {
93
99
  clientId: "",
94
- clientSecret: ""
100
+ clientSecret: "",
101
+ redirectURL: ""
95
102
  });
96
103
  runtimeConfig.oauth.keycloak = defu(runtimeConfig.oauth.keycloak, {
97
104
  clientId: "",
98
105
  clientSecret: "",
99
106
  serverUrl: "",
100
- realm: ""
107
+ realm: "",
108
+ redirectURL: ""
101
109
  });
102
110
  runtimeConfig.oauth.linkedin = defu(runtimeConfig.oauth.linkedin, {
103
111
  clientId: "",
104
- clientSecret: ""
112
+ clientSecret: "",
113
+ redirectURL: ""
105
114
  });
106
115
  runtimeConfig.oauth.cognito = defu(runtimeConfig.oauth.cognito, {
107
116
  clientId: "",
108
117
  clientSecret: "",
109
118
  region: "",
110
- userPoolId: ""
119
+ userPoolId: "",
120
+ redirectURL: ""
111
121
  });
112
122
  runtimeConfig.oauth.facebook = defu(runtimeConfig.oauth.facebook, {
113
123
  clientId: "",
114
- clientSecret: ""
124
+ clientSecret: "",
125
+ redirectURL: ""
115
126
  });
116
127
  runtimeConfig.oauth.paypal = defu(runtimeConfig.oauth.paypal, {
117
128
  clientId: "",
118
- clientSecret: ""
129
+ clientSecret: "",
130
+ redirectURL: ""
119
131
  });
120
132
  runtimeConfig.oauth.steam = defu(runtimeConfig.oauth.steam, {
121
- apiKey: ""
133
+ apiKey: "",
134
+ redirectURL: ""
122
135
  });
123
136
  runtimeConfig.oauth.x = defu(runtimeConfig.oauth.x, {
124
137
  clientId: "",
125
- clientSecret: ""
138
+ clientSecret: "",
139
+ redirectURL: ""
126
140
  });
127
141
  runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
128
142
  clientId: "",
129
143
  clientSecret: "",
130
- domain: ""
144
+ domain: "",
145
+ redirectURL: ""
131
146
  });
132
147
  runtimeConfig.oauth.yandex = defu(runtimeConfig.oauth.yandex, {
133
148
  clientId: "",
134
- clientSecret: ""
149
+ clientSecret: "",
150
+ redirectURL: ""
135
151
  });
136
152
  }
137
153
  });
@@ -1,6 +1,8 @@
1
1
  import { defineNuxtPlugin, useUserSession } from "#imports";
2
2
  export default defineNuxtPlugin(async (nuxtApp) => {
3
- if (!nuxtApp.payload.serverRendered || Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached)) {
3
+ if (!nuxtApp.payload.serverRendered) {
4
+ await useUserSession().fetch();
5
+ } else if (Boolean(nuxtApp.payload.prerenderedAt) || Boolean(nuxtApp.payload.isCached)) {
4
6
  nuxtApp.hook("app:mounted", async () => {
5
7
  await useUserSession().fetch();
6
8
  });
@@ -51,5 +51,10 @@ export interface OAuthAuth0Config {
51
51
  * @example { display: 'popup' }
52
52
  */
53
53
  authorizationParams?: Record<string, string>;
54
+ /**
55
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
56
+ * @default process.env.NUXT_OAUTH_AUTH0_REDIRECT_URL or current URL
57
+ */
58
+ redirectURL?: string;
54
59
  }
55
60
  export declare function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthAuth0Config>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -1,6 +1,7 @@
1
- import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, getRequestURL, sendRedirect } from "h3";
2
2
  import { withQuery, parsePath } from "ufo";
3
3
  import { defu } from "defu";
4
+ import { handleMissingConfiguration, handleAccessTokenErrorResponse } from "../utils.js";
4
5
  import { useRuntimeConfig } from "#imports";
5
6
  export function oauthAuth0EventHandler({ config, onSuccess, onError }) {
6
7
  return eventHandler(async (event) => {
@@ -9,16 +10,11 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }) {
9
10
  });
10
11
  const { code } = getQuery(event);
11
12
  if (!config.clientId || !config.clientSecret || !config.domain) {
12
- const error = createError({
13
- statusCode: 500,
14
- message: "Missing NUXT_OAUTH_AUTH0_CLIENT_ID or NUXT_OAUTH_AUTH0_CLIENT_SECRET or NUXT_OAUTH_AUTH0_DOMAIN env variables."
15
- });
16
- if (!onError) throw error;
17
- return onError(event, error);
13
+ return handleMissingConfiguration(event, "auth0", ["clientId", "clientSecret", "domain"], onError);
18
14
  }
19
15
  const authorizationURL = `https://${config.domain}/authorize`;
20
16
  const tokenURL = `https://${config.domain}/oauth/token`;
21
- const redirectUrl = getRequestURL(event).href;
17
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
22
18
  if (!code) {
23
19
  config.scope = config.scope || ["openid", "offline_access"];
24
20
  if (config.emailRequired && !config.scope.includes("email")) {
@@ -29,7 +25,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }) {
29
25
  withQuery(authorizationURL, {
30
26
  response_type: "code",
31
27
  client_id: config.clientId,
32
- redirect_uri: redirectUrl,
28
+ redirect_uri: redirectURL,
33
29
  scope: config.scope.join(" "),
34
30
  audience: config.audience || "",
35
31
  max_age: config.maxAge || 0,
@@ -49,7 +45,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }) {
49
45
  grant_type: "authorization_code",
50
46
  client_id: config.clientId,
51
47
  client_secret: config.clientSecret,
52
- redirect_uri: parsePath(redirectUrl).pathname,
48
+ redirect_uri: parsePath(redirectURL).pathname,
53
49
  code
54
50
  }
55
51
  }
@@ -57,13 +53,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }) {
57
53
  return { error };
58
54
  });
59
55
  if (tokens.error) {
60
- const error = createError({
61
- statusCode: 401,
62
- message: `Auth0 login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
63
- data: tokens
64
- });
65
- if (!onError) throw error;
66
- return onError(event, error);
56
+ return handleAccessTokenErrorResponse(event, "auth0", tokens, onError);
67
57
  }
68
58
  const tokenType = tokens.token_type;
69
59
  const accessToken = tokens.access_token;
@@ -39,5 +39,10 @@ export interface OAuthBattledotnetConfig {
39
39
  * @see https://develop.battle.net/documentation/guides/using-oauth/authorization-code-flow
40
40
  */
41
41
  authorizationParams?: Record<string, string>;
42
+ /**
43
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
44
+ * @default process.env.NUXT_OAUTH_BATTLEDOTNET_REDIRECT_URL or current URL
45
+ */
46
+ redirectURL?: string;
42
47
  }
43
48
  export declare function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBattledotnetConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -2,6 +2,7 @@ import { randomUUID } from "node:crypto";
2
2
  import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
3
3
  import { withQuery, parsePath } from "ufo";
4
4
  import { defu } from "defu";
5
+ import { handleAccessTokenErrorResponse, handleMissingConfiguration } from "../utils.js";
5
6
  import { useRuntimeConfig } from "#imports";
6
7
  export function oauthBattledotnetEventHandler({ config, onSuccess, onError }) {
7
8
  return eventHandler(async (event) => {
@@ -22,13 +23,14 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }) {
22
23
  return onError(event, error);
23
24
  }
24
25
  if (!config.clientId || !config.clientSecret) {
25
- const error = createError({
26
- statusCode: 500,
27
- message: "Missing NUXT_OAUTH_BATTLEDOTNET_CLIENT_ID or NUXT_OAUTH_BATTLEDOTNET_CLIENT_SECRET env variables."
28
- });
29
- if (!onError) throw error;
30
- return onError(event, error);
26
+ return handleMissingConfiguration(
27
+ event,
28
+ "battledotnet",
29
+ ["clientId", "clientSecret"],
30
+ onError
31
+ );
31
32
  }
33
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
32
34
  if (!code) {
33
35
  config.scope = config.scope || ["openid"];
34
36
  config.region = config.region || "EU";
@@ -36,12 +38,11 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }) {
36
38
  config.authorizationURL = "https://oauth.battlenet.com.cn/authorize";
37
39
  config.tokenURL = "https://oauth.battlenet.com.cn/token";
38
40
  }
39
- const redirectUrl2 = getRequestURL(event).href;
40
41
  return sendRedirect(
41
42
  event,
42
43
  withQuery(config.authorizationURL, {
43
44
  client_id: config.clientId,
44
- redirect_uri: redirectUrl2,
45
+ redirect_uri: redirectURL,
45
46
  scope: config.scope.join(" "),
46
47
  state: randomUUID(),
47
48
  // Todo: handle PKCE flow
@@ -50,7 +51,6 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }) {
50
51
  })
51
52
  );
52
53
  }
53
- const redirectUrl = getRequestURL(event).href;
54
54
  config.scope = config.scope || [];
55
55
  if (!config.scope.includes("openid")) {
56
56
  config.scope.push("openid");
@@ -68,20 +68,14 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }) {
68
68
  code,
69
69
  grant_type: "authorization_code",
70
70
  scope: config.scope.join(" "),
71
- redirect_uri: parsePath(redirectUrl).pathname
71
+ redirect_uri: parsePath(redirectURL).pathname
72
72
  }
73
73
  }
74
74
  ).catch((error) => {
75
75
  return { error };
76
76
  });
77
77
  if (tokens.error) {
78
- const error = createError({
79
- statusCode: 401,
80
- message: `Battle.net login failed: ${tokens.error || "Unknown error"}`,
81
- data: tokens
82
- });
83
- if (!onError) throw error;
84
- return onError(event, error);
78
+ return handleAccessTokenErrorResponse(event, "battle.net", tokens, onError);
85
79
  }
86
80
  const accessToken = tokens.access_token;
87
81
  const user = await $fetch("https://oauth.battle.net/userinfo", {
@@ -30,5 +30,15 @@ export interface OAuthCognitoConfig {
30
30
  * @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
31
31
  */
32
32
  authorizationParams?: Record<string, string>;
33
+ /**
34
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
35
+ * @default process.env.NUXT_OAUTH_COGNITO_REDIRECT_URL or current URL
36
+ */
37
+ redirectURL?: string;
38
+ /**
39
+ * AWS Cognito App Custom Domain – some pool configurations require this
40
+ * @default ''
41
+ */
42
+ domain?: string;
33
43
  }
34
44
  export declare function oauthCognitoEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthCognitoConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -1,6 +1,7 @@
1
- import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, getRequestURL, sendRedirect } from "h3";
2
2
  import { withQuery, parsePath } from "ufo";
3
3
  import { defu } from "defu";
4
+ import { handleAccessTokenErrorResponse, handleMissingConfiguration } from "../utils.js";
4
5
  import { useRuntimeConfig } from "#imports";
5
6
  export function oauthCognitoEventHandler({ config, onSuccess, onError }) {
6
7
  return eventHandler(async (event) => {
@@ -9,23 +10,19 @@ export function oauthCognitoEventHandler({ config, onSuccess, onError }) {
9
10
  });
10
11
  const { code } = getQuery(event);
11
12
  if (!config.clientId || !config.clientSecret || !config.userPoolId || !config.region) {
12
- const error = createError({
13
- statusCode: 500,
14
- message: "Missing NUXT_OAUTH_COGNITO_CLIENT_ID, NUXT_OAUTH_COGNITO_CLIENT_SECRET, NUXT_OAUTH_COGNITO_USER_POOL_ID, or NUXT_OAUTH_COGNITO_REGION env variables."
15
- });
16
- if (!onError) throw error;
17
- return onError(event, error);
13
+ return handleMissingConfiguration(event, "cognito", ["clientId", "clientSecret", "userPoolId", "region"], onError);
18
14
  }
19
- const authorizationURL = `https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/authorize`;
20
- const tokenURL = `https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/token`;
21
- const redirectUrl = getRequestURL(event).href;
15
+ const urlBase = config?.domain || `${config.userPoolId}.auth.${config.region}.amazoncognito.com`;
16
+ const authorizationURL = `https://${urlBase}/oauth2/authorize`;
17
+ const tokenURL = `https://${urlBase}/oauth2/token`;
18
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
22
19
  if (!code) {
23
20
  config.scope = config.scope || ["openid", "profile"];
24
21
  return sendRedirect(
25
22
  event,
26
23
  withQuery(authorizationURL, {
27
24
  client_id: config.clientId,
28
- redirect_uri: redirectUrl,
25
+ redirect_uri: redirectURL,
29
26
  response_type: "code",
30
27
  scope: config.scope.join(" "),
31
28
  ...config.authorizationParams
@@ -39,23 +36,17 @@ export function oauthCognitoEventHandler({ config, onSuccess, onError }) {
39
36
  headers: {
40
37
  "Content-Type": "application/x-www-form-urlencoded"
41
38
  },
42
- body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectUrl).pathname}&code=${code}`
39
+ body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectURL).pathname}&code=${code}`
43
40
  }
44
41
  ).catch((error) => {
45
42
  return { error };
46
43
  });
47
44
  if (tokens.error) {
48
- const error = createError({
49
- statusCode: 401,
50
- message: `Cognito login failed: ${tokens.error_description || "Unknown error"}`,
51
- data: tokens
52
- });
53
- if (!onError) throw error;
54
- return onError(event, error);
45
+ return handleAccessTokenErrorResponse(event, "cognito", tokens, onError);
55
46
  }
56
47
  const tokenType = tokens.token_type;
57
48
  const accessToken = tokens.access_token;
58
- const user = await $fetch(`https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/userInfo`, {
49
+ const user = await $fetch(`https://${urlBase}/oauth2/userInfo`, {
59
50
  headers: {
60
51
  Authorization: `${tokenType} ${accessToken}`
61
52
  }
@@ -44,5 +44,10 @@ export interface OAuthDiscordConfig {
44
44
  * @example { allow_signup: 'true' }
45
45
  */
46
46
  authorizationParams?: Record<string, string>;
47
+ /**
48
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
49
+ * @default process.env.NUXT_OAUTH_DISCORD_REDIRECT_URL or current URL
50
+ */
51
+ redirectURL?: string;
47
52
  }
48
53
  export declare function oauthDiscordEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthDiscordConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -1,6 +1,7 @@
1
- import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
1
+ import { eventHandler, getQuery, getRequestURL, sendRedirect } from "h3";
2
2
  import { withQuery, parseURL, stringifyParsedURL } from "ufo";
3
3
  import { defu } from "defu";
4
+ import { handleAccessTokenErrorResponse, handleMissingConfiguration } from "../utils.js";
4
5
  import { useRuntimeConfig } from "#imports";
5
6
  export function oauthDiscordEventHandler({ config, onSuccess, onError }) {
6
7
  return eventHandler(async (event) => {
@@ -12,14 +13,9 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }) {
12
13
  });
13
14
  const { code } = getQuery(event);
14
15
  if (!config.clientId || !config.clientSecret) {
15
- const error = createError({
16
- statusCode: 500,
17
- message: "Missing NUXT_OAUTH_DISCORD_CLIENT_ID or NUXT_OAUTH_DISCORD_CLIENT_SECRET env variables."
18
- });
19
- if (!onError) throw error;
20
- return onError(event, error);
16
+ return handleMissingConfiguration(event, "discord", ["clientId", "clientSecret"], onError);
21
17
  }
22
- const redirectUrl = getRequestURL(event).href;
18
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
23
19
  if (!code) {
24
20
  config.scope = config.scope || [];
25
21
  if (config.emailRequired && !config.scope.includes("email")) {
@@ -33,13 +29,13 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }) {
33
29
  withQuery(config.authorizationURL, {
34
30
  response_type: "code",
35
31
  client_id: config.clientId,
36
- redirect_uri: redirectUrl,
32
+ redirect_uri: redirectURL,
37
33
  scope: config.scope.join(" "),
38
34
  ...config.authorizationParams
39
35
  })
40
36
  );
41
37
  }
42
- const parsedRedirectUrl = parseURL(redirectUrl);
38
+ const parsedRedirectUrl = parseURL(redirectURL);
43
39
  parsedRedirectUrl.search = "";
44
40
  const tokens = await $fetch(
45
41
  config.tokenURL,
@@ -60,13 +56,7 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }) {
60
56
  return { error };
61
57
  });
62
58
  if (tokens.error) {
63
- const error = createError({
64
- statusCode: 401,
65
- message: `Discord login failed: ${tokens.error?.data?.error_description || "Unknown error"}`,
66
- data: tokens
67
- });
68
- if (!onError) throw error;
69
- return onError(event, error);
59
+ return handleAccessTokenErrorResponse(event, "discord", tokens, onError);
70
60
  }
71
61
  const accessToken = tokens.access_token;
72
62
  const user = await $fetch("https://discord.com/api/users/@me", {
@@ -39,5 +39,10 @@ export interface OAuthFacebookConfig {
39
39
  * @see https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
40
40
  */
41
41
  authorizationParams?: Record<string, string>;
42
+ /**
43
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
44
+ * @default process.env.NUXT_OAUTH_FACEBOOK_REDIRECT_URL or current URL
45
+ */
46
+ redirectURL?: string;
42
47
  }
43
48
  export declare function oauthFacebookEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthFacebookConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -7,6 +7,7 @@ import {
7
7
  } from "h3";
8
8
  import { withQuery } from "ufo";
9
9
  import { defu } from "defu";
10
+ import { handleAccessTokenErrorResponse, handleMissingConfiguration } from "../utils.js";
10
11
  import { useRuntimeConfig } from "#imports";
11
12
  export function oauthFacebookEventHandler({
12
13
  config,
@@ -30,21 +31,16 @@ export function oauthFacebookEventHandler({
30
31
  return onError(event, error);
31
32
  }
32
33
  if (!config.clientId) {
33
- const error = createError({
34
- statusCode: 500,
35
- message: "Missing NUXT_OAUTH_FACEBOOK_CLIENT_ID or NUXT_OAUTH_FACEBOOK_CLIENT_SECRET env variables."
36
- });
37
- if (!onError) throw error;
38
- return onError(event, error);
34
+ return handleMissingConfiguration(event, "facebook", ["clientId"], onError);
39
35
  }
40
- const redirectUrl = getRequestURL(event).href;
36
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
41
37
  if (!query.code) {
42
38
  config.scope = config.scope || [];
43
39
  return sendRedirect(
44
40
  event,
45
41
  withQuery(config.authorizationURL, {
46
42
  client_id: config.clientId,
47
- redirect_uri: redirectUrl,
43
+ redirect_uri: redirectURL,
48
44
  scope: config.scope.join(" ")
49
45
  })
50
46
  );
@@ -54,18 +50,12 @@ export function oauthFacebookEventHandler({
54
50
  body: {
55
51
  client_id: config.clientId,
56
52
  client_secret: config.clientSecret,
57
- redirect_uri: redirectUrl,
53
+ redirect_uri: redirectURL,
58
54
  code: query.code
59
55
  }
60
56
  });
61
57
  if (tokens.error) {
62
- const error = createError({
63
- statusCode: 401,
64
- message: `Facebook login failed: ${tokens.error || "Unknown error"}`,
65
- data: tokens
66
- });
67
- if (!onError) throw error;
68
- return onError(event, error);
58
+ return handleAccessTokenErrorResponse(event, "facebook", tokens, onError);
69
59
  }
70
60
  const accessToken = tokens.access_token;
71
61
  config.fields = config.fields || ["id", "name"];
@@ -38,5 +38,11 @@ export interface OAuthGitHubConfig {
38
38
  * @example { allow_signup: 'true' }
39
39
  */
40
40
  authorizationParams?: Record<string, string>;
41
+ /**
42
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
43
+ * @default process.env.NUXT_OAUTH_GITHUB_REDIRECT_URL
44
+ * @see https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps
45
+ */
46
+ redirectURL?: string;
41
47
  }
42
48
  export declare function oauthGitHubEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthGitHubConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;
@@ -1,6 +1,7 @@
1
1
  import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from "h3";
2
2
  import { withQuery } from "ufo";
3
3
  import { defu } from "defu";
4
+ import { handleAccessTokenErrorResponse, handleMissingConfiguration } from "../utils.js";
4
5
  import { useRuntimeConfig } from "#imports";
5
6
  export function oauthGitHubEventHandler({ config, onSuccess, onError }) {
6
7
  return eventHandler(async (event) => {
@@ -20,24 +21,19 @@ export function oauthGitHubEventHandler({ config, onSuccess, onError }) {
20
21
  return onError(event, error);
21
22
  }
22
23
  if (!config.clientId || !config.clientSecret) {
23
- const error = createError({
24
- statusCode: 500,
25
- message: "Missing NUXT_OAUTH_GITHUB_CLIENT_ID or NUXT_OAUTH_GITHUB_CLIENT_SECRET env variables."
26
- });
27
- if (!onError) throw error;
28
- return onError(event, error);
24
+ return handleMissingConfiguration(event, "github", ["clientId", "clientSecret"], onError);
29
25
  }
30
26
  if (!query.code) {
31
27
  config.scope = config.scope || [];
32
28
  if (config.emailRequired && !config.scope.includes("user:email")) {
33
29
  config.scope.push("user:email");
34
30
  }
35
- const redirectUrl = getRequestURL(event).href;
31
+ const redirectURL = config.redirectURL || getRequestURL(event).href;
36
32
  return sendRedirect(
37
33
  event,
38
34
  withQuery(config.authorizationURL, {
39
35
  client_id: config.clientId,
40
- redirect_uri: redirectUrl,
36
+ redirect_uri: redirectURL,
41
37
  scope: config.scope.join(" "),
42
38
  ...config.authorizationParams
43
39
  })
@@ -55,13 +51,7 @@ export function oauthGitHubEventHandler({ config, onSuccess, onError }) {
55
51
  }
56
52
  );
57
53
  if (tokens.error) {
58
- const error = createError({
59
- statusCode: 401,
60
- message: `GitHub login failed: ${tokens.error || "Unknown error"}`,
61
- data: tokens
62
- });
63
- if (!onError) throw error;
64
- return onError(event, error);
54
+ return handleAccessTokenErrorResponse(event, "github", tokens, onError);
65
55
  }
66
56
  const accessToken = tokens.access_token;
67
57
  const user = await $fetch("https://api.github.com/user", {
@@ -38,5 +38,10 @@ export interface OAuthGoogleConfig {
38
38
  * @example { access_type: 'offline' }
39
39
  */
40
40
  authorizationParams?: Record<string, string>;
41
+ /**
42
+ * Redirect URL to to allow overriding for situations like prod failing to determine public hostname
43
+ * @default process.env.NUXT_OAUTH_GOOGLE_REDIRECT_URL or current URL
44
+ */
45
+ redirectURL?: string;
41
46
  }
42
47
  export declare function oauthGoogleEventHandler({ config, onSuccess, onError, }: OAuthConfig<OAuthGoogleConfig>): import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void>>;