Skip to content

Commit

Permalink
Add generic provider
Browse files Browse the repository at this point in the history
  • Loading branch information
c-jar committed Nov 2, 2024
1 parent b5f32c0 commit 5b07c06
Show file tree
Hide file tree
Showing 4 changed files with 208 additions and 2 deletions.
23 changes: 21 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,30 @@ MAIL_PASSWORD=
# MAIL_DKIM_SELECTOR = 'default'; # Match your DKIM DNS selector
# MAIL_DKIM_PASSPHRASE = ''; # Only if your key has a passphrase

# List of socialite providers separated by a space. Possible value : keycloak, generic
# List of socialite providers separated by a space. Possible value : keycloak, oidc
SOCIALITE_PROVIDERS=""

KEYCLAOK_DISPLAY_NAME="Keycloak"
KEYCLOAK_ALLOW_CREATE_USER=false
KEYCLOAK_ALLOW_UPDATE_USER=false
KEYCLOAK_DEFAULT_ROLE="auditee"
KEYCLOAK_ROLE_CLAIM="resource_access.deming.roles.0"
KEYCLOAK_ADDITIONAL_SCOPES="roles"
KEYCLOAK_ADDITIONAL_SCOPES="roles"

KEYCLOAK_CLIENT_ID=deming
KEYCLOAK_CLIENT_SECRET=secret
KEYCLOAK_REDIRECT_URI=${APP_URL}auth/callback/keycloak
KEYCLOAK_BASE_URL=https://keycloak.local
KEYCLOAK_REALM=main

OIDC_DISPLAY_NAME="Generic OIDC"
OIDC_ALLOW_CREATE_USER=false
OIDC_ALLOW_UPDATE_USER=false
OIDC_DEFAULT_ROLE="auditee"
OIDC_ROLE_CLAIM=""
OIDC_ADDITIONAL_SCOPES="deming_role"

OIDC_CLIENT_ID=deming
OIDC_CLIENT_SECRET=deming
OIDC_BASE_URL=http://auth.lan
OIDC_REDIRECT_URI=${APP_URL}auth/callback/oidc
19 changes: 19 additions & 0 deletions app/Providers/AppServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,5 +47,24 @@ public function boot()
$event->extendSocialite('keycloak', \SocialiteProviders\Keycloak\Provider::class);
});
}

if (in_array('oidc', Config::get('services.socialite_controller.providers'))){
$this->bootOIDCSocialite();
}
}

/**
* Register Generic OpenID Connect Provider.
*/
private function bootOIDCSocialite()
{
$socialite = $this->app->make('Laravel\Socialite\Contracts\Factory');
$socialite->extend(
'oidc',
function ($app) use ($socialite) {
$config = $app['config']['services.oidc'];
return $socialite->buildProvider(\App\Providers\Socialite\GenericSocialiteProvider::class, $config);
}
);
}
}
143 changes: 143 additions & 0 deletions app/Providers/Socialite/GenericSocialiteProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
<?php

namespace App\Providers\Socialite;

use Laravel\Socialite\Two\AbstractProvider;
use Laravel\Socialite\Two\ProviderInterface;
use GuzzleHttp\Exception\GuzzleException;
use Laravel\Socialite\Two\User;
use Log;

/**
* Generic OpenId Connect provider for Socialite.
*/
class GenericSocialiteProvider extends AbstractProvider implements ProviderInterface
{

/**
* Unique Provider Identifier.
*/
public const IDENTIFIER = 'OIDC';

/**
* Scope definitions.
*/
public const SCOPE_EMAIL = 'email';
public const SCOPE_OPENID = 'openid';
public const SCOPE_PROFILE = 'profile';

/**
* Adjust the available read / write attributes in cognito client app.
*
* {@inheritdoc}
*/
protected $scopes = [
self::SCOPE_OPENID,
self::SCOPE_PROFILE,
self::SCOPE_EMAIL,
];

/**
* {@inheritdoc}
*/
protected $scopeSeparator = ' ';

/**
* Return provider Url.
* @return string
*/
public function getOIDCUrl()
{
return rtrim(config('services.oidc.host'), '/').'/oauth2';
}

/**
* @param string $state
*
* @return string
*/
protected function getAuthUrl($state)
{
$base_url = $this->getOIDCUrl().'/authorize';
// If authorize endpoint set, use it instead
if (config('services.oidc.authorize_endpoint')){
$base_url = config('services.oidc.authorize_endpoint');
}
Log::debug('Buiild auth url from base : '.$base_url);
return $this->buildAuthUrlFromBase($base_url, $state);
}

/**
* @return string
*/
protected function getTokenUrl()
{
// If token endpoint set, use it instead
if (config('services.oidc.token_endpoint')){
return config('services.oidc.token_endpoint');
}
return $this->getOIDCUrl() . '/token';
}

/**
* @param string $token
*
* @throws GuzzleException
*
* @return array|mixed
*/
protected function getUserByToken($token)
{
$base_url = $this->getOIDCUrl() . '/userinfo';
// If userinfo endpoint set, use it instead
if (config('services.oidc.userinfo_endpoint')){
$base_url = config('services.oidc.userinfo_endpoint');
}

Log::debug('Get user info from '.$base_url);
$response = $this->getHttpClient()->post($base_url, [
'headers' => [
'cache-control' => 'no-cache',
'Authorization' => 'Bearer ' . $token,
'Content-Type' => 'application/x-www-form-urlencoded',
],
]);

return json_decode($response->getBody()->getContents(), true);
}

/**
* @return User
*/
protected function mapUserToObject(array $user)
{
Log::debug('Provider return user :'.var_export($user, true));
$socialite_user = [];
foreach(config('services.oidc.map_user_attr') as $socialite_attr => $provider_attr){
if ( ! array_key_exists($provider_attr, $user)){
Log::debug("'$provider_attr' not provided");
continue;
}
Log::debug("Map socialite_user['$socialite_attr']=".$user[$provider_attr]);
$socialite_user[$socialite_attr] = $user[$provider_attr];
}
return (new User())->setRaw($user)->map($socialite_user);
}

/**
* {@inheritdoc}
*/
public static function additionalConfigKeys(): array
{
return [
'client_id',
'host',
'logout_uri',
'redirect',
'authorize_endpoint',
'userinfo_endpoint',
'token_endpoint',
'map_user_attr',
];
}
}
25 changes: 25 additions & 0 deletions config/services.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@
'role_claim' => env('KEYCLOAK_ROLE_CLAIM', ''),
'additional_scopes' => explode(' ', env('KEYCLOAK_ADDITIONAL_SCOPES', "")),
],
'oidc' => [
'display_name' => env('OIDC_DISPLAY_NAME', 'Generic OIDC'),
'allow_create_user' => env("OIDC_ALLOW_CREATE_USER", false),
'allow_update_user' => env("OIDC_ALLOW_UPDATE_USER", false),
// Set to null if you want role to be set explicitily
'default_role' => env('OIDC_DEFAULT_ROLE', 'auditee'),
'role_claim' => env('OIDC_ROLE_CLAIM', 'role'),
'additional_scopes' => explode(' ', env('OIDC_ADDITIONAL_SCOPES', "")),
],
],

'keycloak' => [
Expand All @@ -52,4 +61,20 @@
'base_url' => env('KEYCLOAK_BASE_URL'), // Specify your keycloak server URL here
'realms' => env('KEYCLOAK_REALM'), // Specify your keycloak realm
],

'oidc' => [
'client_id' => env('OIDC_CLIENT_ID'),
'client_secret' => env('OIDC_CLIENT_SECRET'),
'host' => env('OIDC_BASE_URL'),
'redirect' => env('OIDC_REDIRECT_URI', rtrim(env('APP_URL'), '/').'/auth/callback/oidc'),
'authorize_endpoint' => env('OIDC_AUTHORIZE_ENDPOINT', null),
'token_endpoint' => env('OIDC_TOKEN_ENDPOINT', null),
'userinfo_endpoint' => env('OIDC_USERINFO_ENDPOINT', null),
'map_user_attr' => [
'id' => 'sub',
'name' => 'name',
'locale' => 'locale',
'email' => 'email'
],
],
];

0 comments on commit 5b07c06

Please sign in to comment.