Compare commits

..

No commits in common. "6840624be2ed8850b4a34b611965e617b1496361" and "e24e019609bcae6c43ab828bfce0cf04ab8c0dc7" have entirely different histories.

9 changed files with 97 additions and 193 deletions

View File

@ -22,7 +22,6 @@
"require": {
"php": "^8.1",
"filament/filament": "^3.0",
"league/oauth2-client": "*",
"spatie/laravel-package-tools": "^1.15.0"
},
"require-dev": {
@ -71,4 +70,4 @@
},
"minimum-stability": "dev",
"prefer-stable": true
}
}

View File

@ -1,16 +0,0 @@
<?php
// config for AlexanderGabriel/FilamentOauth2
return [
'clientId' => env("OAUTH2_CLIENT_ID"),
'clientSecret' => env("OAUTH2_CLIENT_SECRET"),
'baseUrl' => env("OAUTH2_BASE_URL"), // https://DOMAIN/realms/REALM/protocol/openid-connect
'urlAuthorize' => env("OAUTH2_URL_AUTHORIZE", env("OAUTH2_BASE_URL")."/auth"),
'urlAccessToken' => env("OAUTH2_URL_ACCESS_TOKEN", env("OAUTH2_BASE_URL")."/token"),
'urlResourceOwnerDetails' => env("OAUTH2_URL_RSOURCE_OWNER_DETAILS", env("OAUTH2_BASE_URL")."/userinfo"),
'scopes' => env("OAUTH2_SCOPES", "profile email openid"),
'updateRoles' => env("OAUTH2_UPDATE_ROLES", false)
];

6
config/oauth2.php Normal file
View File

@ -0,0 +1,6 @@
<?php
// config for AlexanderGabriel/FilamentOauth2
return [
];

View File

@ -0,0 +1,19 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('filament_oauth2_table', function (Blueprint $table) {
$table->id();
// add fields
$table->timestamps();
});
}
};

View File

@ -1,26 +0,0 @@
<?php
// https://github.com/filamentphp/actions/blob/4.x/routes/web.php
use AlexanderGabriel\FilamentOauth2\Http\Controllers\Oauth2Controller;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Route;
$panel = Filament::getCurrentPanel();
Route::prefix($panel->getPath())
->middleware($panel->getMiddleware())
->group(function () {
Route::get('login', [Oauth2Controller::class, 'redirectToOauth2Server'])->name('redirectToOauth2Server');
Route::name('filament-oauth2.')
->prefix('filament-oauth2')
->group(function () {
Route::get('redirectToOauth2Server', [Oauth2Controller::class, 'redirectToOauth2Server'])
->name('redirectToOauth2Server');
Route::get('handleCallback', [Oauth2Controller::class, 'handleCallback'])
->name('handleCallback');
});
});

View File

@ -2,12 +2,4 @@
namespace AlexanderGabriel\FilamentOauth2;
use AlexanderGabriel\FilamentOauth2\Http\Controllers\Oauth2Controller;
class FilamentOauth2 {
public static function getLoginRouteAction(): array
{
return [Oauth2Controller::class, 'redirectToOauth2Server'];
}
}
class FilamentOauth2 {}

View File

@ -2,7 +2,6 @@
namespace AlexanderGabriel\FilamentOauth2;
use AlexanderGabriel\FilamentOauth2\Facades\FilamentOauth2;
use Filament\Contracts\Plugin;
use Filament\Panel;
@ -15,7 +14,7 @@ class FilamentOauth2Plugin implements Plugin
public function register(Panel $panel): void
{
$panel->login(FilamentOauth2::getLoginRouteAction());
//
}
public function boot(Panel $panel): void

View File

@ -2,6 +2,12 @@
namespace AlexanderGabriel\FilamentOauth2;
use Filament\Support\Assets\AlpineComponent;
use Filament\Support\Assets\Asset;
use Filament\Support\Assets\Css;
use Filament\Support\Assets\Js;
use Filament\Support\Facades\FilamentAsset;
use Filament\Support\Facades\FilamentIcon;
use Illuminate\Filesystem\Filesystem;
use Livewire\Features\SupportTesting\Testable;
use Spatie\LaravelPackageTools\Commands\InstallCommand;
@ -28,9 +34,10 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
->hasInstallCommand(function (InstallCommand $command) {
$command
->publishConfigFile()
->publishMigrations()
->askToRunMigrations()
->askToStarRepoOnGitHub('alexandergabriel/filament-oauth2');
})
->hasRoutes('web');
});
$configFileName = $package->shortName();
@ -38,6 +45,10 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
$package->hasConfigFile();
}
if (file_exists($package->basePath('/../database/migrations'))) {
$package->hasMigrations($this->getMigrations());
}
if (file_exists($package->basePath('/../resources/lang'))) {
$package->hasTranslations();
}
@ -51,6 +62,20 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
public function packageBooted(): void
{
// Asset Registration
FilamentAsset::register(
$this->getAssets(),
$this->getAssetPackageName()
);
FilamentAsset::registerScriptData(
$this->getScriptData(),
$this->getAssetPackageName()
);
// Icon Registration
FilamentIcon::register($this->getIcons());
// Handle Stubs
if (app()->runningInConsole()) {
foreach (app(Filesystem::class)->files(__DIR__ . '/../stubs/') as $file) {
@ -64,6 +89,23 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
Testable::mixin(new TestsFilamentOauth2);
}
protected function getAssetPackageName(): ?string
{
return 'alexandergabriel/filament-oauth2';
}
/**
* @return array<Asset>
*/
protected function getAssets(): array
{
return [
// AlpineComponent::make('filament-oauth2', __DIR__ . '/../resources/dist/components/filament-oauth2.js'),
Css::make('filament-oauth2-styles', __DIR__ . '/../resources/dist/filament-oauth2.css'),
Js::make('filament-oauth2-scripts', __DIR__ . '/../resources/dist/filament-oauth2.js'),
];
}
/**
* @return array<class-string>
*/
@ -74,6 +116,14 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
];
}
/**
* @return array<string>
*/
protected function getIcons(): array
{
return [];
}
/**
* @return array<string>
*/
@ -82,4 +132,21 @@ class FilamentOauth2ServiceProvider extends PackageServiceProvider
return [];
}
/**
* @return array<string, mixed>
*/
protected function getScriptData(): array
{
return [];
}
/**
* @return array<string>
*/
protected function getMigrations(): array
{
return [
'create_filament-oauth2_table',
];
}
}

View File

@ -1,136 +0,0 @@
<?php
namespace AlexanderGabriel\FilamentOauth2\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Models\Role;
use App\Models\User;
use Dotenv\Exception\ValidationException;
use Exception;
use Filament\Facades\Filament;
use Filament\Http\Responses\Auth\LoginResponse;
use Filament\Models\Contracts\FilamentUser;
use Illuminate\Http\Request;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Provider\GenericProvider;
use League\OAuth2\Client\Token\AccessToken;
class Oauth2Controller extends Controller
{
private GenericProvider $oauth2Provider;
public function __construct()
{
// https://oauth2-client.thephpleague.com/usage/
$this->oauth2Provider = new GenericProvider([
'clientId' => config('filament-oauth2.clientId'), // The client ID assigned to you by the provider
'clientSecret' => config('filament-oauth2.clientSecret'), // The client password assigned to you by the provider
'redirectUri' => route('filament-oauth2.handleCallback'),
'urlAuthorize' => config('filament-oauth2.urlAuthorize'),
'urlAccessToken' => config('filament-oauth2.urlAccessToken'),
'urlResourceOwnerDetails' => config('filament-oauth2.urlResourceOwnerDetails'),
'scopes' => config('filament-oauth2.scopes'),
]);
}
public function redirectToOauth2Server()
{
return redirect($this->oauth2Provider->getAuthorizationUrl());
}
public function handleCallback(Request $request)
{
try {
$accessToken = $this->oauth2Provider->getAccessToken('authorization_code', ['code' => $request->input('code')]);
$oauth2User = $this->oauth2Provider->getResourceOwner($accessToken)->toArray();
$user = User::firstOrCreate([
'email' => $oauth2User['email'],
],[
'name' => $oauth2User['name'],
'password' => 'nonsense'
]);
if($user->name != $oauth2User['name'])
{
$user->name = $oauth2User['name'];
$user->save();
}
Filament::auth()->loginUsingId($user->id, false);
if (
($user instanceof FilamentUser) &&
(! $user->canAccessPanel(Filament::getCurrentPanel()))
) {
Filament::auth()->logout();
$this->throwFailureValidationException();
}
session()->regenerate();
//Should i update Roles and are there roles?
$hasRoles = false;
try {
$roles = $user->roles();
if($roles) $hasRoles = true;
}
catch (Exception $e) {
//keine Rollen... Nichts tun...
}
if($hasRoles && config('filament-oauth2.updateRoles') != false) {
//Are there roles in the Token?
$accessToken = explode(".", $accessToken);
if(isset($accessToken[1])) {
$accessToken = json_decode(base64_decode($accessToken[1]));
$clientId = config('filament-oauth2.clientId');
if(isset($accessToken->resource_access) && isset($accessToken->resource_access->$clientId)) {
// Roles are defined. Maybe empty to remove all Roles from user
// TODO: test
if(!isset($accessToken->resource_access->$clientId->roles)) $roles = [];
else $roles = $accessToken->resource_access->$clientId->roles;
$userRoles = $user->roles();
//disconnect roles
foreach ($userRoles as $userRole) {
if(!in_array($userRole, $roles)) {
$user->roles()->detach($userRole);
}
}
// connect or create roles
foreach($roles as $role) {
$existingRole = Role::first('name', '=', $role);
if($existingRole) {
$user->roles()->attach($existingRole->id);
} else {
$newRole = Role::create(['name' => $role]);
$newRole->save();
$user->roles()->attach($newRole);
}
}
}
}
}
return app(LoginResponse::class);
} catch (IdentityProviderException $e) {
dd($e->getMessage());
}
}
protected function throwFailureValidationException(): never
{
throw ValidationException::withMessages([
'data.email' => __('filament-panels::pages/auth/login.messages.failed'),
]);
}
//TODO: Logout
// filament-oauth2-demo/vendor/filament/filament/src/Http/Controllers/Auth/LogoutController.php
}