Skip to content

Commit cdca787

Browse files
acidjazzatinux
andauthored
feat: support redirectURL config for all providers
* 🔧 attempt at allowing a configurable redirectUrl for google * 🚧 temporarily pull our extend since it breaks * ♻️ put extends back * ✨ proper override just like microsoft * 🔧 GitHub needs this as well * chore: add redirectURL support for all providers * chore: update doc --------- Co-authored-by: Sébastien Chopin <seb@nuxt.com>
1 parent 0345169 commit cdca787

19 files changed

+155
-53
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ export default oauthGitHubEventHandler({
233233

234234
Make sure to set the callback URL in your OAuth app settings as `<your-domain>/auth/github`.
235235

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+
236238
### Extend Session
237239

238240
We leverage hooks to let you extend the session data with your own data or log when the user clears the session.

src/module.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,28 +81,33 @@ export default defineNuxtModule<ModuleOptions>({
8181
runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
8282
clientId: '',
8383
clientSecret: '',
84+
redirectURL: '',
8485
})
8586
// Spotify OAuth
8687
runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
8788
clientId: '',
8889
clientSecret: '',
90+
redirectURL: '',
8991
})
9092
// Google OAuth
9193
runtimeConfig.oauth.google = defu(runtimeConfig.oauth.google, {
9294
clientId: '',
9395
clientSecret: '',
96+
redirectURL: '',
9497
})
9598
// Twitch OAuth
9699
runtimeConfig.oauth.twitch = defu(runtimeConfig.oauth.twitch, {
97100
clientId: '',
98101
clientSecret: '',
102+
redirectURL: '',
99103
})
100104
// Auth0 OAuth
101105
runtimeConfig.oauth.auth0 = defu(runtimeConfig.oauth.auth0, {
102106
clientId: '',
103107
clientSecret: '',
104108
domain: '',
105109
audience: '',
110+
redirectURL: '',
106111
})
107112
// Microsoft OAuth
108113
runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
@@ -113,66 +118,77 @@ export default defineNuxtModule<ModuleOptions>({
113118
authorizationURL: '',
114119
tokenURL: '',
115120
userURL: '',
116-
redirectUrl: '',
121+
redirectURL: '',
117122
})
118123
// Discord OAuth
119124
runtimeConfig.oauth.discord = defu(runtimeConfig.oauth.discord, {
120125
clientId: '',
121126
clientSecret: '',
127+
redirectURL: '',
122128
})
123129
// Battle.net OAuth
124130
runtimeConfig.oauth.battledotnet = defu(runtimeConfig.oauth.battledotnet, {
125131
clientId: '',
126132
clientSecret: '',
133+
redirectURL: '',
127134
})
128135
// Keycloak OAuth
129136
runtimeConfig.oauth.keycloak = defu(runtimeConfig.oauth.keycloak, {
130137
clientId: '',
131138
clientSecret: '',
132139
serverUrl: '',
133140
realm: '',
141+
redirectURL: '',
134142
})
135143
// LinkedIn OAuth
136144
runtimeConfig.oauth.linkedin = defu(runtimeConfig.oauth.linkedin, {
137145
clientId: '',
138146
clientSecret: '',
147+
redirectURL: '',
139148
})
140149
// Cognito OAuth
141150
runtimeConfig.oauth.cognito = defu(runtimeConfig.oauth.cognito, {
142151
clientId: '',
143152
clientSecret: '',
144153
region: '',
145154
userPoolId: '',
155+
redirectURL: '',
146156
})
147157
// Facebook OAuth
148158
runtimeConfig.oauth.facebook = defu(runtimeConfig.oauth.facebook, {
149159
clientId: '',
150160
clientSecret: '',
161+
redirectURL: '',
151162
})
152163
// PayPal OAuth
153164
runtimeConfig.oauth.paypal = defu(runtimeConfig.oauth.paypal, {
154165
clientId: '',
155166
clientSecret: '',
167+
redirectURL: '',
156168
})
157169
// Steam OAuth
158170
runtimeConfig.oauth.steam = defu(runtimeConfig.oauth.steam, {
159171
apiKey: '',
172+
redirectURL: '',
160173
})
161174
// X OAuth
162175
runtimeConfig.oauth.x = defu(runtimeConfig.oauth.x, {
163176
clientId: '',
164177
clientSecret: '',
178+
redirectURL: '',
165179
})
166180
// XSUAA OAuth
167181
runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
168182
clientId: '',
169183
clientSecret: '',
170184
domain: '',
185+
redirectURL: '',
171186
})
172187
// Yandex OAuth
173188
runtimeConfig.oauth.yandex = defu(runtimeConfig.oauth.yandex, {
174189
clientId: '',
175190
clientSecret: '',
191+
redirectURL: '',
176192
})
177193
},
178194
})

src/runtime/server/lib/oauth/auth0.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ export interface OAuthAuth0Config {
5757
* @example { display: 'popup' }
5858
*/
5959
authorizationParams?: Record<string, string>
60+
/**
61+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
62+
* @default process.env.NUXT_OAUTH_AUTH0_REDIRECT_URL or current URL
63+
*/
64+
redirectURL?: string
6065
}
6166

6267
export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthAuth0Config>) {
@@ -77,7 +82,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConf
7782
const authorizationURL = `https://${config.domain}/authorize`
7883
const tokenURL = `https://${config.domain}/oauth/token`
7984

80-
const redirectUrl = getRequestURL(event).href
85+
const redirectURL = config.redirectURL || getRequestURL(event).href
8186
if (!code) {
8287
config.scope = config.scope || ['openid', 'offline_access']
8388
if (config.emailRequired && !config.scope.includes('email')) {
@@ -89,7 +94,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConf
8994
withQuery(authorizationURL as string, {
9095
response_type: 'code',
9196
client_id: config.clientId,
92-
redirect_uri: redirectUrl,
97+
redirect_uri: redirectURL,
9398
scope: config.scope.join(' '),
9499
audience: config.audience || '',
95100
max_age: config.maxAge || 0,
@@ -112,7 +117,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConf
112117
grant_type: 'authorization_code',
113118
client_id: config.clientId,
114119
client_secret: config.clientSecret,
115-
redirect_uri: parsePath(redirectUrl).pathname,
120+
redirect_uri: parsePath(redirectURL).pathname,
116121
code,
117122
},
118123
},

src/runtime/server/lib/oauth/battledotnet.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,11 @@ export interface OAuthBattledotnetConfig {
4646
* @see https://develop.battle.net/documentation/guides/using-oauth/authorization-code-flow
4747
*/
4848
authorizationParams?: Record<string, string>
49+
/**
50+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
51+
* @default process.env.NUXT_OAUTH_BATTLEDOTNET_REDIRECT_URL or current URL
52+
*/
53+
redirectURL?: string
4954
}
5055

5156
export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBattledotnetConfig>) {
@@ -78,6 +83,7 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
7883
return onError(event, error)
7984
}
8085

86+
const redirectURL = config.redirectURL || getRequestURL(event).href
8187
if (!code) {
8288
config.scope = config.scope || ['openid']
8389
config.region = config.region || 'EU'
@@ -88,12 +94,11 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
8894
}
8995

9096
// Redirect to Battle.net Oauth page
91-
const redirectUrl = getRequestURL(event).href
9297
return sendRedirect(
9398
event,
9499
withQuery(config.authorizationURL as string, {
95100
client_id: config.clientId,
96-
redirect_uri: redirectUrl,
101+
redirect_uri: redirectURL,
97102
scope: config.scope.join(' '),
98103
state: randomUUID(), // Todo: handle PKCE flow
99104
response_type: 'code',
@@ -102,7 +107,6 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
102107
)
103108
}
104109

105-
const redirectUrl = getRequestURL(event).href
106110
config.scope = config.scope || []
107111
if (!config.scope.includes('openid')) {
108112
config.scope.push('openid')
@@ -124,7 +128,7 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
124128
code,
125129
grant_type: 'authorization_code',
126130
scope: config.scope.join(' '),
127-
redirect_uri: parsePath(redirectUrl).pathname,
131+
redirect_uri: parsePath(redirectURL).pathname,
128132
},
129133
},
130134
).catch((error) => {

src/runtime/server/lib/oauth/cognito.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ export interface OAuthCognitoConfig {
3636
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
3737
*/
3838
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_COGNITO_REDIRECT_URL or current URL
42+
*/
43+
redirectURL?: string
3944
}
4045

4146
export function oauthCognitoEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthCognitoConfig>) {
@@ -57,15 +62,15 @@ export function oauthCognitoEventHandler({ config, onSuccess, onError }: OAuthCo
5762
const authorizationURL = `https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/authorize`
5863
const tokenURL = `https://${config.userPoolId}.auth.${config.region}.amazoncognito.com/oauth2/token`
5964

60-
const redirectUrl = getRequestURL(event).href
65+
const redirectURL = config.redirectURL || getRequestURL(event).href
6166
if (!code) {
6267
config.scope = config.scope || ['openid', 'profile']
6368
// Redirect to Cognito login page
6469
return sendRedirect(
6570
event,
6671
withQuery(authorizationURL as string, {
6772
client_id: config.clientId,
68-
redirect_uri: redirectUrl,
73+
redirect_uri: redirectURL,
6974
response_type: 'code',
7075
scope: config.scope.join(' '),
7176
...config.authorizationParams,
@@ -82,7 +87,7 @@ export function oauthCognitoEventHandler({ config, onSuccess, onError }: OAuthCo
8287
headers: {
8388
'Content-Type': 'application/x-www-form-urlencoded',
8489
},
85-
body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectUrl).pathname}&code=${code}`,
90+
body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectURL).pathname}&code=${code}`,
8691
},
8792
).catch((error) => {
8893
return { error }

src/runtime/server/lib/oauth/discord.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ export interface OAuthDiscordConfig {
5151
* @example { allow_signup: 'true' }
5252
*/
5353
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_DISCORD_REDIRECT_URL or current URL
57+
*/
58+
redirectURL?: string
5459
}
5560

5661
export function oauthDiscordEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthDiscordConfig>) {
@@ -72,7 +77,7 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }: OAuthCo
7277
return onError(event, error)
7378
}
7479

75-
const redirectUrl = getRequestURL(event).href
80+
const redirectURL = config.redirectURL || getRequestURL(event).href
7681
if (!code) {
7782
config.scope = config.scope || []
7883
if (config.emailRequired && !config.scope.includes('email')) {
@@ -88,14 +93,14 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }: OAuthCo
8893
withQuery(config.authorizationURL as string, {
8994
response_type: 'code',
9095
client_id: config.clientId,
91-
redirect_uri: redirectUrl,
96+
redirect_uri: redirectURL,
9297
scope: config.scope.join(' '),
9398
...config.authorizationParams,
9499
}),
95100
)
96101
}
97102

98-
const parsedRedirectUrl = parseURL(redirectUrl)
103+
const parsedRedirectUrl = parseURL(redirectURL)
99104
parsedRedirectUrl.search = ''
100105
// TODO: improve typing
101106
// eslint-disable-next-line @typescript-eslint/no-explicit-any

src/runtime/server/lib/oauth/facebook.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ export interface OAuthFacebookConfig {
5555
* @see https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
5656
*/
5757
authorizationParams?: Record<string, string>
58+
/**
59+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
60+
* @default process.env.NUXT_OAUTH_FACEBOOK_REDIRECT_URL or current URL
61+
*/
62+
redirectURL?: string
5863
}
5964

6065
export function oauthFacebookEventHandler({
@@ -90,16 +95,15 @@ export function oauthFacebookEventHandler({
9095
return onError(event, error)
9196
}
9297

93-
const redirectUrl = getRequestURL(event).href
94-
98+
const redirectURL = config.redirectURL || getRequestURL(event).href
9599
if (!query.code) {
96100
config.scope = config.scope || []
97101
// Redirect to Facebook Oauth page
98102
return sendRedirect(
99103
event,
100104
withQuery(config.authorizationURL as string, {
101105
client_id: config.clientId,
102-
redirect_uri: redirectUrl,
106+
redirect_uri: redirectURL,
103107
scope: config.scope.join(' '),
104108
}),
105109
)
@@ -112,7 +116,7 @@ export function oauthFacebookEventHandler({
112116
body: {
113117
client_id: config.clientId,
114118
client_secret: config.clientSecret,
115-
redirect_uri: redirectUrl,
119+
redirect_uri: redirectURL,
116120
code: query.code,
117121
},
118122
})

src/runtime/server/lib/oauth/github.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,13 @@ export interface OAuthGitHubConfig {
4747
* @example { allow_signup: 'true' }
4848
*/
4949
authorizationParams?: Record<string, string>
50+
51+
/**
52+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
53+
* @default process.env.NUXT_OAUTH_GITHUB_REDIRECT_URL
54+
* @see https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps
55+
*/
56+
redirectURL?: string
5057
}
5158

5259
export function oauthGitHubEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthGitHubConfig>) {
@@ -83,12 +90,12 @@ export function oauthGitHubEventHandler({ config, onSuccess, onError }: OAuthCon
8390
config.scope.push('user:email')
8491
}
8592
// Redirect to GitHub Oauth page
86-
const redirectUrl = getRequestURL(event).href
93+
const redirectURL = config.redirectURL || getRequestURL(event).href
8794
return sendRedirect(
8895
event,
8996
withQuery(config.authorizationURL as string, {
9097
client_id: config.clientId,
91-
redirect_uri: redirectUrl,
98+
redirect_uri: redirectURL,
9299
scope: config.scope.join(' '),
93100
...config.authorizationParams,
94101
}),

src/runtime/server/lib/oauth/google.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ export interface OAuthGoogleConfig {
5656
* @example { access_type: 'offline' }
5757
*/
5858
authorizationParams?: Record<string, string>
59+
60+
/**
61+
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
62+
* @default process.env.NUXT_OAUTH_GOOGLE_REDIRECT_URL or current URL
63+
*/
64+
redirectURL?: string
5965
}
6066

6167
export function oauthGoogleEventHandler({
@@ -81,7 +87,7 @@ export function oauthGoogleEventHandler({
8187
return onError(event, error)
8288
}
8389

84-
const redirectUrl = getRequestURL(event).href
90+
const redirectURL = config.redirectURL || getRequestURL(event).href
8591
if (!code) {
8692
config.scope = config.scope || ['email', 'profile']
8793
// Redirect to Google Oauth page
@@ -90,7 +96,7 @@ export function oauthGoogleEventHandler({
9096
withQuery(config.authorizationURL as string, {
9197
response_type: 'code',
9298
client_id: config.clientId,
93-
redirect_uri: redirectUrl,
99+
redirect_uri: redirectURL,
94100
scope: config.scope.join(' '),
95101
...config.authorizationParams,
96102
}),
@@ -101,7 +107,7 @@ export function oauthGoogleEventHandler({
101107
// eslint-disable-next-line @typescript-eslint/no-explicit-any
102108
const body: any = {
103109
grant_type: 'authorization_code',
104-
redirect_uri: parsePath(redirectUrl).pathname,
110+
redirect_uri: parsePath(redirectURL).pathname,
105111
client_id: config.clientId,
106112
client_secret: config.clientSecret,
107113
code,

0 commit comments

Comments
 (0)