Skip to content

Commit

Permalink
feat: support redirectURL config for all providers
Browse files Browse the repository at this point in the history
* 🔧 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>
  • Loading branch information
acidjazz and atinux committed Aug 15, 2024
1 parent 0345169 commit cdca787
Show file tree
Hide file tree
Showing 19 changed files with 155 additions and 53 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,8 @@ export default oauthGitHubEventHandler({

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

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.

### Extend Session

We leverage hooks to let you extend the session data with your own data or log when the user clears the session.
Expand Down
18 changes: 17 additions & 1 deletion src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,28 +81,33 @@ export default defineNuxtModule<ModuleOptions>({
runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Spotify OAuth
runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Google OAuth
runtimeConfig.oauth.google = defu(runtimeConfig.oauth.google, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Twitch OAuth
runtimeConfig.oauth.twitch = defu(runtimeConfig.oauth.twitch, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Auth0 OAuth
runtimeConfig.oauth.auth0 = defu(runtimeConfig.oauth.auth0, {
clientId: '',
clientSecret: '',
domain: '',
audience: '',
redirectURL: '',
})
// Microsoft OAuth
runtimeConfig.oauth.microsoft = defu(runtimeConfig.oauth.microsoft, {
Expand All @@ -113,66 +118,77 @@ export default defineNuxtModule<ModuleOptions>({
authorizationURL: '',
tokenURL: '',
userURL: '',
redirectUrl: '',
redirectURL: '',
})
// Discord OAuth
runtimeConfig.oauth.discord = defu(runtimeConfig.oauth.discord, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Battle.net OAuth
runtimeConfig.oauth.battledotnet = defu(runtimeConfig.oauth.battledotnet, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Keycloak OAuth
runtimeConfig.oauth.keycloak = defu(runtimeConfig.oauth.keycloak, {
clientId: '',
clientSecret: '',
serverUrl: '',
realm: '',
redirectURL: '',
})
// LinkedIn OAuth
runtimeConfig.oauth.linkedin = defu(runtimeConfig.oauth.linkedin, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Cognito OAuth
runtimeConfig.oauth.cognito = defu(runtimeConfig.oauth.cognito, {
clientId: '',
clientSecret: '',
region: '',
userPoolId: '',
redirectURL: '',
})
// Facebook OAuth
runtimeConfig.oauth.facebook = defu(runtimeConfig.oauth.facebook, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// PayPal OAuth
runtimeConfig.oauth.paypal = defu(runtimeConfig.oauth.paypal, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// Steam OAuth
runtimeConfig.oauth.steam = defu(runtimeConfig.oauth.steam, {
apiKey: '',
redirectURL: '',
})
// X OAuth
runtimeConfig.oauth.x = defu(runtimeConfig.oauth.x, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
// XSUAA OAuth
runtimeConfig.oauth.xsuaa = defu(runtimeConfig.oauth.xsuaa, {
clientId: '',
clientSecret: '',
domain: '',
redirectURL: '',
})
// Yandex OAuth
runtimeConfig.oauth.yandex = defu(runtimeConfig.oauth.yandex, {
clientId: '',
clientSecret: '',
redirectURL: '',
})
},
})
11 changes: 8 additions & 3 deletions src/runtime/server/lib/oauth/auth0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ export interface OAuthAuth0Config {
* @example { display: 'popup' }
*/
authorizationParams?: Record<string, string>
/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_AUTH0_REDIRECT_URL or current URL
*/
redirectURL?: string
}

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

const redirectUrl = getRequestURL(event).href
const redirectURL = config.redirectURL || getRequestURL(event).href
if (!code) {
config.scope = config.scope || ['openid', 'offline_access']
if (config.emailRequired && !config.scope.includes('email')) {
Expand All @@ -89,7 +94,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConf
withQuery(authorizationURL as string, {
response_type: 'code',
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
audience: config.audience || '',
max_age: config.maxAge || 0,
Expand All @@ -112,7 +117,7 @@ export function oauthAuth0EventHandler({ config, onSuccess, onError }: OAuthConf
grant_type: 'authorization_code',
client_id: config.clientId,
client_secret: config.clientSecret,
redirect_uri: parsePath(redirectUrl).pathname,
redirect_uri: parsePath(redirectURL).pathname,
code,
},
},
Expand Down
12 changes: 8 additions & 4 deletions src/runtime/server/lib/oauth/battledotnet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@ export interface OAuthBattledotnetConfig {
* @see https://develop.battle.net/documentation/guides/using-oauth/authorization-code-flow
*/
authorizationParams?: Record<string, string>
/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_BATTLEDOTNET_REDIRECT_URL or current URL
*/
redirectURL?: string
}

export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthBattledotnetConfig>) {
Expand Down Expand Up @@ -78,6 +83,7 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
return onError(event, error)
}

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

// Redirect to Battle.net Oauth page
const redirectUrl = getRequestURL(event).href
return sendRedirect(
event,
withQuery(config.authorizationURL as string, {
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
state: randomUUID(), // Todo: handle PKCE flow
response_type: 'code',
Expand All @@ -102,7 +107,6 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
)
}

const redirectUrl = getRequestURL(event).href
config.scope = config.scope || []
if (!config.scope.includes('openid')) {
config.scope.push('openid')
Expand All @@ -124,7 +128,7 @@ export function oauthBattledotnetEventHandler({ config, onSuccess, onError }: OA
code,
grant_type: 'authorization_code',
scope: config.scope.join(' '),
redirect_uri: parsePath(redirectUrl).pathname,
redirect_uri: parsePath(redirectURL).pathname,
},
},
).catch((error) => {
Expand Down
11 changes: 8 additions & 3 deletions src/runtime/server/lib/oauth/cognito.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ export interface OAuthCognitoConfig {
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
*/
authorizationParams?: Record<string, string>
/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_COGNITO_REDIRECT_URL or current URL
*/
redirectURL?: string
}

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

const redirectUrl = getRequestURL(event).href
const redirectURL = config.redirectURL || getRequestURL(event).href
if (!code) {
config.scope = config.scope || ['openid', 'profile']
// Redirect to Cognito login page
return sendRedirect(
event,
withQuery(authorizationURL as string, {
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
response_type: 'code',
scope: config.scope.join(' '),
...config.authorizationParams,
Expand All @@ -82,7 +87,7 @@ export function oauthCognitoEventHandler({ config, onSuccess, onError }: OAuthCo
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectUrl).pathname}&code=${code}`,
body: `grant_type=authorization_code&client_id=${config.clientId}&client_secret=${config.clientSecret}&redirect_uri=${parsePath(redirectURL).pathname}&code=${code}`,
},
).catch((error) => {
return { error }
Expand Down
11 changes: 8 additions & 3 deletions src/runtime/server/lib/oauth/discord.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ export interface OAuthDiscordConfig {
* @example { allow_signup: 'true' }
*/
authorizationParams?: Record<string, string>
/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_DISCORD_REDIRECT_URL or current URL
*/
redirectURL?: string
}

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

const redirectUrl = getRequestURL(event).href
const redirectURL = config.redirectURL || getRequestURL(event).href
if (!code) {
config.scope = config.scope || []
if (config.emailRequired && !config.scope.includes('email')) {
Expand All @@ -88,14 +93,14 @@ export function oauthDiscordEventHandler({ config, onSuccess, onError }: OAuthCo
withQuery(config.authorizationURL as string, {
response_type: 'code',
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
...config.authorizationParams,
}),
)
}

const parsedRedirectUrl = parseURL(redirectUrl)
const parsedRedirectUrl = parseURL(redirectURL)
parsedRedirectUrl.search = ''
// TODO: improve typing
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
12 changes: 8 additions & 4 deletions src/runtime/server/lib/oauth/facebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ export interface OAuthFacebookConfig {
* @see https://developers.facebook.com/docs/facebook-login/guides/advanced/manual-flow/
*/
authorizationParams?: Record<string, string>
/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_FACEBOOK_REDIRECT_URL or current URL
*/
redirectURL?: string
}

export function oauthFacebookEventHandler({
Expand Down Expand Up @@ -90,16 +95,15 @@ export function oauthFacebookEventHandler({
return onError(event, error)
}

const redirectUrl = getRequestURL(event).href

const redirectURL = config.redirectURL || getRequestURL(event).href
if (!query.code) {
config.scope = config.scope || []
// Redirect to Facebook Oauth page
return sendRedirect(
event,
withQuery(config.authorizationURL as string, {
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
}),
)
Expand All @@ -112,7 +116,7 @@ export function oauthFacebookEventHandler({
body: {
client_id: config.clientId,
client_secret: config.clientSecret,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
code: query.code,
},
})
Expand Down
11 changes: 9 additions & 2 deletions src/runtime/server/lib/oauth/github.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,13 @@ export interface OAuthGitHubConfig {
* @example { allow_signup: 'true' }
*/
authorizationParams?: Record<string, string>

/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_GITHUB_REDIRECT_URL
* @see https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/differences-between-github-apps-and-oauth-apps
*/
redirectURL?: string
}

export function oauthGitHubEventHandler({ config, onSuccess, onError }: OAuthConfig<OAuthGitHubConfig>) {
Expand Down Expand Up @@ -83,12 +90,12 @@ export function oauthGitHubEventHandler({ config, onSuccess, onError }: OAuthCon
config.scope.push('user:email')
}
// Redirect to GitHub Oauth page
const redirectUrl = getRequestURL(event).href
const redirectURL = config.redirectURL || getRequestURL(event).href
return sendRedirect(
event,
withQuery(config.authorizationURL as string, {
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
...config.authorizationParams,
}),
Expand Down
12 changes: 9 additions & 3 deletions src/runtime/server/lib/oauth/google.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export interface OAuthGoogleConfig {
* @example { access_type: 'offline' }
*/
authorizationParams?: Record<string, string>

/**
* Redirect URL to to allow overriding for situations like prod failing to determine public hostname
* @default process.env.NUXT_OAUTH_GOOGLE_REDIRECT_URL or current URL
*/
redirectURL?: string
}

export function oauthGoogleEventHandler({
Expand All @@ -81,7 +87,7 @@ export function oauthGoogleEventHandler({
return onError(event, error)
}

const redirectUrl = getRequestURL(event).href
const redirectURL = config.redirectURL || getRequestURL(event).href
if (!code) {
config.scope = config.scope || ['email', 'profile']
// Redirect to Google Oauth page
Expand All @@ -90,7 +96,7 @@ export function oauthGoogleEventHandler({
withQuery(config.authorizationURL as string, {
response_type: 'code',
client_id: config.clientId,
redirect_uri: redirectUrl,
redirect_uri: redirectURL,
scope: config.scope.join(' '),
...config.authorizationParams,
}),
Expand All @@ -101,7 +107,7 @@ export function oauthGoogleEventHandler({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const body: any = {
grant_type: 'authorization_code',
redirect_uri: parsePath(redirectUrl).pathname,
redirect_uri: parsePath(redirectURL).pathname,
client_id: config.clientId,
client_secret: config.clientSecret,
code,
Expand Down
Loading

0 comments on commit cdca787

Please sign in to comment.