diff --git a/README.md b/README.md index 76e7af1..6165da6 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ This package helps you authenticate users on a Laravel API based on JWT tokens g 1. If everything is ok, find the user on database and authenticate it on my API. +1. Optionally, the user can be created / updated in the API users database. + 1. Return response # Install @@ -95,6 +97,8 @@ return [ 'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null), 'load_user_from_database' => env('KEYCLOAK_LOAD_USER_FROM_DATABASE', true), + + 'user_provider_custom_retrieve_method' => null, 'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'username'), @@ -124,6 +128,15 @@ If you do not have an `users` table you must disable this. It fetchs user from database and fill values into authenticated user object. If enabled, it will work together with `user_provider_credential` and `token_principal_attribute`. +✔️ **user_provider_custom_retrieve_method** + +*Default is `null`.* + +If you have an `users` table and want it to be updated (creating or updating users) based on the token, you can inform a custom method on a custom UserProvider, that will be called instead `retrieveByCredentials` and will receive the complete decoded token as parameter, not just the credentials (as default). +This will allow you to customize the way you want to interact with your database, before matching and delivering the authenticated user object, having all the information contained in the (valid) access token available. To read more about custom UserProviders, please check [Laravel's documentation about](https://laravel.com/docs/8.x/authentication#adding-custom-user-providers). + +If using this feature, obviously, values defined for `user_provider_credential` and `token_principal_attribute` will be ignored. + ✔️ **user_provider_credential** *Required. Default is `username`.* diff --git a/config/keycloak.php b/config/keycloak.php index fed0460..8b74bef 100644 --- a/config/keycloak.php +++ b/config/keycloak.php @@ -4,6 +4,8 @@ 'realm_public_key' => env('KEYCLOAK_REALM_PUBLIC_KEY', null), 'load_user_from_database' => env('KEYCLOAK_LOAD_USER_FROM_DATABASE', true), + + 'user_provider_custom_retrieve_method' => null, 'user_provider_credential' => env('KEYCLOAK_USER_PROVIDER_CREDENTIAL', 'username'), diff --git a/src/KeycloakGuard.php b/src/KeycloakGuard.php index 0562371..21ea639 100644 --- a/src/KeycloakGuard.php +++ b/src/KeycloakGuard.php @@ -124,7 +124,12 @@ public function validate(array $credentials = []) $this->validateResources(); if ($this->config['load_user_from_database']) { - $user = $this->provider->retrieveByCredentials($credentials); + $methodOnProvider = $this->config['user_provider_custom_retrieve_method'] ?? null; + if ($methodOnProvider) { + $user = $this->provider->{$methodOnProvider}($this->decodedToken, $credentials); + } else { + $user = $this->provider->retrieveByCredentials($credentials); + } if (!$user) { throw new UserNotFoundException("User not found. Credentials: " . json_encode($credentials)); diff --git a/tests/AuthenticateTest.php b/tests/AuthenticateTest.php index a785905..8692077 100644 --- a/tests/AuthenticateTest.php +++ b/tests/AuthenticateTest.php @@ -2,11 +2,13 @@ namespace KeycloakGuard\Tests; use Illuminate\Http\Request; +use Illuminate\Hashing\BcryptHasher; use Illuminate\Support\Facades\Auth; use KeycloakGuard\KeycloakGuard; use KeycloakGuard\Tests\Models\User; use KeycloakGuard\KeycloakGuardServiceProvider; use KeycloakGuard\Tests\Controllers\FooController; +use KeycloakGuard\Tests\Extensions\CustomUserProvider; use Illuminate\Routing\Router; use Illuminate\Events\Dispatcher; use KeycloakGuard\Exceptions\UserNotFoundException; @@ -181,4 +183,17 @@ public function it_prevent_cross_roles_resources() } + + /** @test */ + public function custom_user_retrieve_method() + { + config(['keycloak.user_provider_custom_retrieve_method' => 'custom_retrieve']); + + Auth::extend('keycloak', function ($app, $name, array $config) { + return new KeycloakGuard(new CustomUserProvider(new BcryptHasher(), User::class), $app->request); + }); + + $response = $this->withToken()->json('GET', '/foo/secret'); + $this->assertTrue(Auth::user()->customRetrieve); + } } diff --git a/tests/Extensions/CustomUserProvider.php b/tests/Extensions/CustomUserProvider.php new file mode 100644 index 0000000..9ee3c4a --- /dev/null +++ b/tests/Extensions/CustomUserProvider.php @@ -0,0 +1,17 @@ +customRetrieve = true; + + return $model; + } +}