Initial Version
This commit is contained in:
157
lib/Provider.php
Normal file
157
lib/Provider.php
Normal file
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KTXM\AuthenticationProviderOidc;
|
||||
|
||||
use KTXF\Security\Authentication\AuthenticationProviderAbstract;
|
||||
use KTXF\Security\Authentication\ProviderContext;
|
||||
use KTXF\Security\Authentication\ProviderResult;
|
||||
|
||||
/**
|
||||
* OpenID Connect Identity Provider
|
||||
*
|
||||
* Implements OIDC authentication flow for SSO.
|
||||
* Can be used as primary (redirect to IdP) or secondary (step-up authentication).
|
||||
*/
|
||||
class Provider extends AuthenticationProviderAbstract
|
||||
{
|
||||
public function __construct(
|
||||
private readonly OidcClient $client,
|
||||
) { }
|
||||
|
||||
// =========================================================================
|
||||
// Provider Implementation
|
||||
// =========================================================================
|
||||
|
||||
public function type(): string
|
||||
{
|
||||
return 'authentication';
|
||||
}
|
||||
|
||||
public function identifier(): string
|
||||
{
|
||||
return 'oidc';
|
||||
}
|
||||
|
||||
public function method(): string
|
||||
{
|
||||
return self::METHOD_REDIRECT;
|
||||
}
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
return 'OpenID Connect';
|
||||
}
|
||||
|
||||
public function description(): string
|
||||
{
|
||||
return 'Authenticate using an OpenID Connect identity provider.';
|
||||
}
|
||||
|
||||
public function icon(): string
|
||||
{
|
||||
return 'fa-solid fa-circle-o';
|
||||
}
|
||||
|
||||
public function beginRedirect(ProviderContext $context, string $callbackUrl, ?string $returnUrl = null): ProviderResult
|
||||
{
|
||||
$config = $context->config;
|
||||
|
||||
if (empty($config)) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INVALID_PROVIDER,
|
||||
'OIDC provider configuration is missing'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$authData = $this->client->beginAuth($config, $callbackUrl, $returnUrl);
|
||||
|
||||
return ProviderResult::redirect(
|
||||
$authData['redirect_url'],
|
||||
[
|
||||
'state' => $authData['state'],
|
||||
'nonce' => $authData['nonce'] ?? null,
|
||||
'return_url' => $returnUrl,
|
||||
]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INTERNAL,
|
||||
'Failed to initiate OIDC authentication: ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function completeRedirect(ProviderContext $context, array $params): ProviderResult
|
||||
{
|
||||
$config = $context->config;
|
||||
$expectedState = $context->getMeta('state');
|
||||
$expectedNonce = $context->getMeta('nonce');
|
||||
|
||||
if (empty($config)) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INVALID_PROVIDER,
|
||||
'OIDC provider configuration is missing'
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($expectedState)) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INVALID_CREDENTIALS,
|
||||
'Missing expected state for OIDC verification'
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->client->completeAuth($params, $config, $expectedState, $expectedNonce);
|
||||
|
||||
if (!$result || !isset($result['success']) || !$result['success']) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INVALID_CREDENTIALS,
|
||||
$result['error'] ?? 'OIDC authentication failed'
|
||||
);
|
||||
}
|
||||
|
||||
return ProviderResult::success(
|
||||
[
|
||||
'provider' => $this->identifier(),
|
||||
'subject' => $result['sub'] ?? null,
|
||||
'email' => $result['email'] ?? null,
|
||||
'name' => $result['name'] ?? null,
|
||||
'attributes' => $result['attributes'] ?? [],
|
||||
],
|
||||
[
|
||||
'return_url' => $context->getMeta('return_url'),
|
||||
]
|
||||
);
|
||||
} catch (\Throwable $e) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INTERNAL,
|
||||
'Failed to complete OIDC authentication: ' . $e->getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Attribute Helpers
|
||||
// =========================================================================
|
||||
|
||||
public function getAttributes(array $identity): array
|
||||
{
|
||||
return $identity['attributes'] ?? [];
|
||||
}
|
||||
|
||||
public function mapAttributes(array $attributes, array $mapping): array
|
||||
{
|
||||
$mapped = [];
|
||||
foreach ($mapping as $sourceKey => $targetKey) {
|
||||
if (isset($attributes[$sourceKey])) {
|
||||
$mapped[$targetKey] = $attributes[$sourceKey];
|
||||
}
|
||||
}
|
||||
return $mapped;
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user