1- import type { H3Event } from 'h3'
1+ import type { H3Event , H3Error } from 'h3'
22import { eventHandler , createError , getQuery , sendRedirect } from 'h3'
33import { withQuery } from 'ufo'
44import { ofetch } from 'ofetch'
55import { defu } from 'defu'
66import { useRuntimeConfig } from '#imports'
77import type { OAuthConfig } from '#auth-utils'
8- import { createHash , randomBytes } from 'node:crypto '
8+ import { type OAuthChecks , checks } from '../../utils/security '
99
1010export interface OAuthOidcConfig {
1111 /**
@@ -69,10 +69,17 @@ export interface OAuthOidcConfig {
6969 * @example ['openid']
7070 */
7171 scope ?: string [ ]
72+ /**
73+ * checks
74+ * @default []
75+ * @see https://auth0.com/docs/flows/authorization-code-flow-with-proof-key-for-code-exchange-pkce
76+ * @see https://auth0.com/docs/protocols/oauth2/oauth-state
77+ */
78+ checks ?: OAuthChecks [ ]
7279}
7380
7481function validateConfig ( config : any ) {
75- const requiredConfigKeys = [ 'clientId' , 'clientSecret' , 'authorizationUrl' , 'tokenUrl' , 'userinfoUrl' , 'redirectUri' ]
82+ const requiredConfigKeys = [ 'clientId' , 'clientSecret' , 'authorizationUrl' , 'tokenUrl' , 'userinfoUrl' , 'redirectUri' , 'responseType' ]
7683 const missingConfigKeys : string [ ] = [ ]
7784 requiredConfigKeys . forEach ( key => {
7885 if ( ! config [ key ] ) {
@@ -82,7 +89,7 @@ function validateConfig(config: any) {
8289 if ( missingConfigKeys . length ) {
8390 const error = createError ( {
8491 statusCode : 500 ,
85- message : `Missing config keys:${ missingConfigKeys . join ( ', ' ) } `
92+ message : `Missing config keys: ${ missingConfigKeys . join ( ', ' ) } `
8693 } )
8794
8895 return {
@@ -93,21 +100,11 @@ function validateConfig(config: any) {
93100 return { valid : true }
94101}
95102
96- function createCodeChallenge ( verifier : string ) {
97- return createHash ( 'sha256' )
98- . update ( verifier )
99- . digest ( 'base64' )
100- . replace ( / \+ / g, '-' )
101- . replace ( / \/ / g, '_' )
102- . replace ( / = + $ / , '' )
103- }
104-
105103export function oidcEventHandler ( { config, onSuccess, onError } : OAuthConfig < OAuthOidcConfig > ) {
106104 return eventHandler ( async ( event : H3Event ) => {
107- const storage = useStorage ( 'redis' )
108105 // @ts -ignore
109106 config = defu ( config , useRuntimeConfig ( event ) . oauth ?. oidc ) as OAuthOidcConfig
110- const { code, state } = getQuery ( event )
107+ const { code } = getQuery ( event )
111108
112109 const validationResult = validateConfig ( config )
113110
@@ -116,12 +113,8 @@ export function oidcEventHandler({ config, onSuccess, onError }: OAuthConfig<OAu
116113 return onError ( event , validationResult . error )
117114 }
118115
119- if ( ! code && ! state ) {
120- const state = randomBytes ( 10 ) . toString ( 'hex' )
121- const codeVerifier = randomBytes ( 52 ) . toString ( 'hex' )
122- const challenge = createCodeChallenge ( codeVerifier )
123- await storage . setItem ( 'oidc:verifier:' + state , codeVerifier )
124- await storage . setItem ( 'oidc:challenge:' + state , challenge )
116+ if ( ! code ) {
117+ const authParams = await checks . create ( event , config . checks ) // Initialize checks
125118 // Redirect to OIDC login page
126119 return sendRedirect (
127120 event ,
@@ -133,14 +126,19 @@ export function oidcEventHandler({ config, onSuccess, onError }: OAuthConfig<OAu
133126 claims : config ?. claims || { } ,
134127 grant_type : config . grantType || 'authorization_code' ,
135128 audience : config . audience || null ,
136- state : state ,
137- code_challenge : config . codeChallengeMethod ? challenge : null ,
138- code_challenge_method : config . codeChallengeMethod ,
129+ ...authParams
139130 } )
140131 )
141132 }
142133
143- const codeVerifier : string = await storage . getItem ( 'oidc:verifier:' + state ) || ''
134+ // Verify checks
135+ let checkResult
136+ try {
137+ checkResult = await checks . use ( event , config . checks )
138+ } catch ( error ) {
139+ if ( ! onError ) throw error
140+ return onError ( event , error as H3Error )
141+ }
144142
145143 // @ts -ignore
146144 const queryString = new URLSearchParams ( {
@@ -150,9 +148,10 @@ export function oidcEventHandler({ config, onSuccess, onError }: OAuthConfig<OAu
150148 redirect_uri : config . redirectUri ,
151149 response_type : config . responseType ,
152150 grant_type : config . grantType || 'authorization_code' ,
153- code_verifier : codeVerifier ,
151+ ... checkResult
154152 } )
155153
154+ // Request tokens.
156155 const tokens : any = await ofetch (
157156 config . tokenUrl as string ,
158157 {
@@ -178,6 +177,8 @@ export function oidcEventHandler({ config, onSuccess, onError }: OAuthConfig<OAu
178177 const tokenType = tokens . token_type
179178 const accessToken = tokens . access_token
180179 const userInfoUrl = config . userinfoUrl || ''
180+
181+ // Request userinfo.
181182 const user : any = await ofetch ( userInfoUrl , {
182183 headers : {
183184 Authorization : `${ tokenType } ${ accessToken } `
0 commit comments