2525use SimpleSAML \OpenID \Exceptions \OpenId4VciProofException ;
2626use SimpleSAML \OpenID \Jwk ;
2727use SimpleSAML \OpenID \VerifiableCredentials ;
28+ use SimpleSAML \OpenID \VerifiableCredentials \OpenId4VciProof ;
2829use Symfony \Component \HttpFoundation \Request ;
2930use Symfony \Component \HttpFoundation \Response ;
3031
3132class CredentialIssuerCredentialController
3233{
33-
3434 public const SD_JWT_FORMAT_IDS = [
3535 CredentialFormatIdentifiersEnum::DcSdJwt->value ,
3636 CredentialFormatIdentifiersEnum::VcSdJwt->value ,
@@ -88,22 +88,48 @@ public function credential(Request $request): Response
8888
8989 // TODO mivanci Validate credential request
9090
91+ $ credentialFormatId = $ requestData [ClaimsEnum::Format->value ] ?? null ;
92+
93+ if (is_null ($ credentialFormatId )) {
94+ throw OidcServerException::serverError ('Credential format missing in request. ' );
95+ }
96+
97+ if (
98+ !in_array ($ credentialFormatId , [
99+ CredentialFormatIdentifiersEnum::JwtVcJson->value ,
100+ CredentialFormatIdentifiersEnum::DcSdJwt->value ,
101+ CredentialFormatIdentifiersEnum::VcSdJwt->value , // Deprecated value, but let's support it for now.
102+ ])
103+ ) {
104+ return $ this ->routes ->newJsonErrorResponse (
105+ 'unsupported_credential_type ' ,
106+ sprintf ('Credential format ID "%s" is not supported. ' , $ credentialFormatId ),
107+ );
108+ }
109+
91110 // TODO mivanci Check / handle credential_identifier parameter.
92111
93112 $ credentialConfigurationId = $ requestData [ClaimsEnum::CredentialConfigurationId->value ] ?? null ;
94113
95114 if (is_null ($ credentialConfigurationId )) {
96- // Check per draft 14
115+ // TODO mivanci Update this to newest draft.
116+ // Check per draft 14 (Sphereon wallet case).
97117 if (
118+ $ credentialFormatId === CredentialFormatIdentifiersEnum::JwtVcJson->value &&
98119 is_array (
99120 $ credentialDefinitionType =
100- $ requestData [ClaimsEnum::CredentialDefinition->value ][ClaimsEnum::Type->value ],
121+ $ requestData [ClaimsEnum::CredentialDefinition->value ][ClaimsEnum::Type->value ] ?? null ,
101122 )
102123 ) {
103124 $ credentialConfigurationId =
104125 $ this ->moduleConfig ->getCredentialConfigurationIdForCredentialDefinitionType (
105126 $ credentialDefinitionType ,
106127 );
128+ } elseif (
129+ in_array ($ credentialFormatId , self ::SD_JWT_FORMAT_IDS , true ) &&
130+ is_string ($ vct = $ requestData [ClaimsEnum::Vct->value ] ?? null )
131+ ) {
132+ $ credentialConfigurationId = $ vct ;
107133 }
108134 }
109135
@@ -114,38 +140,13 @@ public function credential(Request $request): Response
114140 );
115141 }
116142
117- if (
118- !is_array (
119- $ credentialConfiguration = $ this ->moduleConfig ->getCredentialConfiguration ($ credentialConfigurationId ),
120- )
121- ) {
143+ if (!is_array ($ this ->moduleConfig ->getCredentialConfiguration ($ credentialConfigurationId ))) {
122144 return $ this ->routes ->newJsonErrorResponse (
123145 'unsupported_credential_type ' ,
124146 sprintf ('Credential configuration ID "%s" is not supported. ' , $ credentialConfigurationId ),
125147 );
126148 }
127149
128- $ credentialFormatId = $ credentialConfiguration [ClaimsEnum::Format->value ] ?? null ;
129-
130- if (is_null ($ credentialFormatId )) {
131- throw OidcServerException::serverError (
132- 'Credential format not specified for configuration ID: ' . $ credentialConfigurationId ,
133- );
134- }
135-
136- if (
137- !in_array ($ credentialFormatId , [
138- CredentialFormatIdentifiersEnum::JwtVcJson->value ,
139- CredentialFormatIdentifiersEnum::DcSdJwt->value ,
140- CredentialFormatIdentifiersEnum::VcSdJwt->value , // Deprecated value, but let's support it for now.
141- ])
142- ) {
143- return $ this ->routes ->newJsonErrorResponse (
144- 'unsupported_credential_type ' ,
145- sprintf ('Credential format ID "%s" is not supported. ' , $ credentialFormatId ),
146- );
147- }
148-
149150 $ userId = $ accessToken ->getUserIdentifier ();
150151 $ userEntity = $ this ->userRepository ->getUserEntityByIdentifier ($ userId );
151152 if ($ userEntity === null ) {
@@ -155,6 +156,7 @@ public function credential(Request $request): Response
155156 // Placeholder sub identifier. Will do if proof is not provided.
156157 $ sub = $ this ->moduleConfig ->getIssuer () . '/sub/ ' . $ userId ;
157158
159+ $ proof = null ;
158160 // Validate proof, if provided.
159161 // TODO mivanci consider making proof mandatory (in issuer metadata).
160162 if (
@@ -284,12 +286,11 @@ public function credential(Request $request): Response
284286 saltBlacklist: $ disclosureBag ->salts (),
285287 );
286288
287- $ disclosureBag ->add ($ disclosure );;
289+ $ disclosureBag ->add ($ disclosure );
288290 }
289291 }
290292
291- dd ($ disclosureBag ->all ());
292- // Also make sure that the subject identifier is in credentialSubject claim.
293+ // Make sure that the subject identifier is in credentialSubject claim.
293294 $ this ->setCredentialClaimValue (
294295 $ credentialSubject ,
295296 [ClaimsEnum::Credential_Subject->value , ClaimsEnum::Id->value ],
@@ -336,15 +337,13 @@ public function credential(Request $request): Response
336337 ],
337338 //ClaimsEnum::Issuer->value => $this->moduleConfig->getIssuer(),
338339 ClaimsEnum::Issuer->value => $ issuerDid ,
339- //ClaimsEnum::Issuer->value => 'https://idp.mivanci.incubator.hexaa.eu/ssp/module.php/oidc/jwks',
340340 ClaimsEnum::Issuance_Date->value => $ issuedAt ->format (\DateTimeInterface::RFC3339 ),
341341 ClaimsEnum::Id->value => $ vcId ,
342342 ClaimsEnum::Credential_Subject->value =>
343343 $ credentialSubject [ClaimsEnum::Credential_Subject->value ] ?? [],
344344 ],
345345 //ClaimsEnum::Iss->value => $this->moduleConfig->getIssuer(),
346346 ClaimsEnum::Iss->value => $ issuerDid ,
347- //ClaimsEnum::Iss->value => 'https://idp.mivanci.incubator.hexaa.eu/ssp/module.php/oidc/jwks',
348347 ClaimsEnum::Iat->value => $ issuedAt ->getTimestamp (),
349348 ClaimsEnum::Nbf->value => $ issuedAt ->getTimestamp (),
350349 ClaimsEnum::Sub->value => $ sub ,
@@ -357,27 +356,33 @@ public function credential(Request $request): Response
357356 }
358357
359358 if (in_array ($ credentialFormatId , self ::SD_JWT_FORMAT_IDS , true )) {
360- // TODO selectiveDisclosureBag
359+ $ sdJwtPayload = [
360+ ClaimsEnum::Iss->value => $ issuerDid ,
361+ ClaimsEnum::Iat->value => $ issuedAt ->getTimestamp (),
362+ ClaimsEnum::Nbf->value => $ issuedAt ->getTimestamp (),
363+ ClaimsEnum::Sub->value => $ sub ,
364+ ClaimsEnum::Jti->value => $ vcId ,
365+ ClaimsEnum::Vct->value => $ credentialConfigurationId ,
366+ ];
367+
368+ if ($ proof instanceof OpenId4VciProof) {
369+ $ sdJwtPayload [ClaimsEnum::Cnf->value ] = [
370+ ClaimsEnum::Kid->value => $ proof ->getKeyId (),
371+ ];
372+ }
361373
362374 $ verifiableCredential = $ this ->verifiableCredentials ->sdJwtVcFactory ()->fromData (
363375 $ signingKey ,
364376 $ signatureAlgorithm ,
365- [
366- ClaimsEnum::Iss->value => $ issuerDid ,
367- ClaimsEnum::Iat->value => $ issuedAt ->getTimestamp (),
368- ClaimsEnum::Nbf->value => $ issuedAt ->getTimestamp (),
369- ClaimsEnum::Sub->value => $ sub ,
370- ClaimsEnum::Jti->value => $ vcId ,
371- ],
377+ $ sdJwtPayload ,
372378 [
373379 ClaimsEnum::Kid->value => $ issuerDid . '#0 ' ,
374380 ],
381+ disclosureBag: $ disclosureBag ,
375382 jwtTypesEnum: JwtTypesEnum::VcSdJwt,
376383 );
377384 }
378385
379-
380-
381386 $ this ->loggerService ->debug ('response ' , [
382387 'credentials ' => [
383388 ['credential ' => $ verifiableCredential ->getToken ()],
0 commit comments