Skip to content

Commit 36d09d0

Browse files
committed
Add grant_type param to VCI api
1 parent 1937761 commit 36d09d0

File tree

6 files changed

+229
-100
lines changed

6 files changed

+229
-100
lines changed

routing/routes/routes.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use SimpleSAML\Module\oidc\Controllers\Admin\ConfigController;
1313
use SimpleSAML\Module\oidc\Controllers\Admin\FederationTestController;
1414
use SimpleSAML\Module\oidc\Controllers\Admin\VerifiableCredentailsTestController;
15-
use SimpleSAML\Module\oidc\Controllers\Api\VciCredentialOfferController;
15+
use SimpleSAML\Module\oidc\Controllers\Api\VciCredentialOfferApiController;
1616
use SimpleSAML\Module\oidc\Controllers\AuthorizationController;
1717
use SimpleSAML\Module\oidc\Controllers\ConfigurationDiscoveryController;
1818
use SimpleSAML\Module\oidc\Controllers\EndSessionController;
@@ -149,6 +149,6 @@
149149
$routes->add(
150150
RoutesEnum::ApiVciCredentialOffer->name,
151151
RoutesEnum::ApiVciCredentialOffer->value,
152-
)->controller([VciCredentialOfferController::class, 'credentialOffer'])
152+
)->controller([VciCredentialOfferApiController::class, 'credentialOffer'])
153153
->methods([HttpMethodsEnum::POST->value]);
154154
};
Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace SimpleSAML\Module\oidc\Controllers\Api;
6+
7+
use SimpleSAML\Module\oidc\Codebooks\ApiScopesEnum;
8+
use SimpleSAML\Module\oidc\Exceptions\AuthorizationException;
9+
use SimpleSAML\Module\oidc\Factories\CredentialOfferUriFactory;
10+
use SimpleSAML\Module\oidc\ModuleConfig;
11+
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
12+
use SimpleSAML\Module\oidc\Services\Api\Authorization;
13+
use SimpleSAML\Module\oidc\Services\LoggerService;
14+
use SimpleSAML\Module\oidc\Utils\Routes;
15+
use SimpleSAML\OpenID\Codebooks\GrantTypesEnum;
16+
use Symfony\Component\HttpFoundation\Request;
17+
use Symfony\Component\HttpFoundation\Response;
18+
19+
/**
20+
* TODO mivanci Add API documentation.
21+
*/
22+
class VciCredentialOfferApiController
23+
{
24+
/**
25+
* @throws OidcServerException
26+
*/
27+
public function __construct(
28+
protected readonly ModuleConfig $moduleConfig,
29+
protected readonly Authorization $authorization,
30+
protected readonly LoggerService $loggerService,
31+
protected readonly Routes $routes,
32+
protected readonly CredentialOfferUriFactory $credentialOfferUriFactory,
33+
) {
34+
if (!$this->moduleConfig->getApiEnabled()) {
35+
$this->loggerService->warning('API capabilities not enabled.');
36+
throw OidcServerException::forbidden('API capabilities not enabled.');
37+
}
38+
39+
if (!$this->moduleConfig->getVerifiableCredentialEnabled()) {
40+
$this->loggerService->warning('Verifiable Credential capabilities not enabled.');
41+
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled.');
42+
}
43+
}
44+
45+
/**
46+
*/
47+
public function credentialOffer(Request $request): Response
48+
{
49+
$this->loggerService->debug('VciCredentialOfferApiController::credentialOffer');
50+
51+
$this->loggerService->debug(
52+
'VciCredentialOfferApiController: Request data: ',
53+
$request->getPayload()->all(),
54+
);
55+
56+
try {
57+
$this->authorization->requireTokenForAnyOfScope(
58+
$request,
59+
[ApiScopesEnum::VciCredentialOffer, ApiScopesEnum::VciAll, ApiScopesEnum::All],
60+
);
61+
} catch (AuthorizationException $e) {
62+
$this->loggerService->error(
63+
'VciCredentialOfferApiController: AuthorizationException: ' . $e->getMessage(),
64+
);
65+
return $this->routes->newJsonErrorResponse(
66+
error: 'unauthorized',
67+
description: $e->getMessage(),
68+
httpCode: Response::HTTP_UNAUTHORIZED,
69+
);
70+
}
71+
72+
$input = $request->getPayload()->all();
73+
74+
$credentialConfigurationId = $input['credential_configuration_id'] ?? null;
75+
76+
if (!is_string($credentialConfigurationId)) {
77+
$this->loggerService->error(
78+
'VciCredentialOfferApiController: credential_configuration_id not provided or not a string.',
79+
);
80+
return $this->routes->newJsonErrorResponse(
81+
error: 'invalid_request',
82+
description: 'No credential configuration ID (credential_configuration_id) provided.',
83+
httpCode: Response::HTTP_BAD_REQUEST,
84+
);
85+
}
86+
87+
$credentialConfiguration = $this->moduleConfig->getCredentialConfiguration($credentialConfigurationId);
88+
89+
if (!is_array($credentialConfiguration)) {
90+
$this->loggerService->error(
91+
'VciCredentialOfferApiController: Provided Credential Configuration ID is not supported.',
92+
['credentialConfigurationId' => $credentialConfigurationId],
93+
);
94+
return $this->routes->newJsonErrorResponse(
95+
error: 'invalid_request',
96+
description: 'Provided credential configuration ID (credential_configuration_id) is not supported.',
97+
httpCode: Response::HTTP_BAD_REQUEST,
98+
);
99+
}
100+
101+
$grantType = $input['grant_type'] ?? null;
102+
103+
if (!is_string($grantType)) {
104+
$this->loggerService->error('VciCredentialOfferApiController: Grant Type (grant_type) not provided.');
105+
return $this->routes->newJsonErrorResponse(
106+
error: 'invalid_request',
107+
description: 'No credential Grant Type (grant_type) provided.',
108+
httpCode: Response::HTTP_BAD_REQUEST,
109+
);
110+
}
111+
112+
$grantTypeEnum = GrantTypesEnum::tryFrom($grantType);
113+
114+
if (!$grantTypeEnum instanceof GrantTypesEnum) {
115+
$this->loggerService->error(
116+
'VciCredentialOfferApiController: Invalid credential Grant Type (grant_type) provided.',
117+
['grantType' => $grantType],
118+
);
119+
return $this->routes->newJsonErrorResponse(
120+
error: 'invalid_request',
121+
description: 'Invalid credential Grant Type (grant_type) provided.',
122+
httpCode: Response::HTTP_BAD_REQUEST,
123+
);
124+
}
125+
126+
if (!$grantTypeEnum->canBeUsedForVerifiableCredentialIssuance()) {
127+
$this->loggerService->error(
128+
'VciCredentialOfferApiController: Provided Grant Type can not be used for verifiable credential' .
129+
' issuance.',
130+
['grantType' => $grantType],
131+
);
132+
return $this->routes->newJsonErrorResponse(
133+
error: 'invalid_request',
134+
description: 'Provided Grant Type can not be used for verifiable credential issuance.',
135+
httpCode: Response::HTTP_BAD_REQUEST,
136+
);
137+
}
138+
139+
$credentialOfferUri = null;
140+
141+
if ($grantTypeEnum === GrantTypesEnum::AuthorizationCode) {
142+
$this->loggerService->debug(
143+
'VciCredentialOfferApiController: AuthorizationCode Grant Type provided. Building credential ' .
144+
'offer for Authorization Code Flow.',
145+
);
146+
$credentialOfferUri = $this->credentialOfferUriFactory->buildForAuthorization(
147+
[$credentialConfigurationId],
148+
);
149+
}
150+
151+
if ($grantTypeEnum === GrantTypesEnum::PreAuthorizedCode) {
152+
$this->loggerService->debug(
153+
'VciCredentialOfferApiController: PreAuthorizedCode Grant Type provided. Building credential ' .
154+
'offer for Pre-authorized Code Flow.',
155+
);
156+
157+
/** @psalm-suppress MixedAssignment */
158+
$userAttributes = $input['user_attributes'] ?? [];
159+
$userAttributes = is_array($userAttributes) ? $userAttributes : [];
160+
$useTxCode = boolval($input['use_tx_code'] ?? false);
161+
/** @psalm-suppress MixedAssignment */
162+
$usersEmailAttributeName = $input['users_email_attribute_name'] ?? null;
163+
$usersEmailAttributeName = is_string($usersEmailAttributeName) ? $usersEmailAttributeName : null;
164+
/** @psalm-suppress MixedAssignment */
165+
$authenticationSourceId = $input['authentication_source_id'] ?? null;
166+
$authenticationSourceId = is_string($authenticationSourceId) ? $authenticationSourceId : null;
167+
168+
if (is_null($usersEmailAttributeName) && is_string($authenticationSourceId)) {
169+
$usersEmailAttributeName = $this->moduleConfig->getUsersEmailAttributeNameForAuthSourceId(
170+
$authenticationSourceId,
171+
);
172+
}
173+
174+
$this->loggerService->debug(
175+
'VciCredentialOfferApiController: PreAuthorizedCode data:',
176+
[
177+
'userAttributes' => $userAttributes,
178+
'useTxCode' => $useTxCode,
179+
'authenticationSourceId' => $authenticationSourceId,
180+
'usersEmailAttributeName' => $usersEmailAttributeName,
181+
],
182+
);
183+
184+
$credentialOfferUri = $this->credentialOfferUriFactory->buildPreAuthorized(
185+
[$credentialConfigurationId],
186+
$userAttributes,
187+
$useTxCode,
188+
$usersEmailAttributeName,
189+
);
190+
}
191+
192+
if ($credentialOfferUri !== null) {
193+
$data = [
194+
'credential_offer_uri' => $credentialOfferUri,
195+
];
196+
197+
$this->loggerService->debug(
198+
'VciCredentialOfferApiController: Credential Offer URI built successfully, returning data:',
199+
$data,
200+
);
201+
return $this->routes->newJsonResponse(
202+
data: $data,
203+
);
204+
}
205+
206+
$this->loggerService->debug(
207+
'VciCredentialOfferApiController: Credential Offer URI NOT built for provided Grant Type.',
208+
['grantType' => $grantType],
209+
);
210+
211+
return $this->routes->newJsonErrorResponse(
212+
error: 'invalid_request',
213+
description: 'No implementation for provided Grant Type.',
214+
httpCode: Response::HTTP_BAD_REQUEST,
215+
);
216+
}
217+
}

src/Controllers/Api/VciCredentialOfferController.php

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/Controllers/VerifiableCredentials/CredentialIssuerConfigurationController.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use SimpleSAML\Module\oidc\ModuleConfig;
1717
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
18+
use SimpleSAML\Module\oidc\Services\LoggerService;
1819
use SimpleSAML\Module\oidc\Utils\Routes;
1920
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
2021
use Symfony\Component\HttpFoundation\Response;
@@ -27,9 +28,11 @@ class CredentialIssuerConfigurationController
2728
public function __construct(
2829
protected readonly ModuleConfig $moduleConfig,
2930
protected readonly Routes $routes,
31+
protected readonly LoggerService $loggerService,
3032
) {
3133
if (!$this->moduleConfig->getVerifiableCredentialEnabled()) {
32-
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled');
34+
$this->loggerService->warning('Verifiable Credential capabilities not enabled.');
35+
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled.');
3336
}
3437
}
3538

src/Controllers/VerifiableCredentials/CredentialIssuerCredentialController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ public function __construct(
5555
protected readonly Did $did,
5656
) {
5757
if (!$this->moduleConfig->getVerifiableCredentialEnabled()) {
58-
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled');
58+
$this->loggerService->warning('Verifiable Credential capabilities not enabled.');
59+
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled.');
5960
}
6061
}
6162

src/Controllers/VerifiableCredentials/JwtVcIssuerConfigurationController.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use SimpleSAML\Module\oidc\Codebooks\RoutesEnum;
1717
use SimpleSAML\Module\oidc\ModuleConfig;
1818
use SimpleSAML\Module\oidc\Server\Exceptions\OidcServerException;
19+
use SimpleSAML\Module\oidc\Services\LoggerService;
1920
use SimpleSAML\Module\oidc\Utils\Routes;
2021
use SimpleSAML\OpenID\Codebooks\ClaimsEnum;
2122
use Symfony\Component\HttpFoundation\Response;
@@ -28,9 +29,11 @@ class JwtVcIssuerConfigurationController
2829
public function __construct(
2930
protected readonly ModuleConfig $moduleConfig,
3031
protected readonly Routes $routes,
32+
protected readonly LoggerService $loggerService,
3133
) {
3234
if (!$this->moduleConfig->getVerifiableCredentialEnabled()) {
33-
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled');
35+
$this->loggerService->warning('Verifiable Credential capabilities not enabled.');
36+
throw OidcServerException::forbidden('Verifiable Credential capabilities not enabled.');
3437
}
3538
}
3639

0 commit comments

Comments
 (0)