1212use SimpleSAML \Module \oidc \Repositories \UserRepository ;
1313use SimpleSAML \Module \oidc \Server \Exceptions \OidcServerException ;
1414use SimpleSAML \Module \oidc \Services \LoggerService ;
15+ use SimpleSAML \Module \oidc \Utils \DidKeyResolver ;
1516use SimpleSAML \Module \oidc \Utils \RequestParamsResolver ;
1617use SimpleSAML \Module \oidc \Utils \Routes ;
1718use SimpleSAML \OpenID \Algorithms \SignatureAlgorithmEnum ;
@@ -40,6 +41,7 @@ public function __construct(
4041 protected readonly LoggerService $ loggerService ,
4142 protected readonly RequestParamsResolver $ requestParamsResolver ,
4243 protected readonly UserRepository $ userRepository ,
44+ protected readonly DidKeyResolver $ didKeyResolver ,
4345 ) {
4446 if (!$ this ->moduleConfig ->getVerifiableCredentialEnabled ()) {
4547 throw OidcServerException::forbidden ('Verifiable Credential capabilities not enabled ' );
@@ -75,23 +77,65 @@ public function credential(Request $request): Response
7577 );
7678 }
7779
78- // TODO mivanci validate credential request, including proof. Sample:
80+ // Validate credential request, including proof
81+ if (isset ($ requestData ['proof ' ]) && isset ($ requestData ['proof ' ]['proof_type ' ]) &&
82+ $ requestData ['proof ' ]['proof_type ' ] === 'jwt ' && isset ($ requestData ['proof ' ]['jwt ' ])) {
83+
84+ $ proofJwt = $ requestData ['proof ' ]['jwt ' ];
85+ $ this ->loggerService ->debug ('Verifying proof JWT: ' . $ proofJwt );
86+
87+ try {
88+ // Parse the JWT to extract header and payload
89+ $ jwtParts = explode ('. ' , $ proofJwt );
90+ if (count ($ jwtParts ) !== 3 ) {
91+ throw OidcServerException::invalidRequest ('Invalid JWT format in proof ' );
92+ }
93+
94+ $ header = json_decode (Base64Url::decode ($ jwtParts [0 ]), true );
95+ $ payload = json_decode (Base64Url::decode ($ jwtParts [1 ]), true );
96+
97+ if (!isset ($ payload ['iss ' ])) {
98+ throw OidcServerException::invalidRequest ('Missing issuer (iss) in proof JWT ' );
99+ }
100+
101+ $ issuer = $ payload ['iss ' ];
102+ $ this ->loggerService ->debug ('Proof JWT issuer: ' . $ issuer );
103+
104+ // Check if the issuer is a did:key
105+ if (str_starts_with ($ issuer , 'did:key: ' )) {
106+ $ this ->loggerService ->debug ('Extracting JWK from did:key: ' . $ issuer );
107+
108+ // Extract JWK from did:key
109+ $ jwk = $ this ->didKeyResolver ->extractJwkFromDidKey ($ issuer );
110+
111+ // If kid is present in the header, add it to the JWK
112+ if (isset ($ header ['kid ' ])) {
113+ $ jwk ['kid ' ] = $ header ['kid ' ];
114+ } else {
115+ // If no kid in header, use the did:key as kid
116+ $ jwk ['kid ' ] = $ issuer ;
117+ }
118+
119+ $ this ->loggerService ->debug ('Extracted JWK: ' , $ jwk );
120+
121+ // TODO: Verify the JWT signature using the extracted JWK
122+ // This would typically involve using a JWT library to verify the signature
123+ // For now, we'll just log that we've extracted the JWK successfully
124+ $ this ->loggerService ->debug ('JWK extracted successfully from did:key ' );
125+ }
126+ } catch (\Exception $ e ) {
127+ $ this ->loggerService ->error ('Error processing proof JWT: ' . $ e ->getMessage ());
128+ throw OidcServerException::invalidRequest ('Error processing proof JWT: ' . $ e ->getMessage ());
129+ }
130+ }
131+
79132 /**
80- * 'credential_definition' =>
81- * array (
82- * 'type' =>
83- * array (
84- * 0 => 'VerifiableCredential',
85- * 1 => 'ResearchAndScholarshipCredentialJwtVcJson',
86- * ),
87- * ),
88- * 'format' => 'jwt_vc_json',
133+ * Sample proof structure:
89134 * 'proof' =>
90135 * array (
91136 * 'proof_type' => 'jwt',
92137 * 'jwt' => 'eyJ0eXAiOiJvcGVuaWQ0dmNpLXByb29mK2p3dCIsImFsZyI6IkVTMjU2Iiwia2lkIjoiZGlkOmtleTp6MmRtekQ4MWNnUHg4VmtpN0pidXVNbUZZcldQZ1lveXR5a1VaM2V5cWh0MWo5S2JyU2ZYMkJVeHNVaW5QbVA3QUVzZEN4OWpQYlV0ZkIzWXN2MTd4TGpyZkMxeDNVZmlMTWtyeWdTZDJMeWltQ3RGejhHWlBqOFFrMUJFU0F6M21LWGRCTEpuUHNNQ0R4Nm9QNjNuZVpmR1NKelF5SjRLVlN6Nmt4UTJQOTE4NGdXS1FnI3oyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnJTZlgyQlV4c1VpblBtUDdBRXNkQ3g5alBiVXRmQjNZc3YxN3hManJmQzF4M1VmaUxNa3J5Z1NkMkx5aW1DdEZ6OEdaUGo4UWsxQkVTQXozbUtYZEJMSm5Qc01DRHg2b1A2M25lWmZHU0p6UXlKNEtWU3o2a3hRMlA5MTg0Z1dLUWcifQ.eyJhdWQiOiJodHRwczovL2lkcC5taXZhbmNpLmluY3ViYXRvci5oZXhhYS5ldSIsImlhdCI6MTc0ODUxNDE0NywiZXhwIjoxNzQ4NTE0ODA3LCJpc3MiOiJkaWQ6a2V5OnoyZG16RDgxY2dQeDhWa2k3SmJ1dU1tRllyV1BnWW95dHlrVVozZXlxaHQxajlLYnJTZlgyQlV4c1VpblBtUDdBRXNkQ3g5alBiVXRmQjNZc3YxN3hManJmQzF4M1VmaUxNa3J5Z1NkMkx5aW1DdEZ6OEdaUGo4UWsxQkVTQXozbUtYZEJMSm5Qc01DRHg2b1A2M25lWmZHU0p6UXlKNEtWU3o2a3hRMlA5MTg0Z1dLUWciLCJqdGkiOiJiMmNlZDQ2Yi0zOWNiLTRkZDAtYmQxZS1hNzY5ZWNlOWUxMTIifQ.SPdMSnrfF8ybhfYluzz5OrfWJQDOpCu7-of8zVbp5UR89GaB7j14Egext1h9pYgl6JwIP8zibUjTSc8JLVYuvA',
93138 * ),
94- * )
95139 */
96140
97141 // TODO mivanci Check / handle credential_identifier parameter.
@@ -113,7 +157,10 @@ public function credential(Request $request): Response
113157 }
114158
115159 if (is_null ($ credentialConfigurationId )) {
116- return $ this ->routes ->newJsonErrorResponse ('invalid_credential_request ' , 'Can not resolve credential configuration ID. ' );
160+ return $ this ->routes ->newJsonErrorResponse (
161+ 'invalid_credential_request ' ,
162+ 'Can not resolve credential configuration ID. ' ,
163+ );
117164 }
118165
119166 if (!in_array ($ credentialConfigurationId , $ this ->moduleConfig ->getCredentialConfigurationIdsSupported ())) {
@@ -135,7 +182,6 @@ public function credential(Request $request): Response
135182 // as per the credential configuration supported configuration.
136183 $ validClaimPaths = $ this ->moduleConfig ->getValidCredentialClaimPathsFor ($ credentialConfigurationId );
137184
138-
139185 // Map user attributes to credential claims
140186 $ credentialSubject = [];
141187 $ attributeToCredentialClaimPathMap = $ this ->moduleConfig ->getUserAttributeToCredentialClaimPathMapFor (
@@ -144,6 +190,13 @@ public function credential(Request $request): Response
144190 foreach ($ attributeToCredentialClaimPathMap as $ mapEntry ) {
145191 $ userAttributeName = key ($ mapEntry );
146192 $ credentialClaimPath = current ($ mapEntry );
193+ if (!in_array ($ credentialClaimPath , $ validClaimPaths )) {
194+ $ this ->loggerService ->warning (
195+ 'Attribute "%s" does not use one of valid credential claim paths. ' ,
196+ $ mapEntry ,
197+ );
198+ continue ;
199+ }
147200 if (isset ($ userAttributes [$ userAttributeName ])) {
148201 $ this ->setCredentialClaimValue (
149202 $ credentialSubject ,
0 commit comments