Skip to content

Commit ffecb82

Browse files
Azurencyatinux
andauthored
feat: added auth0 as oauth provider (#6)
* feat: added auth0 as oauth provider * chore: update * update readme --------- Co-authored-by: Sébastien Chopin <[email protected]>
1 parent b460d12 commit ffecb82

File tree

8 files changed

+179
-13
lines changed

8 files changed

+179
-13
lines changed

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Minimalist Authentication module for Nuxt exposing Vue composables and server ut
1515
## Features
1616

1717
- Secured & sealed cookies sessions
18-
- OAuth Providers
18+
- [OAuth Providers](#supported-oauth-providers)
1919

2020
## Requirements
2121

@@ -145,11 +145,13 @@ It can also be set using environment variables:
145145
- `NUXT_OAUTH_<PROVIDER>_CLIENT_ID`
146146
- `NUXT_OAUTH_<PROVIDER>_CLIENT_SECRET`
147147

148-
Supported providers:
148+
#### Supported OAuth Providers
149+
149150
- GitHub
150151
- Spotify
151152
- Google
152153
- Twitch
154+
- Auth0
153155

154156
You can add your favorite provider by creating a new file in [src/runtime/server/lib/oauth/](./src/runtime/server/lib/oauth/).
155157

playground/.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,7 @@ NUXT_OAUTH_GOOGLE_CLIENT_SECRET=
1111
# Twitch OAuth
1212
NUXT_OAUTH_TWITCH_CLIENT_ID=
1313
NUXT_OAUTH_TWITCH_CLIENT_SECRET=
14+
# Auth0 OAuth
15+
NUXT_OAUTH_AUTH0_CLIENT_ID=
16+
NUXT_OAUTH_AUTH0_CLIENT_SECRET=
17+
NUXT_OAUTH_AUTH0_DOMAIN=

playground/app.vue

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,22 +36,32 @@ const { loggedIn, session, clear } = useUserSession()
3636
Login with Google
3737
</UButton>
3838
<UButton
39-
v-if="loggedIn"
39+
v-if="!loggedIn || !session.user.twitch"
40+
to="/auth/twitch"
41+
icon="i-simple-icons-twitch"
42+
external
4043
color="gray"
4144
size="xs"
42-
@click="clear"
4345
>
44-
Logout
46+
Login with Twitch
4547
</UButton>
4648
<UButton
47-
v-if="!loggedIn || !session.user.twitch"
48-
to="/auth/twitch"
49-
icon="i-simple-icons-twitch"
49+
v-if="!loggedIn || !session.user.auth0"
50+
to="/auth/auth0"
51+
icon="i-simple-icons-auth0"
5052
external
5153
color="gray"
5254
size="xs"
5355
>
54-
Login with Twitch
56+
Login with Auth0
57+
</UButton>
58+
<UButton
59+
v-if="loggedIn"
60+
color="gray"
61+
size="xs"
62+
@click="clear"
63+
>
64+
Logout
5565
</UButton>
5666
</template>
5767
</UHeader>

playground/auth.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ declare module '#auth-utils' {
55
github?: any
66
google?: any
77
twitch?: any
8+
auth0?: any
89
}
910
loggedInAt: number
1011
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export default oauth.auth0EventHandler({
2+
config: {
3+
emailRequired: true,
4+
},
5+
async onSuccess(event, { user }) {
6+
await setUserSession(event, {
7+
user: {
8+
auth0: user,
9+
},
10+
loggedInAt: Date.now()
11+
})
12+
13+
return sendRedirect(event, '/')
14+
}
15+
})

src/module.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,25 +69,31 @@ export default defineNuxtModule<ModuleOptions>({
6969
})
7070
// OAuth settings
7171
runtimeConfig.oauth = defu(runtimeConfig.oauth, {})
72-
// GitHub Oauth
72+
// GitHub OAuth
7373
runtimeConfig.oauth.github = defu(runtimeConfig.oauth.github, {
7474
clientId: '',
7575
clientSecret: ''
7676
})
77-
// Spotify Oauth
77+
// Spotify OAuth
7878
runtimeConfig.oauth.spotify = defu(runtimeConfig.oauth.spotify, {
7979
clientId: '',
8080
clientSecret: ''
8181
})
82-
// Google Oauth
82+
// Google OAuth
8383
runtimeConfig.oauth.google = defu(runtimeConfig.oauth.google, {
8484
clientId: '',
8585
clientSecret: ''
8686
})
87-
// Twitch Oauth
87+
// Twitch OAuth
8888
runtimeConfig.oauth.twitch = defu(runtimeConfig.oauth.twitch, {
8989
clientId: '',
9090
clientSecret: ''
9191
})
92+
// Auth0 OAuth
93+
runtimeConfig.oauth.auth0 = defu(runtimeConfig.oauth.auth0, {
94+
clientId: '',
95+
clientSecret: '',
96+
domain: ''
97+
})
9298
}
9399
})

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

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import type { H3Event, H3Error } from 'h3'
2+
import { eventHandler, createError, getQuery, getRequestURL, sendRedirect } from 'h3'
3+
import { withQuery, parsePath } from 'ufo'
4+
import { ofetch } from 'ofetch'
5+
import { defu } from 'defu'
6+
import { useRuntimeConfig } from '#imports'
7+
8+
export interface OAuthAuth0Config {
9+
/**
10+
* Auth0 OAuth Client ID
11+
* @default process.env.NUXT_OAUTH_AUTH0_CLIENT_ID
12+
*/
13+
clientId?: string
14+
/**
15+
* Auth0 OAuth Client Secret
16+
* @default process.env.NUXT_OAUTH_AUTH0_CLIENT_SECRET
17+
*/
18+
clientSecret?: string
19+
/**
20+
* Auth0 OAuth Issuer
21+
* @default process.env.NUXT_OAUTH_AUTH0_DOMAIN
22+
*/
23+
domain?: string
24+
/**
25+
* Auth0 OAuth Audience
26+
* @default process.env.NUXT_OAUTH_AUTH0_AUDIENCE
27+
*/
28+
audience?: string
29+
/**
30+
* Auth0 OAuth Scope
31+
* @default []
32+
* @see https://auth0.com/docs/get-started/apis/scopes/openid-connect-scopes
33+
* @example ['openid']
34+
*/
35+
scope?: string[]
36+
/**
37+
* Require email from user, adds the ['email'] scope if not present
38+
* @default false
39+
*/
40+
emailRequired?: boolean
41+
}
42+
43+
interface OAuthConfig {
44+
config?: OAuthAuth0Config
45+
onSuccess: (event: H3Event, result: { user: any, tokens: any }) => Promise<void> | void
46+
onError?: (event: H3Event, error: H3Error) => Promise<void> | void
47+
}
48+
49+
export function auth0EventHandler({ config, onSuccess, onError }: OAuthConfig) {
50+
return eventHandler(async (event: H3Event) => {
51+
// @ts-ignore
52+
config = defu(config, useRuntimeConfig(event).oauth?.auth0) as OAuthAuth0Config
53+
const { code } = getQuery(event)
54+
55+
if (!config.clientId || !config.clientSecret || !config.domain) {
56+
const error = createError({
57+
statusCode: 500,
58+
message: 'Missing NUXT_OAUTH_AUTH0_CLIENT_ID or NUXT_OAUTH_AUTH0_CLIENT_SECRET or NUXT_OAUTH_AUTH0_DOMAIN env variables.'
59+
})
60+
if (!onError) throw error
61+
return onError(event, error)
62+
}
63+
const authorizationURL = `https://${config.domain}/authorize`
64+
const tokenURL = `https://${config.domain}/oauth/token`
65+
66+
const redirectUrl = getRequestURL(event).href
67+
if (!code) {
68+
config.scope = config.scope || ['openid', 'offline_access']
69+
if (config.emailRequired && !config.scope.includes('email')) {
70+
config.scope.push('email')
71+
}
72+
// Redirect to Auth0 Oauth page
73+
return sendRedirect(
74+
event,
75+
withQuery(authorizationURL as string, {
76+
response_type: 'code',
77+
client_id: config.clientId,
78+
redirect_uri: redirectUrl,
79+
scope: config.scope.join(' '),
80+
audience: config.audience || '',
81+
})
82+
)
83+
}
84+
85+
const tokens: any = await ofetch(
86+
tokenURL as string,
87+
{
88+
method: 'POST',
89+
headers: {
90+
'Content-Type': 'application/json'
91+
},
92+
body: {
93+
grant_type: 'authorization_code',
94+
client_id: config.clientId,
95+
client_secret: config.clientSecret,
96+
redirect_uri: parsePath(redirectUrl).pathname,
97+
code,
98+
}
99+
}
100+
).catch(error => {
101+
return { error }
102+
})
103+
if (tokens.error) {
104+
const error = createError({
105+
statusCode: 401,
106+
message: `Auth0 login failed: ${tokens.error?.data?.error_description || 'Unknown error'}`,
107+
data: tokens
108+
})
109+
if (!onError) throw error
110+
return onError(event, error)
111+
}
112+
113+
const tokenType = tokens.token_type
114+
const accessToken = tokens.access_token
115+
const user: any = await ofetch(`https://${config.domain}/userinfo`, {
116+
headers: {
117+
Authorization: `${tokenType} ${accessToken}`
118+
}
119+
})
120+
121+
return onSuccess(event, {
122+
tokens,
123+
user
124+
})
125+
})
126+
}

src/runtime/server/utils/oauth.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { githubEventHandler } from '../lib/oauth/github'
22
import { googleEventHandler } from '../lib/oauth/google'
33
import { spotifyEventHandler } from '../lib/oauth/spotify'
44
import { twitchEventHandler } from '../lib/oauth/twitch'
5+
import { auth0EventHandler } from '../lib/oauth/auth0'
56

67
export const oauth = {
78
githubEventHandler,
89
spotifyEventHandler,
910
googleEventHandler,
1011
twitchEventHandler,
12+
auth0EventHandler
1113
}

0 commit comments

Comments
 (0)