Skip to content
This repository was archived by the owner on Nov 21, 2025. It is now read-only.

Commit 13d9d99

Browse files
fix: add state and code verification, more logs to debug issues
1 parent df5e744 commit 13d9d99

File tree

3 files changed

+70
-11
lines changed

3 files changed

+70
-11
lines changed

.env.example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ AUTH0_CLIENT_SECRET=
66
VITE_AUTH0_REDIRECT_URI=http://localhost:8080/api/auth/callback
77
VITE_AUTH0_MULTI_TENANT_MODE=false
88
VITE_SESSION_SECRET=hello-world-123
9+
VITE_AUTH0_OFFLINE_ACCESS=false

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ Set `VITE_AUTH0_MULTI_TENANT_MODE=true` in `.env`
4949

5050
## Usage
5151

52+
### Environment Variables
53+
54+
See [.env.example](./env.example)
55+
56+
- `VITE_AUTH0_MULTI_TENANT_MODE` requires the Auth0 Organization setup
57+
- `VITE_AUTH0_OFFLINE_ACCESS` requires the Auth0 API to be configured with
58+
offline access
59+
5260
### vite.config.ts/js
5361

5462
There's an issue with vite throwing `process` is undefined errors when the

src/api/callback.js

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { storage } from '../session.js'
2+
import { parseCookie } from 'solid-start/session'
23

34
async function auth0UserInfo(accessToken) {
45
const endpoint = new URL(`https://${process.env.VITE_AUTH0_DOMAIN}/userinfo`)
@@ -17,18 +18,38 @@ async function auth0UserInfo(accessToken) {
1718
return userInfo.json()
1819
}
1920

20-
async function auth0FetchOAuthToken(code, redirectUrl) {
21+
async function auth0FetchOAuthToken(code, state, redirectUrl, organization) {
2122
const endpoint = new URL(
2223
`https://${process.env.VITE_AUTH0_DOMAIN}/oauth/token`
2324
)
2425

2526
const formData = new URLSearchParams()
27+
const scopes = ['openid', 'profile', 'email']
2628
formData.append('grant_type', 'authorization_code')
2729
formData.append('client_id', process.env.VITE_AUTH0_CLIENT_ID)
2830
formData.append('client_secret', process.env.AUTH0_CLIENT_SECRET)
2931
formData.append('code', code)
32+
formData.append('state', state)
3033
formData.append('redirect_uri', redirectUrl)
3134

35+
if (process.env.VITE_AUTH0_OFFLINE_ACCESS === 'true') {
36+
scopes.push('offline_access')
37+
}
38+
formData.append('scope', scopes.join(' '))
39+
40+
if (organization) {
41+
formData.append('organization', organization)
42+
}
43+
44+
if (process.env.VITE_AUTH0_AUDIENCE) {
45+
formData.append('audience', process.env.VITE_AUTH0_AUDIENCE)
46+
}
47+
48+
if (process.env.DEBUG) {
49+
console.log('formData')
50+
console.log(formData)
51+
}
52+
3253
const authToken = await fetch(endpoint, {
3354
method: 'POST',
3455
body: formData
@@ -39,21 +60,48 @@ async function auth0FetchOAuthToken(code, redirectUrl) {
3960

4061
export default async function get(request) {
4162
let baseUrl = process.env.VITE_BASE_URL
63+
if (process.env.DEBUG) {
64+
console.log('baseUrl', baseUrl)
65+
}
66+
67+
const url = new URL(request.url)
4268
const session = await storage.getSession()
4369
const headers = new Headers()
44-
const url = new URL(request.url)
45-
const params = url.searchParams
70+
const code = url.searchParams.get('code')
71+
const state = url.searchParams.get('state')
72+
const cookies = parseCookie(request.headers.get('Cookie'))
73+
const verification = JSON.parse(cookies[`com.auth0.auth.${state}`])
74+
75+
if (!verification) {
76+
console.error('No verification cookie found')
77+
return new Response(JSON.stringify({ error: 'verification failed' }), {
78+
status: 500
79+
})
80+
}
4681

47-
// TODO: security: check response state & cookie state
48-
// const requestHeaders = new Headers(request.headers)
49-
// const requestCookies = requestHeaders.get('cookie')
82+
if (process.env.DEBUG) {
83+
console.log('state verification')
84+
console.log(state)
85+
console.log(verification)
86+
}
5087

51-
if (params.get('code') === undefined || params.get('code') === null) {
88+
if (code === undefined || code === null) {
89+
console.error('No code found')
5290
return new Response(JSON.stringify({ error: 'missing code' }), {
5391
status: 500
5492
})
5593
}
5694

95+
if (state !== verification.state) {
96+
console.error('Code and state do not match verification')
97+
return new Response(
98+
JSON.stringify({ error: 'code and state verification failed' }),
99+
{
100+
status: 403
101+
}
102+
)
103+
}
104+
57105
let redirectUrl = process.env.VITE_AUTH0_REDIRECT_URI
58106
if (process.env.VITE_AUTH0_MULTI_TENANT_MODE === 'true') {
59107
const orgName = url.hostname.split('.')[0]
@@ -65,17 +113,19 @@ export default async function get(request) {
65113
}
66114

67115
const jsonAuthToken = await auth0FetchOAuthToken(
68-
params.get('code'),
69-
redirectUrl
116+
code,
117+
state,
118+
redirectUrl,
119+
verification.organization
70120
)
71121

72-
if (process.env.DEBUG === 'true') {
122+
if (process.env.DEBUG) {
73123
console.log('auth0FetchOAuthToken')
74124
console.log(jsonAuthToken)
75125
}
76126

77127
const userInfo = await auth0UserInfo(jsonAuthToken.access_token)
78-
if (process.env.DEBUG === 'true') {
128+
if (process.env.DEBUG) {
79129
console.log('auth0UserInfo')
80130
console.log(userInfo)
81131
}

0 commit comments

Comments
 (0)