@@ -9,14 +9,14 @@ import {
99 CredentialRequest ,
1010 getCredentialConfigurationsMatchingRequestFormat ,
1111} from '@openid4vc/openid4vci'
12-
1312import createHttpError from 'http-errors'
1413import { getCredentialConfigurationsSupportedForScopes } from '../../shared'
1514import { CredoRouter , getRequestContext } from '../../shared/router'
1615import { addSecondsToDate } from '../../shared/utils'
1716import { OpenId4VcIssuanceSessionState } from '../OpenId4VcIssuanceSessionState'
1817import { OpenId4VcIssuerService } from '../OpenId4VcIssuerService'
1918import { OpenId4VcIssuanceSessionRecord , OpenId4VcIssuanceSessionRepository } from '../repository'
19+ import { oauth2Error , oauth2UnauthorizedError } from '../util/errors'
2020
2121export function configureCredentialEndpoint ( router : CredoRouter , config : BaseOpenId4VcIssuerModuleConfig ) {
2222 router . post ( config . credentialEndpointPath , async ( request : OpenId4VcIssuancePostRequest < CredentialRequest > ) => {
@@ -41,21 +41,12 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
4141 } ,
4242 } )
4343 . catch ( ( error : unknown ) => {
44- throw createHttpError (
45- 403 ,
46- error instanceof Oauth2ResourceUnauthorizedError ? error . message : 'Unknown error occured' ,
47- {
48- headers : {
49- 'WWW-Authenticate' :
50- error instanceof Oauth2ResourceUnauthorizedError
51- ? error . toHeaderValue ( )
52- : new Oauth2ResourceUnauthorizedError (
53- 'No credential configurations match credential request and access token scope' ,
54- [ { scheme : SupportedAuthenticationScheme . DPoP } , { scheme : SupportedAuthenticationScheme . Bearer } ]
55- ) . toHeaderValue ( ) ,
56- } ,
57- }
58- )
44+ throw error instanceof Oauth2ResourceUnauthorizedError
45+ ? oauth2UnauthorizedError ( error . message , error . wwwAuthenticateHeaders )
46+ : oauth2UnauthorizedError ( 'Unknown error occured' , [
47+ { scheme : SupportedAuthenticationScheme . DPoP } ,
48+ { scheme : SupportedAuthenticationScheme . Bearer } ,
49+ ] )
5950 } )
6051 if ( ! resourceRequestResult ) return
6152 const { tokenPayload, accessToken, scheme, authorizationServer } = resourceRequestResult
@@ -75,34 +66,25 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
7566
7667 const subject = tokenPayload . sub
7768 if ( ! subject ) {
78- throw createHttpError (
79- 400 ,
80- `Received token without 'sub' claim. Subject is required for binding issuance session` ,
81- {
82- type : 'oauth2_error' ,
83- errorResponse : { error : Oauth2ErrorCodes . ServerError } ,
84- }
69+ throw oauth2Error (
70+ Oauth2ErrorCodes . ServerError ,
71+ `Received token without 'sub' claim. Subject is required for binding issuance session`
8572 )
8673 }
8774
8875 // Already handle request without format. Simplifies next code sections
8976 if ( ! parsedCredentialRequest . format ) {
90- throw createHttpError (
91- 400 ,
92- parsedCredentialRequest . credentialIdentifier
93- ? `Credential request containing 'credential_identifier' not supported`
94- : parsedCredentialRequest . credentialConfigurationId
95- ? `Credential configuration '${ parsedCredentialRequest . credentialConfigurationId } ' not supported`
96- : `Credential format '${ parsedCredentialRequest . credentialRequest . format } ' not supported` ,
97- {
98- type : 'oauth2_error' ,
99- errorResponse : {
100- error : parsedCredentialRequest . credentialIdentifier
101- ? Oauth2ErrorCodes . InvalidCredentialRequest
102- : Oauth2ErrorCodes . UnsupportedCredentialFormat ,
103- } ,
104- }
105- )
77+ throw parsedCredentialRequest . credentialIdentifier
78+ ? oauth2Error (
79+ Oauth2ErrorCodes . InvalidCredentialRequest ,
80+ `Credential request containing 'credential_identifier' not supported`
81+ )
82+ : oauth2Error (
83+ Oauth2ErrorCodes . UnsupportedCredentialFormat ,
84+ parsedCredentialRequest . credentialConfigurationId
85+ ? `Credential configuration '${ parsedCredentialRequest . credentialConfigurationId } ' not supported`
86+ : `Credential format '${ parsedCredentialRequest . credentialRequest . format } ' not supported`
87+ )
10688 }
10789
10890 if ( preAuthorizedCode || issuerState ) {
@@ -124,34 +106,28 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
124106 }
125107 )
126108
127- throw createHttpError (
128- 400 ,
129- `No issuance session found for incoming credential request for issuer ${ issuer . issuerId } and access token data` ,
130- {
131- type : 'oauth2_error' ,
132- errorResponse : { error : Oauth2ErrorCodes . CredentialRequestDenied } ,
133- }
109+ throw oauth2Error (
110+ Oauth2ErrorCodes . CredentialRequestDenied ,
111+ `No issuance session found for incoming credential request for issuer ${ issuer . issuerId } and access token data`
134112 )
135113 }
136114
137115 // Use issuance session dpop config
138116 if ( issuanceSession . dpop ?. required && ! resourceRequestResult . dpop ) {
139- return createHttpError ( 401 , 'Missing required DPoP proof' , agentContext . config . logger , {
140- tyoe : 'oauth2_error' ,
141- errorResponse : { error : Oauth2ErrorCodes . InvalidDpopProof } ,
142- } )
117+ return oauth2UnauthorizedError ( 'Missing required DPoP proof' , [
118+ {
119+ scheme,
120+ error : Oauth2ErrorCodes . InvalidDpopProof ,
121+ } ,
122+ ] )
143123 }
144124
145125 // Verify the issuance session subject
146126 if ( issuanceSession . authorization ?. subject ) {
147127 if ( issuanceSession . authorization . subject !== tokenPayload . sub ) {
148- throw createHttpError (
149- 400 ,
150- `Issuance session authorization subject does not match with the token payload subject for issuance session '${ issuanceSession . id } '. Returning error response` ,
151- {
152- type : 'oauth2_error' ,
153- errorResponse : { error : Oauth2ErrorCodes . CredentialRequestDenied } ,
154- }
128+ throw oauth2Error (
129+ Oauth2ErrorCodes . CredentialRequestDenied ,
130+ `Issuance session authorization subject does not match with the token payload subject for issuance session '${ issuanceSession . id } '. Returning error response`
155131 )
156132 }
157133 }
@@ -163,10 +139,7 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
163139 ) {
164140 issuanceSession . errorMessage = 'Credential offer has expired'
165141 await openId4VcIssuerService . updateState ( agentContext , issuanceSession , OpenId4VcIssuanceSessionState . Error )
166- throw createHttpError ( 400 , 'Session expired' , {
167- type : 'oauth2_error' ,
168- errorResponse : { error : Oauth2ErrorCodes . CredentialRequestDenied } ,
169- } )
142+ throw oauth2Error ( Oauth2ErrorCodes . CredentialRequestDenied , 'Session expired' )
170143 } else {
171144 issuanceSession . authorization = {
172145 ...issuanceSession . authorization ,
@@ -186,10 +159,12 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
186159
187160 // Use global config when creating a dynamic session
188161 if ( config . dpopRequired && ! resourceRequestResult . dpop ) {
189- return createHttpError ( 401 , 'Missing required DPoP proof' , agentContext . config . logger , {
190- tyoe : 'oauth2_error' ,
191- errorResponse : { error : Oauth2ErrorCodes . InvalidDpopProof } ,
192- } )
162+ throw oauth2UnauthorizedError ( 'Missing required DPoP proof' , [
163+ {
164+ scheme : scheme ,
165+ error : Oauth2ErrorCodes . InvalidDpopProof ,
166+ } ,
167+ ] )
193168 }
194169
195170 const configurationsForScope = getCredentialConfigurationsSupportedForScopes (
@@ -215,17 +190,12 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
215190 }
216191
217192 if ( Object . keys ( configurationsForToken ) . length === 0 ) {
218- throw createHttpError ( 403 , 'No credential configurations match credential request and access token scope' , {
219- headers : {
220- 'WWW-Authenticate' : new Oauth2ResourceUnauthorizedError (
221- 'No credential configurationss match credential request and access token scope' ,
222- {
223- scheme,
224- error : Oauth2ErrorCodes . InsufficientScope ,
225- }
226- ) . toHeaderValue ( ) ,
193+ throw oauth2UnauthorizedError ( 'No credential configurations match credential request and access token scope' , [
194+ {
195+ scheme,
196+ error : Oauth2ErrorCodes . InsufficientScope ,
227197 } ,
228- } )
198+ ] )
229199 }
230200
231201 issuanceSession = new OpenId4VcIssuanceSessionRecord ( {
@@ -251,13 +221,9 @@ export function configureCredentialEndpoint(router: CredoRouter, config: BaseOpe
251221 await issuanceSessionRepository . save ( agentContext , issuanceSession )
252222 openId4VcIssuerService . emitStateChangedEvent ( agentContext , issuanceSession , null )
253223 } else if ( ! issuanceSession ) {
254- throw createHttpError (
255- 400 ,
256- `Access token without 'issuer_state' or 'pre-authorized_code' issued by external authorization server provided, but 'allowDynamicIssuanceSessions' is disabled. Either bind the access token to a stateful credential offer, or enable 'allowDynamicIssuanceSessions'.` ,
257- {
258- type : 'oauth2_error' ,
259- errorResponse : { error : Oauth2ErrorCodes . CredentialRequestDenied } ,
260- }
224+ throw oauth2Error (
225+ Oauth2ErrorCodes . CredentialRequestDenied ,
226+ `Access token without 'issuer_state' or 'pre-authorized_code' issued by external authorization server provided, but 'allowDynamicIssuanceSessions' is disabled. Either bind the access token to a stateful credential offer, or enable 'allowDynamicIssuanceSessions'.`
261227 )
262228 }
263229
0 commit comments