@@ -2,7 +2,8 @@ import type { H3Event } from 'h3'
2
2
import { eventHandler , getQuery , sendRedirect } from 'h3'
3
3
import { withQuery } from 'ufo'
4
4
import { defu } from 'defu'
5
- import { handleMissingConfiguration , handleAccessTokenErrorResponse , getOAuthRedirectURL , requestAccessToken } from '../utils'
5
+ import type { RequestAccessTokenOptions } from '../utils'
6
+ import { handleMissingConfiguration , handleAccessTokenErrorResponse , getOAuthRedirectURL , requestAccessToken , handleState , handlePkceVerifier , handleInvalidState } from '../utils'
6
7
import { useRuntimeConfig , createError } from '#imports'
7
8
import type { OAuthConfig } from '#auth-utils'
8
9
@@ -48,7 +49,7 @@ export function defineOAuthZitadelEventHandler({ config, onSuccess, onError }: O
48
49
authorizationParams : { } ,
49
50
} ) as OAuthZitadelConfig
50
51
51
- const query = getQuery < { code ?: string , error ?: string } > ( event )
52
+ const query = getQuery < { code ?: string , state ?: string , error ?: string } > ( event )
52
53
53
54
if ( query . error ) {
54
55
const error = createError ( {
@@ -60,14 +61,18 @@ export function defineOAuthZitadelEventHandler({ config, onSuccess, onError }: O
60
61
return onError ( event , error )
61
62
}
62
63
63
- if ( ! config . clientId || ! config . clientSecret || ! config . domain ) {
64
- return handleMissingConfiguration ( event , 'zitadel' , [ 'clientId' , 'clientSecret' , 'issuerUrl '] , onError )
64
+ if ( ! config . clientId || ! config . domain ) {
65
+ return handleMissingConfiguration ( event , 'zitadel' , [ 'clientId' , 'domain ' ] , onError )
65
66
}
66
67
67
68
const authorizationURL = `https://${ config . domain } /oauth/v2/authorize`
68
69
const tokenURL = `https://${ config . domain } /oauth/v2/token`
69
70
const redirectURL = config . redirectURL || getOAuthRedirectURL ( event )
70
71
72
+ // Create pkce verifier
73
+ const verifier = await handlePkceVerifier ( event )
74
+ const state = await handleState ( event )
75
+
71
76
if ( ! query . code ) {
72
77
config . scope = config . scope || [ 'openid' ]
73
78
// Redirect to Zitadel OAuth page
@@ -79,23 +84,37 @@ export function defineOAuthZitadelEventHandler({ config, onSuccess, onError }: O
79
84
client_id : config . clientId ,
80
85
redirect_uri : redirectURL ,
81
86
scope : config . scope . join ( ' ' ) ,
87
+ state,
88
+ code_challenge : verifier . code_challenge ,
89
+ code_challenge_method : verifier . code_challenge_method ,
82
90
...config . authorizationParams ,
83
91
} ) ,
84
92
)
85
93
}
86
94
87
- const tokens = await requestAccessToken ( tokenURL , {
88
- headers : {
89
- 'Authorization' : `Basic ${ Buffer . from ( ` ${ config . clientId } : ${ config . clientSecret } ` ) . toString ( 'base64' ) } ` ,
90
- 'Content-Type' : 'application/x-www-form-urlencoded' ,
91
- } ,
95
+ if ( query . state !== state ) {
96
+ return handleInvalidState ( event , 'zitadel' , onError )
97
+ }
98
+
99
+ const request : RequestAccessTokenOptions = {
92
100
body : {
93
101
grant_type : 'authorization_code' ,
94
102
client_id : config . clientId ,
95
103
redirect_uri : redirectURL ,
96
104
code : query . code ,
105
+ code_verifier : verifier . code_verifier ,
97
106
} ,
98
- } )
107
+ }
108
+
109
+ if ( config . clientSecret ) {
110
+ const basicAuthorization = Buffer . from ( `${ config . clientId } :${ config . clientSecret } ` ) . toString ( 'base64' )
111
+ request . headers = {
112
+ 'Authorization' : `Basic ${ basicAuthorization } ` ,
113
+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
114
+ }
115
+ }
116
+
117
+ const tokens = await requestAccessToken ( tokenURL , request )
99
118
100
119
if ( tokens . error ) {
101
120
return handleAccessTokenErrorResponse ( event , 'zitadel' , tokens , onError )
0 commit comments