diff --git a/app/Filament/Resources/Oauth/ProviderResource.php b/app/Filament/Resources/Oauth/ProviderResource.php new file mode 100644 index 00000000..5ccba4e0 --- /dev/null +++ b/app/Filament/Resources/Oauth/ProviderResource.php @@ -0,0 +1,123 @@ +schema([ + Forms\Components\TextInput::make('name') + ->label(__('label.name')) + ->required() + , + Forms\Components\TextInput::make('client_id') + ->label(__('oauth.client_id')) + ->required() + , + Forms\Components\TextInput::make('client_secret') + ->label(__('oauth.secret')) + ->required() + , + Forms\Components\TextInput::make('authorization_endpoint_url') + ->label(__('oauth.authorization_endpoint_url')) + ->required() + , + Forms\Components\TextInput::make('token_endpoint_url') + ->label(__('oauth.token_endpoint_url')) + ->required() + , + Forms\Components\TextInput::make('user_info_endpoint_url') + ->label(__('oauth.user_info_endpoint_url')) + ->required() + , + Forms\Components\TextInput::make('id_claim') + ->label(__('oauth.id_claim')) + ->required() + , + Forms\Components\TextInput::make('username_claim') + ->label(__('oauth.username_claim')) + , + Forms\Components\TextInput::make('email_claim') + ->label(__('oauth.email_claim')) + , + Forms\Components\TextInput::make('priority') + ->label(__('label.priority')) + ->default(0) + ->numeric() + ->helperText(__('label.priority_help')) + , + Forms\Components\Toggle::make('enabled') + ->label(__('label.enabled')) + , + ]); + } + + public static function table(Table $table): Table + { + return $table + ->columns([ + Tables\Columns\TextColumn::make('id'), + Tables\Columns\TextColumn::make('client_id')->label(__('oauth.client_id')), + Tables\Columns\TextColumn::make('client_secret')->label(__('oauth.secret')), + Tables\Columns\TextColumn::make('authorization_endpoint_url')->label(__('oauth.authorization_endpoint_url')), + Tables\Columns\TextColumn::make('uuid') + ->label(__('oauth.redirect')) + ->formatStateUsing(fn ($state) => url("/oauth/callback/$state")) + , + Tables\Columns\TextColumn::make('priority')->label(__('label.priority')), + Tables\Columns\TextColumn::make('updated_at')->label(__('label.updated_at')), + Tables\Columns\IconColumn::make('enabled')->boolean()->label(__('label.enabled')), + ]) + ->defaultSort('priority', 'desc') + ->filters([ + // + ]) + ->actions([ + Tables\Actions\EditAction::make(), + Tables\Actions\DeleteAction::make(), + ]) + ->bulkActions([ + Tables\Actions\BulkActionGroup::make([ + Tables\Actions\DeleteBulkAction::make(), + ]), + ]); + } + + public static function getPages(): array + { + return [ + 'index' => Pages\ManageProviders::route('/'), + ]; + } +} diff --git a/app/Filament/Resources/Oauth/ProviderResource/Pages/ManageProviders.php b/app/Filament/Resources/Oauth/ProviderResource/Pages/ManageProviders.php new file mode 100644 index 00000000..aafe307e --- /dev/null +++ b/app/Filament/Resources/Oauth/ProviderResource/Pages/ManageProviders.php @@ -0,0 +1,20 @@ +baseUri = getSchemeAndHttpHost(); -// -// $this->client = OauthClient::query()->find($this->clientId); -// } - public function redirect(Request $request) + /** + * client redirect to authorization server, use oauth_providers config + * + * @param Request $request + * @param string $uuid + * @return \Illuminate\Foundation\Application|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector + */ + public function redirect(Request $request, string $uuid) { -// $request->session()->put('state', $state = Str::random(40)); + $provider = OauthProvider::query()->where('uuid', $uuid)->firstOrFail(); + $request->session()->put('state', $state = Str::random(40)); $query = http_build_query([ - 'client_id' => $this->client->id, - 'redirect_uri' => $this->client->redirect, + 'client_id' => $provider->client_id, + 'redirect_uri' => $provider->redirect, 'response_type' => 'code', 'scope' => '', -// 'state' => $state, + 'state' => $state, // 'prompt' => 'none', // "none", "consent", or "login" ]); - - return redirect($this->baseUri.'/oauth/authorize?'.$query); + $authorizationUrl = sprintf( + '%s%s%s', + $provider->authorization_endpoint_url, + str_contains($provider->authorization_endpoint_url, '?') ? '&' : '?', + $query + ); + return redirect($authorizationUrl); } - public function callback(Request $request) + /** + * authorization server redirect to this url with auth code after user authorized + * and then use auth code to request to authorization server token endpoint url to get access token + * + * @param Request $request + * @param string $uuid + * @return array|mixed + * @throws \Illuminate\Http\Client\ConnectionException + * @throws \Throwable + */ + public function callback(Request $request, string $uuid) { -// $state = $request->session()->pull('state'); -// -// throw_unless( -// strlen($state) > 0 && $state === $request->state, -// \InvalidArgumentException::class -// ); + $state = $request->session()->pull('state'); - $response = Http::asForm()->post($this->baseUri.'/oauth/token', [ + throw_unless( + strlen($state) > 0 && $state === $request->state, + \InvalidArgumentException::class + ); + + $provider = OauthProvider::query()->where('uuid', $uuid)->firstOrFail(); + + $response = Http::asForm()->post($provider->token_endpoint_url, [ 'grant_type' => 'authorization_code', - 'client_id' => $this->client->id, - 'client_secret' => $this->client->secret, - 'redirect_uri' => $this->client->redirect, + 'client_id' => $provider->client_id, + 'client_secret' => $provider->client_secret, +// 'redirect_uri' => url("oauth/login"), 'code' => $request->code, ]); - - return $response->json(); + $tokenInfo = $response->json(); + do_log("tokenInfo: " . $response->body()); + //use token get user-info + $response = Http::withToken($tokenInfo['access_token'])->get($provider->user_info_endpoint_url); + $userInfo = $response->json(); + do_log("userInfo: " . $response->body()); + $userId = data_get($userInfo, $provider->id_claim); + $username = data_get($userInfo, $provider->username_claim); + $email = data_get($userInfo, $provider->email_claim); + dd($userInfo, $userId, $username, $email); } public function debug(Request $request) diff --git a/app/Models/OauthProvider.php b/app/Models/OauthProvider.php new file mode 100644 index 00000000..eeec084b --- /dev/null +++ b/app/Models/OauthProvider.php @@ -0,0 +1,26 @@ + 'boolean', + ]; + protected static function booted(): void + { + static::creating(function (OauthProvider $model) { + $model->uuid = Uuid\v4(); + }); + } +} diff --git a/app/Providers/Filament/AppPanelProvider.php b/app/Providers/Filament/AppPanelProvider.php index f7d72fd2..7e7b3032 100644 --- a/app/Providers/Filament/AppPanelProvider.php +++ b/app/Providers/Filament/AppPanelProvider.php @@ -11,6 +11,7 @@ use Filament\Pages; use Filament\Panel; use Filament\PanelProvider; use Filament\Support\Colors\Color; +use Filament\Tables\Columns\Column; use Filament\Widgets; use Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse; use Illuminate\Cookie\Middleware\EncryptCookies; @@ -85,6 +86,10 @@ class AppPanelProvider extends PanelProvider ->paginationPageOptions([10, 25, 50, 100]) ; }); + Column::configureUsing(function (Column $section): void { + $section + ->disabledClick(); + }); } public function register(): void diff --git a/database/migrations/2025_04_23_042250_create_oauth_providers_table.php b/database/migrations/2025_04_23_042250_create_oauth_providers_table.php new file mode 100644 index 00000000..72c7ed0c --- /dev/null +++ b/database/migrations/2025_04_23_042250_create_oauth_providers_table.php @@ -0,0 +1,39 @@ +id(); + $table->string('uuid')->unique(); + $table->string('name')->unique(); + $table->string('client_id'); + $table->string('client_secret'); + $table->string('authorization_endpoint_url'); + $table->string('token_endpoint_url'); + $table->string('user_info_endpoint_url'); + $table->string('id_claim'); + $table->string('username_claim')->nullable(); + $table->string('email_claim')->nullable(); + $table->boolean('enabled'); + $table->integer('priority'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('oauth_providers'); + } +}; diff --git a/include/constants.php b/include/constants.php index b6058544..a2553fe0 100644 --- a/include/constants.php +++ b/include/constants.php @@ -1,6 +1,6 @@ '两步验证', 'two_step_code_tooltip' => '如有设置必须填写', 'text_complain' => '申诉通道', + 'other_methods' => "其他登录方式", ); ?> diff --git a/lang/cht/lang_login.php b/lang/cht/lang_login.php index 66ff717e..ec604cec 100644 --- a/lang/cht/lang_login.php +++ b/lang/cht/lang_login.php @@ -35,5 +35,6 @@ $lang_login = array 'rowhead_two_step_code' => '兩步驗證', 'two_step_code_tooltip' => '如有設置必須填寫', 'text_complain' => '申訴通道', + 'other_methods' => "其他登錄方式", ); ?> diff --git a/lang/en/lang_login.php b/lang/en/lang_login.php index 584516b9..996b32c7 100644 --- a/lang/en/lang_login.php +++ b/lang/en/lang_login.php @@ -35,6 +35,7 @@ $lang_login = array 'rowhead_two_step_code' => 'Two-Factor Authentication', 'two_step_code_tooltip' => 'If enabled must be filled in', 'text_complain' => 'Complaint Channel', + 'other_methods' => "Other Methods", ); ?> diff --git a/public/login.php b/public/login.php index eb3cdc79..2f59564c 100644 --- a/public/login.php +++ b/public/login.php @@ -93,8 +93,20 @@ if ($useChallengeResponseAuthentication) { print(''); } ?> - +orderBy("priority", 'desc') + ->where('enabled', '=', 1) + ->get(); +$items = []; +foreach ($oauthProviders as $oauthProvider) { + $items[] = sprintf('[%s]', $oauthProvider->uuid, $oauthProvider->name); +} +if (!empty($items)) { + echo sprintf("
%s: %s
", $lang_login['other_methods'], implode(" ", $items)); +} +?> 'Auth codes', 'oauth_refresh_token' => 'Refresh tokens', 'token' => 'Access tokens', + 'oauth_provider' => 'Authorization provider', ], 'resources' => [ 'agent_allow' => [ diff --git a/resources/lang/en/oauth.php b/resources/lang/en/oauth.php index 8b56ad23..47c60cd9 100644 --- a/resources/lang/en/oauth.php +++ b/resources/lang/en/oauth.php @@ -11,4 +11,11 @@ return [ 'btn_approve' => 'Authorize', 'btn_deny' => 'Cancel', 'skips_authorization' => 'Skips authorization', + 'client_id' => 'Client ID', + 'authorization_endpoint_url' => 'Authorization URL', + 'token_endpoint_url' => 'Get token URL', + 'user_info_endpoint_url' => 'Get user info URL', + 'id_claim' => 'ID claim', + 'username_claim' => 'username claim', + 'email_claim' => 'email claim', ]; diff --git a/resources/lang/zh_CN/admin.php b/resources/lang/zh_CN/admin.php index 56212b13..e6c3c474 100644 --- a/resources/lang/zh_CN/admin.php +++ b/resources/lang/zh_CN/admin.php @@ -41,6 +41,7 @@ return [ 'oauth_auth_code' => '授权码', 'oauth_refresh_token' => '刷新令牌', 'token' => '访问令牌', + 'oauth_provider' => '身份验证', ], 'resources' => [ 'agent_allow' => [ diff --git a/resources/lang/zh_CN/oauth.php b/resources/lang/zh_CN/oauth.php index ad647b60..ce632f6e 100644 --- a/resources/lang/zh_CN/oauth.php +++ b/resources/lang/zh_CN/oauth.php @@ -11,4 +11,11 @@ return [ 'btn_approve' => '授权', 'btn_deny' => '取消', 'skips_authorization' => '跳过授权', + 'client_id' => '客户端 ID', + 'authorization_endpoint_url' => '授权地址', + 'token_endpoint_url' => '获取 token 地址', + 'user_info_endpoint_url' => '获取用户信息地址', + 'id_claim' => 'ID 字段名', + 'username_claim' => '用户名字段名', + 'email_claim' => '邮箱字段名', ]; diff --git a/resources/lang/zh_TW/admin.php b/resources/lang/zh_TW/admin.php index 5011d74f..210d4814 100644 --- a/resources/lang/zh_TW/admin.php +++ b/resources/lang/zh_TW/admin.php @@ -43,6 +43,7 @@ return [ 'oauth_auth_code' => '授權碼', 'oauth_refresh_token' => '刷新令牌', 'token' => '訪問令牌', + 'oauth_provider' => '身份驗證', ], 'resources' => [ 'agent_allow' => [ diff --git a/resources/lang/zh_TW/oauth.php b/resources/lang/zh_TW/oauth.php index c7217224..11cc07d0 100644 --- a/resources/lang/zh_TW/oauth.php +++ b/resources/lang/zh_TW/oauth.php @@ -11,4 +11,11 @@ return [ 'btn_approve' => '授權', 'btn_deny' => '取消', 'skips_authorization' => '跳過授權', + 'client_id' => '客戶端 ID', + 'authorization_endpoint_url' => '授權地址', + 'token_endpoint_url' => '獲取 token 地址', + 'user_info_endpoint_url' => '獲取用戶信息地址', + 'id_claim' => 'ID 字段名', + 'username_claim' => '用戶名字段名', + 'email_claim' => '郵箱字段名', ]; diff --git a/routes/web.php b/routes/web.php index f30a4f3b..35df3f8a 100644 --- a/routes/web.php +++ b/routes/web.php @@ -37,6 +37,6 @@ Route::get('test', [\App\Http\Controllers\ToolController::class, 'test'])->middl Route::group(['prefix' => 'oauth'], function () { Route::get("user-info", [\App\Http\Controllers\OauthController::class, 'userInfo'])->name("oauth.user_info")->middleware('auth:api'); -// Route::get('redirect', [\App\Http\Controllers\OauthController::class, 'redirect']); -// Route::get('callback', [\App\Http\Controllers\OauthController::class, 'callback']); + Route::get('redirect/{uuid}', [\App\Http\Controllers\OauthController::class, 'redirect']); + Route::get('callback/{uuid}', [\App\Http\Controllers\OauthController::class, 'callback']); });