Skip to content

Commit cd7a71e

Browse files
committed
Initial auth-code implementation
1 parent 1210f98 commit cd7a71e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+660
-144
lines changed

docker/conformance.sql

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,16 @@ CREATE TABLE oidc_client (
4343
updated_at TIMESTAMP NULL DEFAULT NULL,
4444
created_at TIMESTAMP NULL DEFAULT NULL,
4545
expires_at TIMESTAMP NULL DEFAULT NULL,
46-
is_federated BOOLEAN NOT NULL DEFAULT false
46+
is_federated BOOLEAN NOT NULL DEFAULT false,
47+
is_generic BOOLEAN NOT NULL DEFAULT false
4748
);
4849
-- Used 'httpd' host for back-channel logout url (https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout)
4950
-- since this is the hostname of conformance server while running in container environment
50-
INSERT INTO oidc_client VALUES('_55a99a1d298da921cb27d700d4604352e51171ebc4','_8967dd97d07cc59db7055e84ac00e79005157c1132','Conformance Client 1',replace('Client 1 for Conformance Testing https://openid.net/certification/connect_op_testing/\n','\n',char(10)),'example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone","offline_access"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false);
51-
INSERT INTO oidc_client VALUES('_34efb61060172a11d62101bc804db789f8f9100b0e','_91a4607a1c10ba801268929b961b3f6c067ff82d21','Conformance Client 2','','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","offline_access"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false);
52-
INSERT INTO oidc_client VALUES('_0afb7d18e54b2de8205a93e38ca119e62ee321d031','_944e73bbeec7850d32b68f1b5c780562c955967e4e','Conformance Client 3','Client for client_secret_post','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false);
53-
INSERT INTO oidc_client VALUES('_8957eda35234902ba8343c0cdacac040310f17dfca','_322d16999f9da8b5abc9e9c0c08e853f60f4dc4804','RP-Initiated Logout Client','Client for testing RP-Initiated Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]',NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false);
54-
INSERT INTO oidc_client VALUES('_9fe2f7589ece1b71f5ef75a91847d71bc5125ec2a6','_3c0beb20194179c01d7796c6836f62801e9ed4b368','Back-Channel Logout Client','Client for testing Back-Channel Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false);
51+
INSERT INTO oidc_client VALUES('_55a99a1d298da921cb27d700d4604352e51171ebc4','_8967dd97d07cc59db7055e84ac00e79005157c1132','Conformance Client 1',replace('Client 1 for Conformance Testing https://openid.net/certification/connect_op_testing/\n','\n',char(10)),'example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone","offline_access"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false);
52+
INSERT INTO oidc_client VALUES('_34efb61060172a11d62101bc804db789f8f9100b0e','_91a4607a1c10ba801268929b961b3f6c067ff82d21','Conformance Client 2','','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","offline_access"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false);
53+
INSERT INTO oidc_client VALUES('_0afb7d18e54b2de8205a93e38ca119e62ee321d031','_944e73bbeec7850d32b68f1b5c780562c955967e4e','Conformance Client 3','Client for client_secret_post','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email"]',1,1,NULL,NULL,NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false);
54+
INSERT INTO oidc_client VALUES('_8957eda35234902ba8343c0cdacac040310f17dfca','_322d16999f9da8b5abc9e9c0c08e853f60f4dc4804','RP-Initiated Logout Client','Client for testing RP-Initiated Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]',NULL,NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false);
55+
INSERT INTO oidc_client VALUES('_9fe2f7589ece1b71f5ef75a91847d71bc5125ec2a6','_3c0beb20194179c01d7796c6836f62801e9ed4b368','Back-Channel Logout Client','Client for testing Back-Channel Logout','example-userpass','["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/callback","https:\/\/www.certification.openid.net\/test\/a\/simplesamlphp-module-oidc\/callback"]','["openid","profile","email","address","phone"]',1,1,NULL,'["https:\/\/localhost.emobix.co.uk:8443\/test\/a\/simplesamlphp-module-oidc\/post_logout_redirect"]','https://httpd:8443/test/a/simplesamlphp-module-oidc/backchannel_logout',NULL,NULL, NULL, NULL, NULL, NULL, 'manual', NULL, NULL, NULL, false, false);
5556
CREATE TABLE oidc_access_token (
5657
id VARCHAR(191) PRIMARY KEY NOT NULL,
5758
scopes TEXT,
@@ -60,6 +61,10 @@ CREATE TABLE oidc_access_token (
6061
client_id VARCHAR(191) NOT NULL,
6162
is_revoked BOOLEAN NOT NULL DEFAULT false,
6263
auth_code_id varchar(191) DEFAULT NULL, requested_claims TEXT NULL,
64+
flow_type CHAR(64) NULL,
65+
authorization_details TEXT NULL,
66+
bound_client_id TEXT NULL,
67+
bound_redirect_uri TEXT NULL,
6368
CONSTRAINT FK_43C1650EA76ED395 FOREIGN KEY (user_id)
6469
REFERENCES oidc_user (id) ON DELETE CASCADE,
6570
CONSTRAINT FK_43C1650E19EB6921 FOREIGN KEY (client_id)
@@ -85,6 +90,8 @@ CREATE TABLE oidc_auth_code (
8590
flow_type CHAR(64) DEFAULT NULL,
8691
tx_code varchar(191) DEFAULT NULL,
8792
authorization_details TEXT NULL,
93+
bound_client_id TEXT NULL,
94+
bound_redirect_uri TEXT NULL,
8895
CONSTRAINT FK_97D32CA7A76ED395 FOREIGN KEY (user_id)
8996
REFERENCES oidc_user (id) ON DELETE CASCADE,
9097
CONSTRAINT FK_97D32CA719EB6921 FOREIGN KEY (client_id)

src/Codebooks/FlowTypeEnum.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,20 @@ enum FlowTypeEnum: string
1313

1414
case VciAuthorizationCode = 'vci_authorization_code';
1515
case VciPreAuthorizedCode = 'vci_pre_authorized_code';
16+
17+
public function isOidcFlow(): bool
18+
{
19+
return match ($this) {
20+
self::OidcAuthorizationCode, self::OidcImplicit, self::OidcHybrid => true,
21+
default => false,
22+
};
23+
}
24+
25+
public function isVciFlow(): bool
26+
{
27+
return match ($this) {
28+
self::VciAuthorizationCode, self::VciPreAuthorizedCode => true,
29+
default => false,
30+
};
31+
}
1632
}

src/Entities/AccessTokenEntity.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
use League\OAuth2\Server\Entities\Traits\AccessTokenTrait;
2525
use League\OAuth2\Server\Entities\Traits\EntityTrait;
2626
use League\OAuth2\Server\Entities\Traits\TokenEntityTrait;
27+
use SimpleSAML\Module\oidc\Codebooks\FlowTypeEnum;
2728
use SimpleSAML\Module\oidc\Entities\Interfaces\AccessTokenEntityInterface;
2829
use SimpleSAML\Module\oidc\Entities\Interfaces\EntityStringRepresentationInterface;
2930
use SimpleSAML\Module\oidc\Entities\Traits\AssociateWithAuthCodeTrait;
@@ -69,6 +70,10 @@ public function __construct(
6970
?array $requestedClaims = null,
7071
?bool $isRevoked = false,
7172
?Configuration $jwtConfiguration = null,
73+
protected readonly ?FlowTypeEnum $flowTypeEnum = null,
74+
protected readonly ?array $authorizationDetails = null,
75+
protected readonly ?string $boundClientId = null,
76+
protected readonly ?string $boundRedirectUri = null,
7277
) {
7378
$this->setIdentifier($id);
7479
$this->setClient($clientEntity);
@@ -114,6 +119,12 @@ public function getState(): array
114119
'is_revoked' => $this->isRevoked(),
115120
'auth_code_id' => $this->getAuthCodeId(),
116121
'requested_claims' => json_encode($this->requestedClaims, JSON_THROW_ON_ERROR),
122+
'flow_type' => $this->flowTypeEnum?->value,
123+
'authorization_details' => is_array($this->authorizationDetails) ?
124+
json_encode($this->authorizationDetails, JSON_THROW_ON_ERROR) :
125+
null,
126+
'bound_client_id' => $this->boundClientId,
127+
'bound_redirect_uri' => $this->boundRedirectUri,
117128
];
118129
}
119130

@@ -158,4 +169,24 @@ protected function convertToJWT(): Token
158169

159170
return $this->jsonWebTokenBuilderService->getSignedProtocolJwt($jwtBuilder);
160171
}
172+
173+
public function getFlowTypeEnum(): ?FlowTypeEnum
174+
{
175+
return $this->flowTypeEnum;
176+
}
177+
178+
public function getAuthorizationDetails(): ?array
179+
{
180+
return $this->authorizationDetails;
181+
}
182+
183+
public function getBoundClientId(): ?string
184+
{
185+
return $this->boundClientId;
186+
}
187+
188+
public function getBoundRedirectUri(): ?string
189+
{
190+
return $this->boundRedirectUri;
191+
}
161192
}

src/Entities/AuthCodeEntity.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public function __construct(
4747
protected readonly ?FlowTypeEnum $flowTypeEnum = null,
4848
protected readonly ?string $txCode = null,
4949
protected readonly ?array $authorizationDetails = null,
50+
protected readonly ?string $boundClientId = null,
51+
protected readonly ?string $boundRedirectUri = null,
5052
) {
5153
$this->identifier = $id;
5254
$this->client = $client;
@@ -77,6 +79,8 @@ public function getState(): array
7779
'authorization_details' => is_array($this->authorizationDetails) ?
7880
json_encode($this->authorizationDetails, JSON_THROW_ON_ERROR) :
7981
null,
82+
'bound_client_id' => $this->boundClientId,
83+
'bound_redirect_uri' => $this->boundRedirectUri,
8084
];
8185
}
8286

@@ -99,4 +103,14 @@ public function getAuthorizationDetails(): ?array
99103
{
100104
return $this->authorizationDetails;
101105
}
106+
107+
public function getBoundClientId(): ?string
108+
{
109+
return $this->boundClientId;
110+
}
111+
112+
public function getBoundRedirectUri(): ?string
113+
{
114+
return $this->boundRedirectUri;
115+
}
102116
}

src/Entities/ClientEntity.php

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ class ClientEntity implements ClientEntityInterface
5151
public const KEY_CREATED_AT = 'created_at';
5252
public const KEY_EXPIRES_AT = 'expires_at';
5353
public const KEY_IS_FEDERATED = 'is_federated';
54+
public const KEY_IS_GENERIC = 'is_generic';
5455

5556
private string $secret;
5657

@@ -93,6 +94,7 @@ class ClientEntity implements ClientEntityInterface
9394
private ?DateTimeImmutable $createdAt;
9495
private ?DateTimeImmutable $expiresAt;
9596
private bool $isFederated;
97+
private bool $isGeneric;
9698

9799
/**
98100
* @param string[] $redirectUri
@@ -126,6 +128,7 @@ public function __construct(
126128
?DateTimeImmutable $createdAt = null,
127129
?DateTimeImmutable $expiresAt = null,
128130
bool $isFederated = false,
131+
bool $isGeneric = false,
129132
) {
130133
$this->identifier = $identifier;
131134
$this->secret = $secret;
@@ -150,6 +153,7 @@ public function __construct(
150153
$this->createdAt = $createdAt;
151154
$this->expiresAt = $expiresAt;
152155
$this->isFederated = $isFederated;
156+
$this->isGeneric = $isGeneric;
153157
}
154158

155159
/**
@@ -188,6 +192,7 @@ public function getState(): array
188192
self::KEY_CREATED_AT => $this->getCreatedAt()?->format('Y-m-d H:i:s'),
189193
self::KEY_EXPIRES_AT => $this->getExpiresAt()?->format('Y-m-d H:i:s'),
190194
self::KEY_IS_FEDERATED => $this->isFederated(),
195+
self::KEY_IS_GENERIC => $this->isGeneric(),
191196
];
192197
}
193198

@@ -217,6 +222,7 @@ public function toArray(): array
217222
self::KEY_CREATED_AT => $this->createdAt,
218223
self::KEY_EXPIRES_AT => $this->expiresAt,
219224
self::KEY_IS_FEDERATED => $this->isFederated,
225+
self::KEY_IS_GENERIC => $this->isGeneric,
220226
];
221227
}
222228

@@ -355,4 +361,9 @@ public function isFederated(): bool
355361
{
356362
return $this->isFederated;
357363
}
364+
365+
public function isGeneric(): bool
366+
{
367+
return $this->isGeneric;
368+
}
358369
}

src/Entities/Interfaces/ClientEntityInterface.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,5 @@ public function getCreatedAt(): ?DateTimeImmutable;
7979
public function getExpiresAt(): ?DateTimeImmutable;
8080
public function isExpired(): bool;
8181
public function isFederated(): bool;
82+
public function isGeneric(): bool;
8283
}

src/Factories/Entities/AccessTokenEntityFactory.php

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use DateTimeImmutable;
88
use League\OAuth2\Server\CryptKey;
99
use League\OAuth2\Server\Entities\ClientEntityInterface as OAuth2ClientEntityInterface;
10+
use SimpleSAML\Module\oidc\Codebooks\FlowTypeEnum;
1011
use SimpleSAML\Module\oidc\Entities\AccessTokenEntity;
1112
use SimpleSAML\Module\oidc\Entities\Interfaces\ClientEntityInterface;
1213
use SimpleSAML\Module\oidc\Helpers;
@@ -35,6 +36,10 @@ public function fromData(
3536
?string $authCodeId = null,
3637
?array $requestedClaims = null,
3738
?bool $isRevoked = false,
39+
?FlowTypeEnum $flowTypeEnum = null,
40+
?array $authorizationDetails = null,
41+
?string $boundClientId = null,
42+
?string $boundRedirectUri = null,
3843
): AccessTokenEntity {
3944
return new AccessTokenEntity(
4045
$id,
@@ -47,6 +52,10 @@ public function fromData(
4752
$authCodeId,
4853
$requestedClaims,
4954
$isRevoked,
55+
flowTypeEnum: $flowTypeEnum,
56+
authorizationDetails: $authorizationDetails,
57+
boundClientId: $boundClientId,
58+
boundRedirectUri: $boundRedirectUri,
5059
);
5160
}
5261

@@ -90,6 +99,16 @@ public function fromState(array $state): AccessTokenEntity
9099
throw OidcServerException::serverError('Invalid Access Token Entity state: requested claims');
91100
}
92101

102+
$flowType = empty($state['flow_type']) ? null : FlowTypeEnum::tryFrom((string)$state['flow_type']);
103+
/** @psalm-suppress MixedAssignment */
104+
$authorizationDetails = isset($state['authorization_details']) && is_string($state['authorization_details']) ?
105+
json_decode($state['authorization_details'], true, 512, JSON_THROW_ON_ERROR) :
106+
null;
107+
$authorizationDetails = is_array($authorizationDetails) ? $authorizationDetails : null;
108+
109+
$boundClientId = empty($state['bound_client_id']) ? null : (string)$state['bound_client_id'];
110+
$boundRedirectUri = empty($state['bound_redirect_uri']) ? null : (string)$state['bound_redirect_uri'];
111+
93112
return $this->fromData(
94113
$id,
95114
$client,
@@ -99,6 +118,10 @@ public function fromState(array $state): AccessTokenEntity
99118
$authCodeId,
100119
$stateRequestedClaims,
101120
$isRevoked,
121+
$flowType,
122+
$authorizationDetails,
123+
$boundClientId,
124+
$boundRedirectUri,
102125
);
103126
}
104127
}

src/Factories/Entities/AuthCodeEntityFactory.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ public function fromData(
3535
?FlowTypeEnum $flowTypeEnum = null,
3636
?string $txCode = null,
3737
?array $authorizationDetails = null,
38+
?string $boundClientId = null,
39+
?string $boundRedirectUri = null,
3840
): AuthCodeEntity {
3941
return new AuthCodeEntity(
4042
$id,
@@ -48,6 +50,8 @@ public function fromData(
4850
$flowTypeEnum,
4951
$txCode,
5052
$authorizationDetails,
53+
$boundClientId,
54+
$boundRedirectUri,
5155
);
5256
}
5357

@@ -97,6 +101,9 @@ public function fromState(array $state): AuthCodeEntity
97101
null;
98102
$authorizationDetails = is_array($authorizationDetails) ? $authorizationDetails : null;
99103

104+
$boundClientId = empty($state['bound_client_id']) ? null : (string)$state['bound_client_id'];
105+
$boundRedirectUri = empty($state['bound_redirect_uri']) ? null : (string)$state['bound_redirect_uri'];
106+
100107
return $this->fromData(
101108
$id,
102109
$client,
@@ -109,6 +116,8 @@ public function fromState(array $state): AuthCodeEntity
109116
$flowType,
110117
$txCode,
111118
$authorizationDetails,
119+
$boundClientId,
120+
$boundRedirectUri,
112121
);
113122
}
114123
}

src/Factories/Entities/ClientEntityFactory.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public function fromData(
6666
?DateTimeImmutable $createdAt = null,
6767
?DateTimeImmutable $expiresAt = null,
6868
bool $isFederated = false,
69+
bool $isGeneric = false,
6970
): ClientEntityInterface {
7071
return new ClientEntity(
7172
$id,
@@ -91,6 +92,7 @@ public function fromData(
9192
$createdAt,
9293
$expiresAt,
9394
$isFederated,
95+
$isGeneric,
9496
);
9597
}
9698

@@ -192,6 +194,7 @@ public function fromRegistrationData(
192194
// $expiresAt = $expiresAt;
193195

194196
$isFederated = $existingClient?->isFederated() ?? false;
197+
$isGeneric = $existingClient?->isGeneric() ?? false;
195198

196199
return $this->fromData(
197200
$id,
@@ -217,6 +220,7 @@ public function fromRegistrationData(
217220
$createdAt,
218221
$expiresAt,
219222
$isFederated,
223+
$isGeneric,
220224
);
221225
}
222226

@@ -355,6 +359,7 @@ public function fromState(array $state): ClientEntityInterface
355359
$this->helpers->dateTime()->getUtc((string)$state[ClientEntity::KEY_EXPIRES_AT]);
356360

357361
$isFederated = (bool)$state[ClientEntity::KEY_IS_FEDERATED];
362+
$isGeneric = (bool)$state[ClientEntity::KEY_IS_GENERIC];
358363

359364
return $this->fromData(
360365
$id,
@@ -380,6 +385,7 @@ public function fromState(array $state): ClientEntityInterface
380385
$createdAt,
381386
$expiresAt,
382387
$isFederated,
388+
$isGeneric,
383389
);
384390
}
385391

@@ -404,6 +410,7 @@ public function getGenericForVci(): ClientEntityInterface
404410
isEnabled: true,
405411
updatedAt: $createdAt,
406412
createdAt: $createdAt,
413+
isGeneric: true,
407414
);
408415
}
409416
}

0 commit comments

Comments
 (0)