Skip to content

Commit a89915d

Browse files
committed
WIP
1 parent 3d0c16a commit a89915d

File tree

4 files changed

+371
-13
lines changed

4 files changed

+371
-13
lines changed

src/Controllers/VerifiableCredentials/CredentialIssuerCredentialController.php

Lines changed: 66 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use SimpleSAML\Module\oidc\Repositories\UserRepository;
1313
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
1414
use SimpleSAML\Module\oidc\Services\LoggerService;
15+
use SimpleSAML\Module\oidc\Utils\DidKeyResolver;
1516
use SimpleSAML\Module\oidc\Utils\RequestParamsResolver;
1617
use SimpleSAML\Module\oidc\Utils\Routes;
1718
use 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,

src/Services/Container.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@
103103
use SimpleSAML\Module\oidc\Stores\Session\LogoutTicketStoreDb;
104104
use SimpleSAML\Module\oidc\Utils\ClaimTranslatorExtractor;
105105
use SimpleSAML\Module\oidc\Utils\ClassInstanceBuilder;
106+
use SimpleSAML\Module\oidc\Utils\DidKeyResolver;
106107
use SimpleSAML\Module\oidc\Utils\FederationCache;
107108
use SimpleSAML\Module\oidc\Utils\FederationParticipationValidator;
108109
use SimpleSAML\Module\oidc\Utils\JwksResolver;
@@ -230,6 +231,9 @@ public function __construct()
230231
$requestParamsResolver = new RequestParamsResolver($helpers, $core, $federation);
231232
$this->services[RequestParamsResolver::class] = $requestParamsResolver;
232233

234+
$didKeyResolver = new DidKeyResolver();
235+
$this->services[DidKeyResolver::class] = $didKeyResolver;
236+
233237
$clientEntityFactory = new ClientEntityFactory(
234238
$sspBridge,
235239
$helpers,

0 commit comments

Comments
 (0)