1
0
mirror of https://github.com/Kovah/LinkAce.git synced 2025-04-21 07:22:20 +02:00

WIP Extend SSO implementation with proper naming, add basic tests, rename ENV key from OAUTH_ to SSO_ (#174 #194)

This commit is contained in:
Kovah 2024-09-18 23:58:36 +02:00
parent 96be6ca0f9
commit a9d92e8309
No known key found for this signature in database
GPG Key ID: AAAA031BA9830D7B
12 changed files with 292 additions and 159 deletions

View File

@ -24,10 +24,10 @@ class SocialiteController extends Controller
// If a user with the provided email address already exists, register the oauth login
if ($user = User::where('email', $authUser->getEmail())->first()) {
if ($user->sso_provider !== $provider) {
if ($user->sso_provider !== null && $user->sso_provider !== $provider) {
abort(403, trans('auth.sso_wrong_provider', [
'currentProvider' => $provider,
'userProvider' => $user->sso_provider,
'currentProvider' => trans('auth.sso.' . $provider),
'userProvider' => trans('auth.sso.' . $user->sso_provider),
]));
}
@ -67,8 +67,12 @@ class SocialiteController extends Controller
protected function authorizeOauthRequest(string $provider): void
{
if (config('auth.oauth.enabled') !== true || !in_array($provider, config('auth.oauth.providers'))) {
abort(403, 'Login unauthorized');
if (config('auth.sso.enabled') !== true || !in_array($provider, config('auth.sso.providers'))) {
abort(403, trans('auth.unauthorized'));
}
if (config('services.' . $provider . '.enabled') !== true) {
abort(403, trans('auth.sso_provider_disabled'));
}
}
}

View File

@ -41,7 +41,7 @@
"spatie/laravel-settings": "^3.2.3",
"symfony/http-client": "^6.0",
"symfony/mailgun-mailer": "^6.0",
"kovah/laravel-socialite-oidc": "^0.1"
"kovah/laravel-socialite-oidc": "^0.2.0"
},
"require-dev": {
"barryvdh/laravel-debugbar": "^3.2",
@ -50,7 +50,7 @@
"fakerphp/faker": "^1.12",
"laravel/tinker": "^2.2",
"mockery/mockery": "^1.3",
"nunomaduro/collision": "^6.1",
"nunomaduro/collision": "^v7.10",
"phpunit/phpunit": "^10.0",
"roave/security-advisories": "dev-latest",
"squizlabs/php_codesniffer": "^3.5"

72
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "fabba9ddaed0892766b3f3c46a30493e",
"content-hash": "82aaed0f0c23a2de44145e73eaa9431b",
"packages": [
{
"name": "aws/aws-crt-php",
@ -1928,16 +1928,16 @@
},
{
"name": "kovah/laravel-socialite-oidc",
"version": "v0.1",
"version": "v0.2.0",
"source": {
"type": "git",
"url": "https://github.com/Kovah/laravel-socialite-oidc.git",
"reference": "5bb9f107ec742505d15f09fafd519b4ce2512bc6"
"reference": "aca47884e996c964a490d91a3038cde5a4965cd4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Kovah/laravel-socialite-oidc/zipball/5bb9f107ec742505d15f09fafd519b4ce2512bc6",
"reference": "5bb9f107ec742505d15f09fafd519b4ce2512bc6",
"url": "https://api.github.com/repos/Kovah/laravel-socialite-oidc/zipball/aca47884e996c964a490d91a3038cde5a4965cd4",
"reference": "aca47884e996c964a490d91a3038cde5a4965cd4",
"shasum": ""
},
"require": {
@ -1983,7 +1983,7 @@
],
"support": {
"issues": "https://github.com/Kovah/laravel-socialite-oidc/issues",
"source": "https://github.com/Kovah/laravel-socialite-oidc/tree/v0.1"
"source": "https://github.com/Kovah/laravel-socialite-oidc/tree/v0.2.0"
},
"funding": [
{
@ -1991,7 +1991,7 @@
"type": "github"
}
],
"time": "2024-09-17T22:50:54+00:00"
"time": "2024-09-18T21:45:27+00:00"
},
{
"name": "laracasts/flash",
@ -10386,38 +10386,43 @@
},
{
"name": "nunomaduro/collision",
"version": "v6.4.0",
"version": "v7.10.0",
"source": {
"type": "git",
"url": "https://github.com/nunomaduro/collision.git",
"reference": "f05978827b9343cba381ca05b8c7deee346b6015"
"reference": "49ec67fa7b002712da8526678abd651c09f375b2"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/f05978827b9343cba381ca05b8c7deee346b6015",
"reference": "f05978827b9343cba381ca05b8c7deee346b6015",
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/49ec67fa7b002712da8526678abd651c09f375b2",
"reference": "49ec67fa7b002712da8526678abd651c09f375b2",
"shasum": ""
},
"require": {
"filp/whoops": "^2.14.5",
"php": "^8.0.0",
"symfony/console": "^6.0.2"
"filp/whoops": "^2.15.3",
"nunomaduro/termwind": "^1.15.1",
"php": "^8.1.0",
"symfony/console": "^6.3.4"
},
"conflict": {
"laravel/framework": ">=11.0.0"
},
"require-dev": {
"brianium/paratest": "^6.4.1",
"laravel/framework": "^9.26.1",
"laravel/pint": "^1.1.1",
"nunomaduro/larastan": "^1.0.3",
"nunomaduro/mock-final-classes": "^1.1.0",
"orchestra/testbench": "^7.7",
"phpunit/phpunit": "^9.5.23",
"spatie/ignition": "^1.4.1"
"brianium/paratest": "^7.3.0",
"laravel/framework": "^10.28.0",
"laravel/pint": "^1.13.3",
"laravel/sail": "^1.25.0",
"laravel/sanctum": "^3.3.1",
"laravel/tinker": "^2.8.2",
"nunomaduro/larastan": "^2.6.4",
"orchestra/testbench-core": "^8.13.0",
"pestphp/pest": "^2.23.2",
"phpunit/phpunit": "^10.4.1",
"sebastian/environment": "^6.0.1",
"spatie/laravel-ignition": "^2.3.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-develop": "6.x-dev"
},
"laravel": {
"providers": [
"NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
@ -10425,6 +10430,9 @@
}
},
"autoload": {
"files": [
"./src/Adapters/Phpunit/Autoload.php"
],
"psr-4": {
"NunoMaduro\\Collision\\": "src/"
}
@ -10470,7 +10478,7 @@
"type": "patreon"
}
],
"time": "2023-01-03T12:54:54+00:00"
"time": "2023-10-11T15:45:01+00:00"
},
{
"name": "phar-io/manifest",
@ -11097,12 +11105,12 @@
"source": {
"type": "git",
"url": "https://github.com/Roave/SecurityAdvisories.git",
"reference": "39a2e3360d55163461cbb72e5d7c872b6c8b11a4"
"reference": "a7b092bec53b91832e9c5c389ce966b50a8d6e8b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/39a2e3360d55163461cbb72e5d7c872b6c8b11a4",
"reference": "39a2e3360d55163461cbb72e5d7c872b6c8b11a4",
"url": "https://api.github.com/repos/Roave/SecurityAdvisories/zipball/a7b092bec53b91832e9c5c389ce966b50a8d6e8b",
"reference": "a7b092bec53b91832e9c5c389ce966b50a8d6e8b",
"shasum": ""
},
"conflict": {
@ -11215,7 +11223,7 @@
"cuyz/valinor": "<0.12",
"czim/file-handling": "<1.5|>=2,<2.3",
"czproject/git-php": "<4.0.3",
"damienharper/auditor-bundle": "<6",
"damienharper/auditor-bundle": "<5.2.6",
"dapphp/securimage": "<3.6.6",
"darylldoyle/safe-svg": "<1.9.10",
"datadog/dd-trace": ">=0.30,<0.30.2",
@ -11445,7 +11453,7 @@
"mantisbt/mantisbt": "<2.26.2",
"marcwillmann/turn": "<0.3.3",
"matyhtf/framework": "<3.0.6",
"mautic/core": "<4.4.12|>=5.0.0.0-alpha,<5.0.4",
"mautic/core": "<4.4.13|>=5.0.0.0-alpha,<5.1.1",
"mautic/core-lib": ">=1.1.3,<4.4.13|>=5.0.0.0-alpha,<5.1.1",
"mdanter/ecc": "<2",
"mediawiki/core": "<1.36.2",
@ -11914,7 +11922,7 @@
"type": "tidelift"
}
],
"time": "2024-09-18T18:05:47+00:00"
"time": "2024-09-18T21:04:55+00:00"
},
{
"name": "sebastian/cli-parser",

View File

@ -20,12 +20,12 @@ return [
/*
|--------------------------------------------------------------------------
| OAuth Settings
| Single Sign On Settings
|--------------------------------------------------------------------------
*/
'oauth' => [
'enabled' => env('OAUTH_ENABLED', false),
'sso' => [
'enabled' => env('SSO_ENABLED', false),
'regular_login_disabled' => env('REGULAR_LOGIN_DISABLED', false),
'providers' => [
'auth0',

View File

@ -34,104 +34,104 @@ return [
// OAuth and SSO
'auth0' => [
'enabled' => env('OAUTH_AUTH0_ENABLED', false),
'base_url' => env('OAUTH_AUTH0_BASE_URL'),
'client_id' => env('OAUTH_AUTH0_CLIENT_ID'),
'client_secret' => env('OAUTH_AUTH0_CLIENT_SECRET'),
'redirect' => '/auth/oauth/auth0/callback',
'enabled' => env('SSO_AUTH0_ENABLED', false),
'base_url' => env('SSO_AUTH0_BASE_URL'),
'client_id' => env('SSO_AUTH0_CLIENT_ID'),
'client_secret' => env('SSO_AUTH0_CLIENT_SECRET'),
'redirect' => '/auth/sso/auth0/callback',
],
'authentik' => [
'enabled' => env('OAUTH_AUTHENTIK_ENABLED', false),
'base_url' => env('OAUTH_AUTHENTIK_BASE_URL'),
'client_id' => env('OAUTH_AUTHENTIK_CLIENT_ID'),
'client_secret' => env('OAUTH_AUTHENTIK_CLIENT_SECRET'),
'redirect' => '/auth/oauth/authentik/callback',
'enabled' => env('SSO_AUTHENTIK_ENABLED', false),
'base_url' => env('SSO_AUTHENTIK_BASE_URL'),
'client_id' => env('SSO_AUTHENTIK_CLIENT_ID'),
'client_secret' => env('SSO_AUTHENTIK_CLIENT_SECRET'),
'redirect' => '/auth/sso/authentik/callback',
],
'azure' => [
'enabled' => env('OAUTH_AZURE_ENABLED', false),
'client_id' => env('OAUTH_AZURE_CLIENT_ID'),
'client_secret' => env('OAUTH_AZURE_CLIENT_SECRET'),
'tenant' => env('OAUTH_AZURE_TENANT_ID'),
'enabled' => env('SSO_AZURE_ENABLED', false),
'client_id' => env('SSO_AZURE_CLIENT_ID'),
'client_secret' => env('SSO_AZURE_CLIENT_SECRET'),
'tenant' => env('SSO_AZURE_TENANT_ID'),
'proxy' => env('PROXY'),
'redirect' => '/auth/oauth/azure/callback',
'redirect' => '/auth/sso/azure/callback',
],
'cognito' => [
'enabled' => env('OAUTH_COGNITO_ENABLED', false),
'host' => env('OAUTH_COGNITO_HOST'),
'client_id' => env('OAUTH_COGNITO_CLIENT_ID'),
'client_secret' => env('OAUTH_COGNITO_CLIENT_SECRET'),
'redirect' => env('OAUTH_COGNITO_CALLBACK_URL'),
'scope' => explode(',', env('OAUTH_COGNITO_LOGIN_SCOPE', '')),
'logout_uri' => env('OAUTH_COGNITO_SIGN_OUT_URL'),
'enabled' => env('SSO_COGNITO_ENABLED', false),
'host' => env('SSO_COGNITO_HOST'),
'client_id' => env('SSO_COGNITO_CLIENT_ID'),
'client_secret' => env('SSO_COGNITO_CLIENT_SECRET'),
'redirect' => env('SSO_COGNITO_CALLBACK_URL'),
'scope' => explode(',', env('SSO_COGNITO_LOGIN_SCOPE', '')),
'logout_uri' => env('SSO_COGNITO_SIGN_OUT_URL'),
],
'fusionauth' => [
'enabled' => env('OAUTH_FUSIONAUTH_ENABLED', false),
'base_url' => env('OAUTH_FUSIONAUTH_BASE_URL'),
'client_id' => env('OAUTH_FUSIONAUTH_CLIENT_ID'),
'client_secret' => env('OAUTH_FUSIONAUTH_CLIENT_SECRET'),
'redirect' => '/auth/oauth/fusionauth/callback',
'enabled' => env('SSO_FUSIONAUTH_ENABLED', false),
'base_url' => env('SSO_FUSIONAUTH_BASE_URL'),
'client_id' => env('SSO_FUSIONAUTH_CLIENT_ID'),
'client_secret' => env('SSO_FUSIONAUTH_CLIENT_SECRET'),
'redirect' => '/auth/sso/fusionauth/callback',
],
'google' => [
'enabled' => env('OAUTH_GOOGLE_ENABLED', false),
'client_id' => env('OAUTH_GOOGLE_CLIENT_ID'),
'client_secret' => env('OAUTH_GOOGLE_CLIENT_SECRET'),
'redirect' => '/auth/oauth/{provider}/callback',
'enabled' => env('SSO_GOOGLE_ENABLED', false),
'client_id' => env('SSO_GOOGLE_CLIENT_ID'),
'client_secret' => env('SSO_GOOGLE_CLIENT_SECRET'),
'redirect' => '/auth/sso/{provider}/callback',
],
'github' => [
'enabled' => env('OAUTH_GITHUB_ENABLED', false),
'client_id' => env('OAUTH_GITHUB_CLIENT_ID'),
'client_secret' => env('OAUTH_GITHUB_CLIENT_SECRET'),
'redirect' => '/auth/oauth/github/callback',
'enabled' => env('SSO_GITHUB_ENABLED', false),
'client_id' => env('SSO_GITHUB_CLIENT_ID'),
'client_secret' => env('SSO_GITHUB_CLIENT_SECRET'),
'redirect' => '/auth/sso/github/callback',
],
'gitlab' => [
'enabled' => env('OAUTH_GITLAB_ENABLED', false),
'host' => env('OAUTH_GITLAB_HOST'),
'client_id' => env('OAUTH_GITLAB_CLIENT_ID'),
'client_secret' => env('OAUTH_GITLAB_CLIENT_SECRET'),
'redirect' => '/auth/oauth/gitlab/callback',
'enabled' => env('SSO_GITLAB_ENABLED', false),
'host' => env('SSO_GITLAB_HOST'),
'client_id' => env('SSO_GITLAB_CLIENT_ID'),
'client_secret' => env('SSO_GITLAB_CLIENT_SECRET'),
'redirect' => '/auth/sso/gitlab/callback',
],
'keycloak' => [
'enabled' => env('OAUTH_KEYCLOAK_ENABLED', false),
'client_id' => env('OAUTH_KEYCLOAK_CLIENT_ID'),
'client_secret' => env('OAUTH_KEYCLOAK_CLIENT_SECRET'),
'realms' => env('OAUTH_KEYCLOAK_REALM'),
'redirect' => '/auth/oauth/keycloak/callback',
'enabled' => env('SSO_KEYCLOAK_ENABLED', false),
'client_id' => env('SSO_KEYCLOAK_CLIENT_ID'),
'client_secret' => env('SSO_KEYCLOAK_CLIENT_SECRET'),
'realms' => env('SSO_KEYCLOAK_REALM'),
'redirect' => '/auth/sso/keycloak/callback',
],
'oidc' => [
'enabled' => env('OAUTH_OIDC_ENABLED', false),
'base_url' => env('OAUTH_OIDC_BASE_URL'),
'client_id' => env('OAUTH_OIDC_CLIENT_ID'),
'client_secret' => env('OAUTH_OIDC_CLIENT_SECRET'),
'scopes' => env('OAUTH_OIDC_SCOPES'),
'redirect' => '/auth/oauth/oidc/callback',
'enabled' => env('SSO_OIDC_ENABLED', false),
'base_url' => env('SSO_OIDC_BASE_URL'),
'client_id' => env('SSO_OIDC_CLIENT_ID'),
'client_secret' => env('SSO_OIDC_CLIENT_SECRET'),
'scopes' => env('SSO_OIDC_SCOPES'),
'redirect' => '/auth/sso/oidc/callback',
],
'okta' => [
'enabled' => env('OAUTH_OKTA_ENABLED', false),
'base_url' => env('OAUTH_OKTA_BASE_URL'),
'client_id' => env('OAUTH_OKTA_CLIENT_ID'),
'client_secret' => env('OAUTH_OKTA_CLIENT_SECRET'),
'redirect' => '/auth/oauth/okta/callback',
'enabled' => env('SSO_OKTA_ENABLED', false),
'base_url' => env('SSO_OKTA_BASE_URL'),
'client_id' => env('SSO_OKTA_CLIENT_ID'),
'client_secret' => env('SSO_OKTA_CLIENT_SECRET'),
'redirect' => '/auth/sso/okta/callback',
],
'zitadel' => [
'enabled' => env('OAUTH_ZITADEL_ENABLED', false),
'client_id' => env('OAUTH_ZITADEL_CLIENT_ID'),
'client_secret' => env('OAUTH_ZITADEL_CLIENT_SECRET'),
'base_url' => env('OAUTH_ZITADEL_BASE_URL'),
'organization_id' => env('OAUTH_ZITADEL_ORGANIZATION_ID'),
'project_id' => env('OAUTH_ZITADEL_PROJECT_ID'),
'post_logout_redirect_uri' => env('OAUTH_ZITADEL_POST_LOGOUT_REDIRECT_URI', '/'),
'redirect' => '/auth/oauth/zitadel/callback',
'enabled' => env('SSO_ZITADEL_ENABLED', false),
'client_id' => env('SSO_ZITADEL_CLIENT_ID'),
'client_secret' => env('SSO_ZITADEL_CLIENT_SECRET'),
'base_url' => env('SSO_ZITADEL_BASE_URL'),
'organization_id' => env('SSO_ZITADEL_ORGANIZATION_ID'),
'project_id' => env('SSO_ZITADEL_PROJECT_ID'),
'post_logout_redirect_uri' => env('SSO_ZITADEL_POST_LOGOUT_REDIRECT_URI', '/'),
'redirect' => '/auth/sso/zitadel/callback',
],
];

View File

@ -18,6 +18,7 @@ return [
'failed' => 'These credentials do not match our records.',
'throttle' => 'Too many login attempts. Please try again in :seconds seconds.',
'unauthorized' => 'Login unauthorized. Please contact your administrator.',
'confirm_title' => 'Confirmation required',
'confirm' => 'Please confirm this action using your current password.',
@ -51,6 +52,7 @@ return [
'api_tokens.revoke_confirm' => 'Do you really want to revoke this token? This step cannot be undone and the token cannot be recovered.',
'api_tokens.revoke_successful' => 'The token was revoked successfully.',
'sso_provider_disabled' => 'The selected SSO provider is not available. Please choose another one.',
'sso_wrong_provider' => 'Unable to login with :currentProvider. Please use :userProvider to login, or contact your administrator for help.',
'sso' => [

View File

@ -8,10 +8,10 @@
<div class="alert alert-info small">@lang('linkace.demo_login_hint')</div>
@endif
@include('partials.alerts')
@if(config('auth.oauth.regular_login_disabled') !== true)
@if(config('auth.sso.regular_login_disabled') !== true)
@include('auth.login-form')
@endif
@if(config('auth.oauth.enabled') === true)
@if(config('auth.sso.enabled') === true)
@include('auth.oauth')
@endif
</div>

View File

@ -1,10 +1,10 @@
<div class="card {{ config('auth.oauth.regular_login_disabled') ? '' : 'mt-4' }}">
<div class="card {{ config('auth.sso.regular_login_disabled') ? '' : 'mt-4' }}">
<div class="card-body">
<h2 class="h6">@lang('linkace.login_with')</h2>
<div class="d-flex flex-wrap gap-2">
@foreach(config('auth.oauth.providers') as $provider)
@foreach(config('auth.sso.providers') as $provider)
@if(config('services.'.$provider.'.enabled') === true)
<a href="{{ route('auth.oauth.redirect', ['provider' => $provider]) }}" class="btn btn-outline-primary">
<a href="{{ route('auth.sso.redirect', ['provider' => $provider]) }}" class="btn btn-outline-primary">
<x-dynamic-component :component="'icon.brand.'.$provider" class="me-1"/> @lang('auth.sso.'.$provider)
</a>
@endif

View File

@ -71,8 +71,8 @@ Route::get('auth/accept-invite', [RegistrationController::class, 'acceptInvitati
Route::post('auth/register', [RegistrationController::class, 'register'])
->name('auth.register');
Route::get('/auth/oauth/{provider}/redirect', [SocialiteController::class, 'redirect'])->name('auth.oauth.redirect');
Route::any('/auth/oauth/{provider}/callback', [SocialiteController::class, 'callback'])->name('auth.oauth.callback');
Route::get('/auth/sso/{provider}/redirect', [SocialiteController::class, 'redirect'])->name('auth.sso.redirect');
Route::any('/auth/sso/{provider}/callback', [SocialiteController::class, 'callback'])->name('auth.sso.callback');
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::get('links/feed', [FeedController::class, 'links'])->name('links.feed');

View File

@ -1,42 +0,0 @@
<?php
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
use PHPUnit\Runner\AfterLastTestHook;
use PHPUnit\Runner\BeforeFirstTestHook;
class Bootstrap implements BeforeFirstTestHook, AfterLastTestHook
{
/*
|--------------------------------------------------------------------------
| Bootstrap The Test Environment
|--------------------------------------------------------------------------
|
| You may specify console commands that execute once before your test is
| run. You are free to add your own additional commands or logic into
| this file as needed in order to help your test suite run quicker.
|
*/
use CreatesApplication;
public function executeBeforeFirstTest(): void
{
$console = $this->createApplication()->make(Kernel::class);
$commands = [
'config:cache',
'event:cache',
];
foreach ($commands as $command) {
$console->call($command);
}
}
public function executeAfterLastTest(): void
{
array_map('unlink', glob('bootstrap/cache/*.phpunit.php'));
}
}

View File

@ -0,0 +1,162 @@
<?php
namespace Tests\Controller;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Socialite\Facades\Socialite;
use Laravel\Socialite\SocialiteServiceProvider;
use Laravel\Socialite\Two\User;
use Tests\TestCase;
class SocialiteControllerTest extends TestCase
{
use RefreshDatabase;
public function testRedirect(): void
{
$this->app->register(SocialiteServiceProvider::class);
Socialite::shouldReceive('driver->redirect')->once()->andReturn(redirect()->to('https://sso-provider.com/auth'));
// SSO disabled
config()->set('auth.sso.enabled', false);
$this->get('auth/sso/auth0/redirect')->assertStatus(403)->assertSee('Login unauthorized');
// SSO enabled but wrong provider
config()->set('auth.sso.enabled', true);
$this->get('auth/sso/hello/redirect')->assertStatus(403)->assertSee('Login unauthorized');
// SSO enabled but disabled provider
$this->get('auth/sso/auth0/redirect')->assertStatus(403)->assertSee('The selected SSO provider is not available.');
// SSO and corresponding driver enabled
config()->set('services.auth0.enabled', true);
$this->get('auth/sso/auth0/redirect')->assertRedirect('https://sso-provider.com/auth');
}
public function testRegularSsoLogin(): void
{
$ssoUser = new User();
$ssoUser->setToken('XF3hkrEeyYkLnTf1fKX');
$ssoUser->map([
'id' => 'sso-user-sub-123',
'email' => 'sso-user@linkace.org',
'name' => 'SSO User',
'nickname' => 'SSOUser',
'given_name' => 'SSO',
'family_name' => 'User',
]);
$this->app->register(SocialiteServiceProvider::class);
Socialite::shouldReceive('driver->user')->once()->andReturn($ssoUser);
config()->set('auth.sso.enabled', true);
config()->set('services.auth0.enabled', true);
$this->get('auth/sso/auth0/callback')->assertRedirect('dashboard');
$this->assertDatabaseHas('users', [
'name' => 'SSOUser',
'email' => 'sso-user@linkace.org',
'sso_id' => 'sso-user-sub-123',
'sso_token' => 'XF3hkrEeyYkLnTf1fKX',
]);
}
public function testLoginWithExistingSsoUser(): void
{
$ssoUser = new User();
$ssoUser->setToken('XF3hkrEeyYkLnTf1fKX');
$ssoUser->map([
'id' => 'sso-user-sub-123',
'email' => 'sso-user@linkace.org',
'name' => 'SSO User',
'nickname' => 'SSOUser',
'given_name' => 'SSO',
'family_name' => 'User',
]);
\App\Models\User::factory()->create([
'name' => 'MrPurpleHat',
'email' => 'sso-user@linkace.org',
'sso_id' => 'sso-user-sub-123',
]);
$this->app->register(SocialiteServiceProvider::class);
Socialite::shouldReceive('driver->user')->once()->andReturn($ssoUser);
config()->set('auth.sso.enabled', true);
config()->set('services.auth0.enabled', true);
$this->get('auth/sso/auth0/callback')->assertRedirect('dashboard');
$this->assertDatabaseHas('users', [
'name' => 'SSOUser',
'email' => 'sso-user@linkace.org',
'sso_id' => 'sso-user-sub-123',
'sso_token' => 'XF3hkrEeyYkLnTf1fKX',
]);
}
public function testLoginWithExistingSsoUserWrongProvider(): void
{
$ssoUser = new User();
$ssoUser->setToken('XF3hkrEeyYkLnTf1fKX');
$ssoUser->map([
'id' => 'sso-user-sub-123',
'email' => 'sso-user@linkace.org',
'name' => 'SSO User',
'nickname' => 'SSOUser',
'given_name' => 'SSO',
'family_name' => 'User',
]);
\App\Models\User::factory()->create([
'name' => 'MrPurpleHat',
'email' => 'sso-user@linkace.org',
'sso_id' => 'oidc123456',
'sso_provider' => 'oidc',
]);
$this->app->register(SocialiteServiceProvider::class);
Socialite::shouldReceive('driver->user')->once()->andReturn($ssoUser);
config()->set('auth.sso.enabled', true);
config()->set('services.auth0.enabled', true);
$this->get('auth/sso/auth0/callback')->assertStatus(403)->assertSee('Unable to login with Auth0. Please use OIDC to login');
}
public function testLoginWithExistingEmail(): void
{
$ssoUser = new User();
$ssoUser->setToken('XF3hkrEeyYkLnTf1fKX');
$ssoUser->map([
'id' => 'sso-user-sub-123',
'email' => 'sso-user@linkace.org',
'name' => 'SSO User',
'nickname' => 'SSOUser',
'given_name' => 'SSO',
'family_name' => 'User',
]);
\App\Models\User::factory()->create([
'name' => 'MrPurpleHat',
'email' => 'sso-user@linkace.org',
]);
$this->app->register(SocialiteServiceProvider::class);
Socialite::shouldReceive('driver->user')->once()->andReturn($ssoUser);
config()->set('auth.sso.enabled', true);
config()->set('services.auth0.enabled', true);
$this->get('auth/sso/auth0/callback')->assertRedirect('dashboard');
$this->assertDatabaseHas('users', [
'name' => 'SSOUser',
'email' => 'sso-user@linkace.org',
'sso_id' => 'sso-user-sub-123',
'sso_token' => 'XF3hkrEeyYkLnTf1fKX',
]);
}
}

View File

@ -3,17 +3,16 @@
namespace Tests;
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
trait CreatesApplication
{
/**
* Creates the application.
*
* @return \Illuminate\Foundation\Application
*/
public function createApplication(): \Illuminate\Foundation\Application
public function createApplication(): Application
{
$app = require __DIR__ . '/../bootstrap/app.php';
$app = require __DIR__.'/../bootstrap/app.php';
$app->make(Kernel::class)->bootstrap();