Skip to content

Commit 3f82355

Browse files
committed
Do VCI adjustments
1 parent 81870f0 commit 3f82355

File tree

6 files changed

+76
-31
lines changed

6 files changed

+76
-31
lines changed

src/Controllers/JwksController.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ public function __invoke(): JsonResponse
3939
return new JsonResponse(
4040
$this->jwks->jwksDecoratorFactory()->fromJwkDecorators(
4141
...$this->moduleConfig->getProtocolSignatureKeyPairBag()->getAllPublicKeys(),
42+
...$this->moduleConfig->getFederationSignatureKeyPairBag()->getAllPublicKeys(),
43+
...$this->moduleConfig->getVciSignatureKeyPairBag()->getAllPublicKeys(),
4244
)->jsonSerialize(),
4345
);
4446
}

src/Controllers/VerifiableCredentials/CredentialIssuerConfigurationController.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ public function configuration(): Response
4040
{
4141
// https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0.html#name-credential-issuer-metadata-p
4242

43-
// TODO mivanci Add support for multiple signature key pairs. For now, we only support (first) one.
4443
$signatureKeyPair = $this->moduleConfig->getVciSignatureKeyPairBag()->getFirstOrFail();
4544

4645
$credentialConfigurationsSupported = $this->moduleConfig->getVciCredentialConfigurationsSupported();
@@ -53,10 +52,16 @@ public function configuration(): Response
5352
$credentialConfiguration[ClaimsEnum::CredentialSigningAlgValuesSupported->value] = [
5453
$signatureKeyPair->getSignatureAlgorithm()->value,
5554
];
56-
// Earlier drafts
57-
// TODO mivanci Delete CryptographicSuitesSupported once we are on the final draft.
58-
$credentialConfiguration[ClaimsEnum::CryptographicSuitesSupported->value] = [
59-
$signatureKeyPair->getSignatureAlgorithm()->value,
55+
$credentialConfiguration[ClaimsEnum::CryptographicBindingMethodsSupported->value] = [
56+
'jwk',
57+
];
58+
$credentialConfiguration[ClaimsEnum::ProofTypesSupported->value] = [
59+
'jwt' => [
60+
ClaimsEnum::ProofSigningAlgValuesSupported->value => $this->moduleConfig
61+
->getSupportedAlgorithms()
62+
->getSignatureAlgorithmBag()
63+
->getAllNamesUnique(),
64+
],
6065
];
6166
$credentialConfigurationsSupported[$credentialConfigurationId] = $credentialConfiguration;
6267
}
@@ -94,8 +99,11 @@ public function configuration(): Response
9499
[
95100
ClaimsEnum::Name->value => $this->moduleConfig->getOrganizationName(),
96101
ClaimsEnum::Locale->value => 'en-US',
97-
// OPTIONAL
98-
// logo
102+
ClaimsEnum::Description->value => $this->moduleConfig->getDescription() ?? 'SimpleSAMLphp Demo VCI',
103+
ClaimsEnum::LogoUri->value => [
104+
ClaimsEnum::Uri->value => $this->moduleConfig->getLogoUri(),
105+
ClaimsEnum::AltText->value => ($this->moduleConfig->getOrganizationName() ?? 'VCI') . ' logo',
106+
],
99107
],
100108

101109
],

src/Controllers/VerifiableCredentials/CredentialIssuerCredentialController.php

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public function credential(Request $request): Response
133133
['accessTokenState' => $accessToken->getState()],
134134
);
135135
return $this->routes->newJsonErrorResponse(
136-
'invalid_token',
136+
'invalid_credential_request',
137137
'Issuer state missing in access token.',
138138
401,
139139
);
@@ -145,8 +145,9 @@ public function credential(Request $request): Response
145145
['issuerState' => $issuerState],
146146
);
147147
return $this->routes->newJsonErrorResponse(
148-
'invalid_token',
148+
'invalid_credential_request',
149149
'Issuer state not valid.',
150+
401,
150151
);
151152
}
152153

@@ -163,6 +164,7 @@ public function credential(Request $request): Response
163164
return $this->routes->newJsonErrorResponse(
164165
'invalid_credential_request',
165166
'Credential configuration ID must not be used together with credential identifier.',
167+
400,
166168
);
167169
}
168170

@@ -182,6 +184,7 @@ public function credential(Request $request): Response
182184
return $this->routes->newJsonErrorResponse(
183185
'invalid_credential_request',
184186
'Can not resolve credential identifier.',
187+
400,
185188
);
186189
}
187190

@@ -224,6 +227,7 @@ public function credential(Request $request): Response
224227
return $this->routes->newJsonErrorResponse(
225228
'invalid_credential_request',
226229
'Credential identifier not used in flow.',
230+
400,
227231
);
228232
}
229233

@@ -282,6 +286,7 @@ public function credential(Request $request): Response
282286
return $this->routes->newJsonErrorResponse(
283287
'invalid_credential_request',
284288
'Can not resolve credential format.',
289+
400,
285290
);
286291
}
287292

@@ -299,6 +304,7 @@ public function credential(Request $request): Response
299304
return $this->routes->newJsonErrorResponse(
300305
'unsupported_credential_type',
301306
sprintf('Credential format ID "%s" is not supported.', $requestedCredentialFormatId),
307+
400,
302308
);
303309
}
304310

@@ -359,6 +365,7 @@ public function credential(Request $request): Response
359365
return $this->routes->newJsonErrorResponse(
360366
'invalid_credential_request',
361367
'Can not resolve credential configuration ID.',
368+
400,
362369
);
363370
}
364371

@@ -369,6 +376,7 @@ public function credential(Request $request): Response
369376
return $this->routes->newJsonErrorResponse(
370377
'unsupported_credential_type',
371378
sprintf('Credential ID "%s" is not supported.', $resolvedCredentialIdentifier),
379+
400,
372380
);
373381
}
374382

@@ -450,27 +458,26 @@ public function credential(Request $request): Response
450458

451459
$this->loggerService->debug('Proof verified successfully using did:key ' . $didKey);
452460

453-
// Verify nonce
461+
// Verify nonce
454462
$nonce = $proof->getNonce();
455-
if ($nonce === null) {
456-
return $this->routes->newJsonErrorResponse(
457-
'invalid_proof',
458-
'Proof MUST contain a c_nonce.',
459-
);
460-
}
461-
462-
if (!$this->nonceService->validateNonce($nonce)) {
463-
return $this->routes->newJsonResponse(
464-
[
465-
'error' => 'invalid_nonce',
466-
'error_description' => 'c_nonce is invalid or expired.',
467-
'c_nonce' => $this->nonceService->generateNonce(),
468-
],
469-
400,
470-
);
463+
if (is_string($nonce) && $nonce !== '') {
464+
$this->loggerService->debug('Proof nonce: ' . $nonce);
465+
466+
if (!$this->nonceService->validateNonce($nonce)) {
467+
$this->loggerService->warning('Proof nonce is invalid or expired. Nonce was: ' . $nonce);
468+
return $this->routes->newJsonErrorResponse(
469+
error: 'invalid_nonce',
470+
description: 'c_nonce is invalid or expired.',
471+
httpCode: 400,
472+
);
473+
}
474+
475+
$this->loggerService->debug('Proof nonce validated successfully.');
476+
} else {
477+
$this->loggerService->warning('Nonce not present in proof, skipping nonce validation.');
471478
}
472479

473-
// Set it as a subject identifier (bind it).
480+
// Set it as a subject identifier (bind it).
474481
$sub = $didKey;
475482
} else {
476483
$this->loggerService->warning(
@@ -507,6 +514,7 @@ public function credential(Request $request): Response
507514
return $this->routes->newJsonErrorResponse(
508515
'invalid_proof',
509516
$message,
517+
400,
510518
);
511519
}
512520
}

src/Controllers/VerifiableCredentials/NonceController.php

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
namespace SimpleSAML\Module\oidc\Controllers\VerifiableCredentials;
66

7+
use SimpleSAML\Module\oidc\ModuleConfig;
8+
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
79
use SimpleSAML\Module\oidc\Services\LoggerService;
810
use SimpleSAML\Module\oidc\Services\NonceService;
911
use SimpleSAML\Module\oidc\Utils\Routes;
@@ -15,7 +17,12 @@ public function __construct(
1517
protected readonly NonceService $nonceService,
1618
protected readonly Routes $routes,
1719
protected readonly LoggerService $loggerService,
20+
protected readonly ModuleConfig $moduleConfig,
1821
) {
22+
if (!$this->moduleConfig->getVciEnabled()) {
23+
$this->loggerService->warning('Verifiable Credential capabilities not enabled.');
24+
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled.');
25+
}
1926
}
2027

2128
/**
@@ -30,7 +37,10 @@ public function nonce(): Response
3037
return $this->routes->newJsonResponse(
3138
['c_nonce' => $nonce],
3239
200,
33-
['Cache-Control' => 'no-store'],
40+
[
41+
'Cache-Control' => 'no-store',
42+
'Access-Control-Allow-Origin' => '*',
43+
],
3444
);
3545
}
3646
}

src/Services/NonceService.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,14 @@ public function validateNonce(string $nonce): bool
6363
return false;
6464
}
6565

66-
// Verify expiration
66+
// Verify expiration. This is also done in the JWS factory class.
6767
$currentTimestamp = $this->jws->helpers()->dateTime()->getUtc()->getTimestamp();
6868
if ($parsedJws->getExpirationTime() < $currentTimestamp) {
6969
$this->loggerService->warning('Nonce validation failed: expired.');
7070
return false;
7171
}
7272

73+
$this->loggerService->debug('Nonce validation succeeded.');
7374
return true;
7475
} catch (\Exception $e) {
7576
$this->loggerService->warning('Nonce validation failed: ' . $e->getMessage());

tests/unit/src/Controllers/VerifiableCredentials/NonceControllerTest.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use PHPUnit\Framework\MockObject\MockObject;
99
use PHPUnit\Framework\TestCase;
1010
use SimpleSAML\Module\oidc\Controllers\VerifiableCredentials\NonceController;
11+
use SimpleSAML\Module\oidc\ModuleConfig;
1112
use SimpleSAML\Module\oidc\Services\LoggerService;
1213
use SimpleSAML\Module\oidc\Services\NonceService;
1314
use SimpleSAML\Module\oidc\Utils\Routes;
@@ -19,12 +20,14 @@ class NonceControllerTest extends TestCase
1920
protected MockObject $nonceServiceMock;
2021
protected MockObject $routesMock;
2122
protected MockObject $loggerServiceMock;
23+
protected MockObject $moduleConfigMock;
2224

2325
public function setUp(): void
2426
{
2527
$this->nonceServiceMock = $this->createMock(NonceService::class);
2628
$this->routesMock = $this->createMock(Routes::class);
2729
$this->loggerServiceMock = $this->createMock(LoggerService::class);
30+
$this->moduleConfigMock = $this->createMock(ModuleConfig::class);
2831
}
2932

3033
/**
@@ -39,10 +42,23 @@ public function testNonce(): void
3942
$responseMock = $this->createMock(JsonResponse::class);
4043
$this->routesMock->expects($this->once())
4144
->method('newJsonResponse')
42-
->with(['c_nonce' => 'mocked_nonce'], 200, ['Cache-Control' => 'no-store'])
45+
->with(
46+
['c_nonce' => 'mocked_nonce'],
47+
200,
48+
['Cache-Control' => 'no-store', 'Access-Control-Allow-Origin' => '*'],
49+
)
4350
->willReturn($responseMock);
4451

45-
$sut = new NonceController($this->nonceServiceMock, $this->routesMock, $this->loggerServiceMock);
52+
$this->moduleConfigMock->expects($this->once())
53+
->method('getVciEnabled')
54+
->willReturn(true);
55+
56+
$sut = new NonceController(
57+
$this->nonceServiceMock,
58+
$this->routesMock,
59+
$this->loggerServiceMock,
60+
$this->moduleConfigMock,
61+
);
4662
$response = $sut->nonce();
4763

4864
$this->assertSame($responseMock, $response);

0 commit comments

Comments
 (0)