authentication provider provisioning
This commit is contained in:
180
lib/Provider.php
180
lib/Provider.php
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace KTXM\AuthenticationProviderOidc;
|
||||
|
||||
use KTXC\Service\UserService;
|
||||
use KTXF\Security\Authentication\AuthenticationProviderAbstract;
|
||||
use KTXF\Security\Authentication\ProviderContext;
|
||||
use KTXF\Security\Authentication\ProviderResult;
|
||||
@@ -18,6 +19,7 @@ class Provider extends AuthenticationProviderAbstract
|
||||
{
|
||||
public function __construct(
|
||||
private readonly OidcClient $client,
|
||||
private readonly UserService $userService,
|
||||
) { }
|
||||
|
||||
// =========================================================================
|
||||
@@ -114,13 +116,21 @@ class Provider extends AuthenticationProviderAbstract
|
||||
);
|
||||
}
|
||||
|
||||
// Provision/update user directly
|
||||
$user = $this->provisionUser($result, $config);
|
||||
|
||||
if (!$user) {
|
||||
return ProviderResult::failed(
|
||||
ProviderResult::ERROR_INTERNAL,
|
||||
'Failed to provision user from OIDC identity'
|
||||
);
|
||||
}
|
||||
|
||||
// Return success with user identifier
|
||||
return ProviderResult::success(
|
||||
[
|
||||
'provider' => $this->identifier(),
|
||||
'subject' => $result['sub'] ?? null,
|
||||
'email' => $result['email'] ?? null,
|
||||
'name' => $result['name'] ?? null,
|
||||
'attributes' => $result['attributes'] ?? [],
|
||||
'user_identifier' => $user['uid'],
|
||||
'user_identity' => $user['identity'],
|
||||
],
|
||||
[
|
||||
'return_url' => $context->getMeta('return_url'),
|
||||
@@ -135,23 +145,165 @@ class Provider extends AuthenticationProviderAbstract
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Attribute Helpers
|
||||
// Provisioning Helpers
|
||||
// =========================================================================
|
||||
|
||||
public function getAttributes(array $identity): array
|
||||
/**
|
||||
* Provision or update user from OIDC identity
|
||||
*/
|
||||
private function provisionUser(array $oidcData, array $config): ?array
|
||||
{
|
||||
return $identity['attributes'] ?? [];
|
||||
$subject = $oidcData['sub'] ?? null;
|
||||
$email = $oidcData['email'] ?? null;
|
||||
|
||||
if (!$subject || !$email) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if user exists by provider subject
|
||||
$user = $this->userService->fetchByProviderSubject($this->identifier(), $subject);
|
||||
|
||||
if ($user) {
|
||||
// Update existing user
|
||||
return $this->updateUser($user, $oidcData, $config);
|
||||
}
|
||||
|
||||
// Check if user exists by email
|
||||
$user = $this->userService->fetchByIdentityRaw($email);
|
||||
|
||||
if ($user) {
|
||||
// Link existing user to this OIDC provider
|
||||
return $this->linkUser($user, $oidcData, $config);
|
||||
}
|
||||
|
||||
// Create new user
|
||||
return $this->createUser($oidcData, $config);
|
||||
}
|
||||
|
||||
public function mapAttributes(array $attributes, array $mapping): array
|
||||
/**
|
||||
* Create new user from OIDC data
|
||||
*/
|
||||
private function createUser(array $oidcData, array $config): array
|
||||
{
|
||||
$mapped = [];
|
||||
foreach ($mapping as $sourceKey => $targetKey) {
|
||||
if (isset($attributes[$sourceKey])) {
|
||||
$mapped[$targetKey] = $attributes[$sourceKey];
|
||||
$managedFields = $this->getManagedFields($config);
|
||||
|
||||
$userData = [
|
||||
'identity' => $oidcData['email'],
|
||||
'label' => $oidcData['name'] ?? $oidcData['email'],
|
||||
'enabled' => true,
|
||||
'provider' => $this->identifier(),
|
||||
'provider_subject' => $oidcData['sub'],
|
||||
'provider_synced_at' => time(),
|
||||
'provider_managed_fields' => $managedFields,
|
||||
'profile' => [],
|
||||
];
|
||||
|
||||
// Map OIDC attributes to profile
|
||||
$mapping = [
|
||||
'given_name' => 'name_given',
|
||||
'family_name' => 'name_family',
|
||||
'picture' => 'avatar',
|
||||
'email' => 'email',
|
||||
'phone_number' => 'phone',
|
||||
];
|
||||
|
||||
foreach ($mapping as $oidcField => $profileField) {
|
||||
if (isset($oidcData[$oidcField])) {
|
||||
$userData['profile'][$profileField] = $oidcData[$oidcField];
|
||||
}
|
||||
}
|
||||
return $mapped;
|
||||
|
||||
return $this->userService->createUser($userData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update existing user with OIDC data
|
||||
*/
|
||||
private function updateUser(array $user, array $oidcData, array $config): array
|
||||
{
|
||||
$managedFields = $this->getManagedFields($config);
|
||||
|
||||
// Update provider sync metadata
|
||||
$this->userService->updateUser($user['uid'], [
|
||||
'provider_synced_at' => time(),
|
||||
'provider_managed_fields' => $managedFields,
|
||||
]);
|
||||
|
||||
// Update label if provided
|
||||
if (isset($oidcData['name'])) {
|
||||
$this->userService->updateUser($user['uid'], [
|
||||
'label' => $oidcData['name'],
|
||||
]);
|
||||
}
|
||||
|
||||
// Update profile fields (only managed ones will be updated due to storeProfile filtering)
|
||||
$profileUpdates = [];
|
||||
$mapping = [
|
||||
'given_name' => 'name_given',
|
||||
'family_name' => 'name_family',
|
||||
'picture' => 'avatar',
|
||||
];
|
||||
|
||||
foreach ($mapping as $oidcField => $profileField) {
|
||||
if (isset($oidcData[$oidcField]) && in_array($profileField, $managedFields)) {
|
||||
$profileUpdates[$profileField] = $oidcData[$oidcField];
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($profileUpdates)) {
|
||||
// Use updateUser with profile. notation since these are managed fields
|
||||
$updates = [];
|
||||
foreach ($profileUpdates as $field => $value) {
|
||||
$updates["profile.{$field}"] = $value;
|
||||
}
|
||||
$this->userService->updateUser($user['uid'], $updates);
|
||||
}
|
||||
|
||||
return $this->userService->fetchByIdentifier($user['uid']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Link existing local user to OIDC provider
|
||||
*/
|
||||
private function linkUser(array $user, array $oidcData, array $config): array
|
||||
{
|
||||
$managedFields = $this->getManagedFields($config);
|
||||
|
||||
// Link existing local user to OIDC provider
|
||||
$this->userService->updateUser($user['uid'], [
|
||||
'provider' => $this->identifier(),
|
||||
'provider_subject' => $oidcData['sub'],
|
||||
'provider_synced_at' => time(),
|
||||
'provider_managed_fields' => $managedFields,
|
||||
]);
|
||||
|
||||
// Then update with OIDC data
|
||||
return $this->updateUser(
|
||||
$this->userService->fetchByIdentifier($user['uid']),
|
||||
$oidcData,
|
||||
$config
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default managed fields for OIDC provider
|
||||
*/
|
||||
private function managedFields(): array
|
||||
{
|
||||
return [
|
||||
'name_given',
|
||||
'name_family',
|
||||
'avatar',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get managed fields from config or use defaults
|
||||
*/
|
||||
private function getManagedFields(array $config): array
|
||||
{
|
||||
// Allow configuration to override default managed fields
|
||||
return $config['managed_fields'] ?? $this->managedFields();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user