@@ -3,24 +3,64 @@ import {
33 AuthorizationRequestOpts ,
44 CodeChallengeMethod ,
55 convertJsonToURI ,
6+ CreateRequestObjectMode ,
67 CredentialConfigurationSupportedV1_0_13 ,
78 CredentialOfferPayloadV1_0_13 ,
89 CredentialOfferRequestWithBaseUrl ,
910 determineSpecVersionFromOffer ,
1011 EndpointMetadataResultV1_0_13 ,
1112 formPost ,
1213 JsonURIMode ,
14+ Jwt ,
1315 OID4VCICredentialFormat ,
1416 OpenId4VCIVersion ,
1517 PARMode ,
1618 PKCEOpts ,
1719 PushedAuthorizationResponse ,
20+ RequestObjectOpts ,
1821 ResponseType ,
1922} from '@sphereon/oid4vci-common' ;
2023import Debug from 'debug' ;
2124
25+ import { ProofOfPossessionBuilder } from './ProofOfPossessionBuilder' ;
26+
2227const debug = Debug ( 'sphereon:oid4vci' ) ;
2328
29+ export async function createSignedAuthRequestWhenNeeded ( requestObject : Record < string , any > , opts : RequestObjectOpts & { aud ?: string } ) {
30+ if ( opts . requestObjectMode === CreateRequestObjectMode . REQUEST_URI ) {
31+ throw Error ( `Request Object Mode ${ opts . requestObjectMode } is not supported yet` ) ;
32+ } else if ( opts . requestObjectMode === CreateRequestObjectMode . REQUEST_OBJECT ) {
33+ if ( typeof opts . signCallbacks ?. signCallback !== 'function' ) {
34+ throw Error ( `No request object sign callback found, whilst request object mode was set to ${ opts . requestObjectMode } ` ) ;
35+ } else if ( ! opts . kid ) {
36+ throw Error ( `No kid found, whilst request object mode was set to ${ opts . requestObjectMode } ` ) ;
37+ }
38+ let client_metadata : any
39+ if ( opts . clientMetadata || opts . jwksUri ) {
40+ client_metadata = opts . clientMetadata ?? { } ;
41+ if ( opts . jwksUri ) {
42+ client_metadata [ 'jwks_uri' ] = opts . jwksUri ;
43+ }
44+ }
45+ let authorization_details = requestObject [ 'authorization_details' ]
46+ if ( typeof authorization_details === 'string' ) {
47+ authorization_details = JSON . parse ( requestObject . authorization_details ) ;
48+ }
49+ if ( ! requestObject . aud && opts . aud ) {
50+ requestObject . aud = opts . aud ;
51+ }
52+ const iss = requestObject . iss ?? opts . iss ?? requestObject . client_id
53+
54+ const jwt : Jwt = { header : { alg : 'ES256' , kid : opts . kid , typ : 'jwt' } , payload : { ...requestObject , iss, authorization_details, ...( client_metadata && { client_metadata} ) } } ;
55+ const pop = await ProofOfPossessionBuilder . fromJwt ( {
56+ jwt,
57+ callbacks : opts . signCallbacks ,
58+ version : OpenId4VCIVersion . VER_1_0_11 ,
59+ mode : 'jwt' ,
60+ } ) . build ( ) ;
61+ requestObject [ 'request' ] = pop . jwt ;
62+ }
63+ }
2464function filterSupportedCredentials (
2565 credentialOffer : CredentialOfferPayloadV1_0_13 ,
2666 credentialsSupported ?: Record < string , CredentialConfigurationSupportedV1_0_13 > ,
@@ -62,15 +102,13 @@ export const createAuthorizationRequestUrl = async ({
62102 }
63103 }
64104
65- const { redirectUri } = authorizationRequest ;
105+ const { redirectUri, requestObjectOpts = { requestObjectMode : CreateRequestObjectMode . NONE } } = authorizationRequest ;
66106 const client_id = clientId ?? authorizationRequest . clientId ;
67- if ( ! client_id ) {
68- throw Error ( `Cannot use PAR without a client_id value set` ) ;
69- }
107+
70108 let { scope, authorizationDetails } = authorizationRequest ;
71109 const parMode = endpointMetadata ?. credentialIssuerMetadata ?. require_pushed_authorization_requests
72110 ? PARMode . REQUIRE
73- : authorizationRequest . parMode ?? PARMode . AUTO ;
111+ : authorizationRequest . parMode ?? ( client_id ? PARMode . AUTO : PARMode . NEVER ) ;
74112 // Scope and authorization_details can be used in the same authorization request
75113 // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param
76114 if ( ! scope && ! authorizationDetails ) {
@@ -138,15 +176,15 @@ export const createAuthorizationRequestUrl = async ({
138176 scope = [ 'openid' , scope ] . filter ( ( s ) => ! ! s ) . join ( ' ' ) ;
139177 }
140178
141- let queryObj : { [ key : string ] : string } | PushedAuthorizationResponse = {
179+ let queryObj : Record < string , any > | PushedAuthorizationResponse = {
142180 response_type : ResponseType . AUTH_CODE ,
143181 ...( ! pkce . disabled && {
144182 code_challenge_method : pkce . codeChallengeMethod ?? CodeChallengeMethod . S256 ,
145183 code_challenge : pkce . codeChallenge ,
146184 } ) ,
147185 authorization_details : JSON . stringify ( handleAuthorizationDetails ( endpointMetadata , authorizationDetails ) ) ,
148186 ...( redirectUri && { redirect_uri : redirectUri } ) ,
149- client_id,
187+ ... ( client_id && { client_id } ) ,
150188 ...( credentialOffer ?. issuerState && { issuer_state : credentialOffer . issuerState } ) ,
151189 scope,
152190 } ;
@@ -170,10 +208,11 @@ export const createAuthorizationRequestUrl = async ({
170208 throw Error ( `PAR error: ${ parResponse . origResponse . statusText } ` ) ;
171209 }
172210 } else {
173- debug ( `PAR response: ${ ( parResponse . successBody , null , 2 ) } ` ) ;
211+ debug ( `PAR response: ${ JSON . stringify ( parResponse . successBody , null , 2 ) } ` ) ;
174212 queryObj = { /*response_type: ResponseType.AUTH_CODE,*/ client_id, request_uri : parResponse . successBody . request_uri } ;
175213 }
176214 }
215+ await createSignedAuthRequestWhenNeeded ( queryObj , { ...requestObjectOpts , aud : endpointMetadata . authorization_server } ) ;
177216
178217 debug ( `Object that will become query params: ` + JSON . stringify ( queryObj , null , 2 ) ) ;
179218 const url = convertJsonToURI ( queryObj , {
0 commit comments