kopia lustrzana https://github.com/pixelfed/pixelfed
Generic OIDC Support
* Everything should be configurable by env variables * Basic request testspull/5608/head
rodzic
89191b8e0b
commit
441c8e0d4c
|
|
@ -14,6 +14,7 @@ use Illuminate\Http\Request;
|
|||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Rules\PixelfedUsername;
|
||||
use InvalidArgumentException;
|
||||
use Purify;
|
||||
|
||||
|
|
@ -359,37 +360,7 @@ class RemoteAuthController extends Controller
|
|||
'required',
|
||||
'min:2',
|
||||
'max:30',
|
||||
function ($attribute, $value, $fail) {
|
||||
$dash = substr_count($value, '-');
|
||||
$underscore = substr_count($value, '_');
|
||||
$period = substr_count($value, '.');
|
||||
|
||||
if (ends_with($value, ['.php', '.js', '.css'])) {
|
||||
return $fail('Username is invalid.');
|
||||
}
|
||||
|
||||
if (($dash + $underscore + $period) > 1) {
|
||||
return $fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
|
||||
}
|
||||
|
||||
if (! ctype_alnum($value[0])) {
|
||||
return $fail('Username is invalid. Must start with a letter or number.');
|
||||
}
|
||||
|
||||
if (! ctype_alnum($value[strlen($value) - 1])) {
|
||||
return $fail('Username is invalid. Must end with a letter or number.');
|
||||
}
|
||||
|
||||
$val = str_replace(['_', '.', '-'], '', $value);
|
||||
if (! ctype_alnum($val)) {
|
||||
return $fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
|
||||
}
|
||||
|
||||
$restricted = RestrictedNames::get();
|
||||
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
|
||||
return $fail('Username cannot be used.');
|
||||
}
|
||||
},
|
||||
new PixelfedUsername(),
|
||||
],
|
||||
]);
|
||||
$username = strtolower($request->input('username'));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\UserOidcMapping;
|
||||
use Purify;
|
||||
use App\Services\EmailService;
|
||||
use App\Services\UserOidcService;
|
||||
use App\User;
|
||||
use Illuminate\Auth\Events\Registered;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Rules\EmailNotBanned;
|
||||
use App\Rules\PixelfedUsername;
|
||||
|
||||
class RemoteOidcController extends Controller
|
||||
{
|
||||
protected $fractal;
|
||||
|
||||
public function start(UserOidcService $provider, Request $request)
|
||||
{
|
||||
abort_unless(config('remote-auth.oidc.enabled'), 404);
|
||||
if ($request->user()) {
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
$url = $provider->getAuthorizationUrl([
|
||||
'scope' => $provider->getDefaultScopes(),
|
||||
]);
|
||||
|
||||
$request->session()->put('oauth2state', $provider->getState());
|
||||
|
||||
return redirect($url);
|
||||
}
|
||||
|
||||
public function handleCallback(UserOidcService $provider, Request $request)
|
||||
{
|
||||
abort_unless(config('remote-auth.oidc.enabled'), 404);
|
||||
|
||||
if ($request->user()) {
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
abort_unless($request->input("state"), 400);
|
||||
abort_unless($request->input("code"), 400);
|
||||
|
||||
abort_unless($request->input("state") == $request->session()->pull('oauth2state'), 400, "invalid state");
|
||||
|
||||
$accessToken = $provider->getAccessToken('authorization_code', [
|
||||
'code' => $request->get('code')
|
||||
]);
|
||||
|
||||
$userInfo = $provider->getResourceOwner($accessToken);
|
||||
$userInfoId = $userInfo->getId();
|
||||
$userInfoData = $userInfo->toArray();
|
||||
|
||||
$mappedUser = UserOidcMapping::where('oidc_id', $userInfoId)->first();
|
||||
if ($mappedUser) {
|
||||
$this->guarder()->login($mappedUser->user);
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
abort_if(EmailService::isBanned($userInfoData["email"]), 400, 'Banned email.');
|
||||
|
||||
$user = $this->createUser([
|
||||
'username' => $userInfoData[config('remote-auth.oidc.field_username')],
|
||||
'name' => $userInfoData["name"] ?? $userInfoData["display_name"] ?? $userInfoData[config('remote-auth.oidc.field_username')],
|
||||
'email' => $userInfoData["email"],
|
||||
]);
|
||||
|
||||
UserOidcMapping::create([
|
||||
'user_id' => $user->id,
|
||||
'oidc_id' => $userInfoId,
|
||||
]);
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
|
||||
protected function createUser($data)
|
||||
{
|
||||
$this->validate(new Request($data), [
|
||||
'email' => [
|
||||
'required',
|
||||
'string',
|
||||
'email:strict,filter_unicode,dns,spoof',
|
||||
'max:255',
|
||||
'unique:users',
|
||||
new EmailNotBanned(),
|
||||
],
|
||||
'username' => [
|
||||
'required',
|
||||
'min:2',
|
||||
'max:30',
|
||||
'unique:users,username',
|
||||
new PixelfedUsername(),
|
||||
],
|
||||
'name' => 'nullable|max:30',
|
||||
]);
|
||||
|
||||
event(new Registered($user = User::create([
|
||||
'name' => Purify::clean($data['name']),
|
||||
'username' => $data['username'],
|
||||
'email' => $data['email'],
|
||||
'password' => Hash::make(Str::password()),
|
||||
'email_verified_at' => now(),
|
||||
'app_register_ip' => request()->ip(),
|
||||
'register_source' => 'oidc',
|
||||
])));
|
||||
|
||||
$this->guarder()->login($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
protected function guarder()
|
||||
{
|
||||
return Auth::guard();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\User;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
|
||||
class UserOidcMapping extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
|
||||
public $timestamps = true;
|
||||
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'oidc_id',
|
||||
];
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ use App\Observers\UserFilterObserver;
|
|||
use App\Observers\UserObserver;
|
||||
use App\Profile;
|
||||
use App\Services\AccountService;
|
||||
use App\Services\UserOidcService;
|
||||
use App\Status;
|
||||
use App\StatusHashtag;
|
||||
use App\User;
|
||||
|
|
@ -112,6 +113,8 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
public function register()
|
||||
{
|
||||
//
|
||||
$this->app->bind(UserOidcService::class, function() {
|
||||
return UserOidcService::build();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use App\Services\EmailService;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class EmailNotBanned implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
* @return void
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
if (EmailService::isBanned($value)) {
|
||||
$fail('Email is invalid.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace App\Rules;
|
||||
|
||||
use Closure;
|
||||
use App\Util\Lexer\RestrictedNames;
|
||||
use Illuminate\Contracts\Validation\ValidationRule;
|
||||
|
||||
class PixelfedUsername implements ValidationRule
|
||||
{
|
||||
/**
|
||||
* Run the validation rule.
|
||||
*
|
||||
* @param string $attribute
|
||||
* @param mixed $value
|
||||
* @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail
|
||||
* @return void
|
||||
*/
|
||||
public function validate(string $attribute, mixed $value, Closure $fail): void
|
||||
{
|
||||
$dash = substr_count($value, '-');
|
||||
$underscore = substr_count($value, '_');
|
||||
$period = substr_count($value, '.');
|
||||
|
||||
if (ends_with($value, ['.php', '.js', '.css'])) {
|
||||
$fail('Username is invalid.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (($dash + $underscore + $period) > 1) {
|
||||
$fail('Username is invalid. Can only contain one dash (-), period (.) or underscore (_).');
|
||||
return;
|
||||
}
|
||||
|
||||
if (! ctype_alnum($value[0])) {
|
||||
$fail('Username is invalid. Must start with a letter or number.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (! ctype_alnum($value[strlen($value) - 1])) {
|
||||
$fail('Username is invalid. Must end with a letter or number.');
|
||||
return;
|
||||
}
|
||||
|
||||
$val = str_replace(['_', '.', '-'], '', $value);
|
||||
if (! ctype_alnum($val)) {
|
||||
$fail('Username is invalid. Username must be alpha-numeric and may contain dashes (-), periods (.) and underscores (_).');
|
||||
return;
|
||||
}
|
||||
|
||||
$restricted = RestrictedNames::get();
|
||||
if (in_array(strtolower($value), array_map('strtolower', $restricted))) {
|
||||
$fail('Username cannot be used.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use League\OAuth2\Client\Provider\GenericProvider;
|
||||
|
||||
class UserOidcService extends GenericProvider {
|
||||
public static function build()
|
||||
{
|
||||
return new UserOidcService([
|
||||
'clientId' => config('remote-auth.oidc.clientId'),
|
||||
'clientSecret' => config('remote-auth.oidc.clientSecret'),
|
||||
'redirectUri' => url('auth/oidc/callback'),
|
||||
'urlAuthorize' => config('remote-auth.oidc.authorizeURL'),
|
||||
'urlAccessToken' => config('remote-auth.oidc.tokenURL'),
|
||||
'urlResourceOwnerDetails' => config('remote-auth.oidc.profileURL'),
|
||||
'scopes' => config('remote-auth.oidc.scopes'),
|
||||
'accessTokenResourceOwnerId' => config('remote-auth.oidc.field_id'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
"laravel/ui": "^4.2",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"league/iso3166": "^2.1|^4.0",
|
||||
"league/oauth2-client": "^2.8",
|
||||
"league/uri": "^7.4",
|
||||
"pbmedia/laravel-ffmpeg": "^8.0",
|
||||
"phpseclib/phpseclib": "~2.0",
|
||||
|
|
|
|||
|
|
@ -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": "a011d3030ab0153865ef4cd6a7b615a3",
|
||||
"content-hash": "ac363dfc5037ce5d118b7b4a8e75bffe",
|
||||
"packages": [
|
||||
{
|
||||
"name": "aws/aws-crt-php",
|
||||
|
|
@ -3872,6 +3872,71 @@
|
|||
],
|
||||
"time": "2024-09-21T08:32:55+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-client",
|
||||
"version": "2.8.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/oauth2-client.git",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/oauth2-client/zipball/9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"reference": "9df2924ca644736c835fc60466a3a60390d334f9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"guzzlehttp/guzzle": "^6.5.8 || ^7.4.5",
|
||||
"php": "^7.1 || >=8.0.0 <8.5.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.3.5",
|
||||
"php-parallel-lint/php-parallel-lint": "^1.4",
|
||||
"phpunit/phpunit": "^7 || ^8 || ^9 || ^10 || ^11",
|
||||
"squizlabs/php_codesniffer": "^3.11"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\OAuth2\\Client\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Alex Bilbie",
|
||||
"email": "hello@alexbilbie.com",
|
||||
"homepage": "http://www.alexbilbie.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"homepage": "https://github.com/shadowhand",
|
||||
"role": "Contributor"
|
||||
}
|
||||
],
|
||||
"description": "OAuth 2.0 Client Library",
|
||||
"keywords": [
|
||||
"Authentication",
|
||||
"SSO",
|
||||
"authorization",
|
||||
"identity",
|
||||
"idp",
|
||||
"oauth",
|
||||
"oauth2",
|
||||
"single sign on"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/oauth2-client/issues",
|
||||
"source": "https://github.com/thephpleague/oauth2-client/tree/2.8.1"
|
||||
},
|
||||
"time": "2025-02-26T04:37:30+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/oauth2-server",
|
||||
"version": "8.5.5",
|
||||
|
|
@ -12680,7 +12745,7 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {},
|
||||
"stability-flags": [],
|
||||
"prefer-stable": true,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
|
@ -12693,6 +12758,6 @@
|
|||
"ext-mbstring": "*",
|
||||
"ext-openssl": "*"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,4 +54,16 @@ return [
|
|||
'limit' => env('PF_LOGIN_WITH_MASTODON_MAX_USES_LIMIT', 3)
|
||||
]
|
||||
],
|
||||
'oidc' => [
|
||||
'enabled' => env('PF_OIDC_ENABLED', false),
|
||||
'clientId' => env('PF_OIDC_CLIENT_ID', false),
|
||||
'clientSecret' => env('PF_OIDC_CLIENT_SECRET', false),
|
||||
'scopes' => env('PF_OIDC_SCOPES', 'openid profile email'),
|
||||
'authorizeURL' => env('PF_OIDC_AUTHORIZE_URL', ''),
|
||||
'tokenURL' => env('PF_OIDC_TOKEN_URL', ''),
|
||||
'profileURL' => env('PF_OIDC_PROFILE_URL', ''),
|
||||
'logoutURL' => env('PF_OIDC_LOGOUT_URL', ''),
|
||||
'field_username' => env('PF_OIDC_USERNAME_FIELD', "preferred_username"),
|
||||
'field_id' => env('PF_OIDC_FIELD_ID', 'sub'),
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('user_oidc_mappings', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->unsignedInteger('user_id')->index();
|
||||
$table->string('oidc_id')->unique()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('user_oidc_mappings');
|
||||
}
|
||||
};
|
||||
|
|
@ -111,6 +111,17 @@
|
|||
</form>
|
||||
@endif
|
||||
|
||||
@if( config('remote-auth.oidc.enabled') )
|
||||
<hr>
|
||||
<div class="form-group row mb-0">
|
||||
<div class="col-md-12">
|
||||
<a href="/auth/oidc/start" class="btn btn-primary btn-sm btn-block rounded-pill font-weight-bold" style="background: linear-gradient(#6364FF, #563ACC);">
|
||||
Sign-in with OIDC
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if((bool) config_cache('pixelfed.open_registration') || (bool) config_cache('instance.curated_registration.enabled'))
|
||||
<hr>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,10 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
|
|||
Route::get('authorize_interaction', 'AuthorizeInteractionController@get');
|
||||
|
||||
Auth::routes();
|
||||
|
||||
Route::get('auth/oidc/start', 'RemoteOidcController@start');
|
||||
Route::get('auth/oidc/callback', 'RemoteOidcController@handleCallback');
|
||||
|
||||
Route::get('auth/raw/mastodon/start', 'RemoteAuthController@startRedirect');
|
||||
Route::post('auth/raw/mastodon/config', 'RemoteAuthController@getConfig');
|
||||
Route::post('auth/raw/mastodon/domains', 'RemoteAuthController@getAuthDomains');
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Feature;
|
||||
|
||||
use App\Models\UserOidcMapping;
|
||||
use App\Services\UserOidcService;
|
||||
use App\User;
|
||||
use Auth;
|
||||
use Faker\Factory as Faker;
|
||||
use League\OAuth2\Client\Provider\GenericResourceOwner;
|
||||
use League\OAuth2\Client\Token\AccessToken;
|
||||
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
|
||||
use Mockery\MockInterface;
|
||||
use Tests\TestCase;
|
||||
|
||||
class RemoteOidcTest extends TestCase
|
||||
{
|
||||
use MockeryPHPUnitIntegration;
|
||||
|
||||
public function test_view_oidc_start()
|
||||
{
|
||||
config([
|
||||
'remote-auth.oidc.enabled'=> true,
|
||||
'remote-auth.oidc.clientId' => 'fake',
|
||||
'remote-auth.oidc.clientSecret' => 'fakeSecret',
|
||||
'remote-auth.oidc.authorizeURL' => 'http://fakeserver.oidc/authorizeURL',
|
||||
'remote-auth.oidc.tokenURL' => 'http://fakeserver.oidc/tokenURL',
|
||||
'remote-auth.oidc.profileURL' => 'http://fakeserver.oidc/profile',
|
||||
]);
|
||||
$response = $this->withoutExceptionHandling()->get('auth/oidc/start');
|
||||
|
||||
$state = session()->get('oauth2state');
|
||||
$callbackUrl = urlencode(url('auth/oidc/callback'));
|
||||
|
||||
$response->assertRedirect("http://fakeserver.oidc/authorizeURL?scope=openid%20profile%20email&state={$state}&response_type=code&approval_prompt=auto&redirect_uri={$callbackUrl}&client_id=fake");
|
||||
}
|
||||
|
||||
public function test_view_oidc_callback_new_user()
|
||||
{
|
||||
$originalUserCount = User::count();
|
||||
$this->assertDatabaseCount('users', $originalUserCount);
|
||||
|
||||
config(['remote-auth.oidc.enabled' => true]);
|
||||
|
||||
$oauthData = array(
|
||||
"sub" => str_random(10),
|
||||
"preferred_username" => fake()->unique()->userName,
|
||||
"email" => fake()->unique()->freeEmail,
|
||||
);
|
||||
|
||||
$this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
|
||||
$mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(["access_token" => "token" ]));
|
||||
$mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
|
||||
return $mock;
|
||||
});
|
||||
|
||||
$response = $this->withoutExceptionHandling()->withSession([
|
||||
'oauth2state' => 'abc123',
|
||||
])->get('auth/oidc/callback?state=abc123&code=1');
|
||||
|
||||
$response->assertRedirect('/');
|
||||
|
||||
$mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
|
||||
$this->assertNotNull($mappedUser, "mapping is found");
|
||||
$user = $mappedUser->user;
|
||||
$this->assertEquals($user->username, $oauthData['preferred_username']);
|
||||
$this->assertEquals($user->email, $oauthData['email']);
|
||||
$this->assertEquals(Auth::guard()->user()->id, $user->id);
|
||||
|
||||
$this->assertDatabaseCount('users', $originalUserCount+1);
|
||||
}
|
||||
|
||||
public function test_view_oidc_callback_existing_user()
|
||||
{
|
||||
$user = User::create([
|
||||
'name' => fake()->name,
|
||||
'username' => fake()->unique()->username,
|
||||
'email' => fake()->unique()->freeEmail,
|
||||
]);
|
||||
$originalUserCount = User::count();
|
||||
$this->assertDatabaseCount('users', $originalUserCount);
|
||||
|
||||
config(['remote-auth.oidc.enabled' => true]);
|
||||
|
||||
$oauthData = array(
|
||||
"sub" => str_random(10),
|
||||
"preferred_username" => $user->username,
|
||||
"email" => $user->email,
|
||||
);
|
||||
|
||||
UserOidcMapping::create([
|
||||
'oidc_id' => $oauthData['sub'],
|
||||
'user_id' => $user->id,
|
||||
]);
|
||||
|
||||
$this->partialMock(UserOidcService::class, function (MockInterface $mock) use ($oauthData) {
|
||||
$mock->shouldReceive('getAccessToken')->once()->andReturn(new AccessToken(["access_token" => "token" ]));
|
||||
$mock->shouldReceive('getResourceOwner')->once()->andReturn(new GenericResourceOwner($oauthData, 'sub'));
|
||||
return $mock;
|
||||
});
|
||||
|
||||
$response = $this->withoutExceptionHandling()->withSession([
|
||||
'oauth2state' => 'abc123',
|
||||
])->get('auth/oidc/callback?state=abc123&code=1');
|
||||
|
||||
$response->assertRedirect('/');
|
||||
|
||||
$mappedUser = UserOidcMapping::where('oidc_id', $oauthData['sub'])->first();
|
||||
$this->assertNotNull($mappedUser, "mapping is found");
|
||||
$user = $mappedUser->user;
|
||||
$this->assertEquals($user->username, $oauthData['preferred_username']);
|
||||
$this->assertEquals($user->email, $oauthData['email']);
|
||||
$this->assertEquals(Auth::guard()->user()->id, $user->id);
|
||||
|
||||
$this->assertDatabaseCount('users', $originalUserCount);
|
||||
}
|
||||
}
|
||||
Ładowanie…
Reference in New Issue