mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-03 14:10:57 +08:00
oauth providers
This commit is contained in:
123
app/Filament/Resources/Oauth/ProviderResource.php
Normal file
123
app/Filament/Resources/Oauth/ProviderResource.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Oauth;
|
||||
|
||||
use App\Filament\Resources\Oauth\ProviderResource\Pages;
|
||||
use App\Filament\Resources\Oauth\ProviderResource\RelationManagers;
|
||||
use App\Models\OauthProvider;
|
||||
use Filament\Forms;
|
||||
use Filament\Forms\Form;
|
||||
use Filament\Resources\Resource;
|
||||
use Filament\Tables;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
|
||||
class ProviderResource extends Resource
|
||||
{
|
||||
protected static ?string $model = OauthProvider::class;
|
||||
|
||||
protected static ?string $navigationIcon = 'heroicon-o-rectangle-stack';
|
||||
|
||||
protected static ?string $navigationGroup = 'Oauth';
|
||||
|
||||
protected static ?int $navigationSort = 5;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
return __('admin.sidebar.oauth_provider');
|
||||
}
|
||||
|
||||
public static function getBreadcrumb(): string
|
||||
{
|
||||
return self::getNavigationLabel();
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
->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('/'),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Filament\Resources\Oauth\ProviderResource\Pages;
|
||||
|
||||
use App\Filament\PageListSingle;
|
||||
use App\Filament\Resources\Oauth\ProviderResource;
|
||||
use Filament\Actions;
|
||||
use Filament\Resources\Pages\ManageRecords;
|
||||
|
||||
class ManageProviders extends PageListSingle
|
||||
{
|
||||
protected static string $resource = ProviderResource::class;
|
||||
|
||||
protected function getHeaderActions(): array
|
||||
{
|
||||
return [
|
||||
Actions\CreateAction::make(),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Resources\UserResource;
|
||||
use App\Models\OauthClient;
|
||||
use App\Models\OauthProvider;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
@@ -11,52 +12,74 @@ use Laravel\Passport\Client;
|
||||
|
||||
class OauthController extends Controller
|
||||
{
|
||||
private int $clientId = 8;
|
||||
private string $baseUri;
|
||||
|
||||
private ?OauthClient $client = null;
|
||||
|
||||
// public function __construct()
|
||||
// {
|
||||
// $this->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)
|
||||
|
||||
26
app/Models/OauthProvider.php
Normal file
26
app/Models/OauthProvider.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Laravel\Passport\Client;
|
||||
use Ramsey\Uuid;
|
||||
|
||||
class OauthProvider extends NexusModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'uuid', 'name', 'client_id', 'client_secret', 'authorization_endpoint_url', 'token_endpoint_url',
|
||||
'user_info_endpoint_url', 'id_claim', 'username_claim', 'email_claim', 'enabled', 'priority',
|
||||
];
|
||||
|
||||
public $timestamps = true;
|
||||
|
||||
protected $casts = [
|
||||
'enabled' => 'boolean',
|
||||
];
|
||||
protected static function booted(): void
|
||||
{
|
||||
static::creating(function (OauthProvider $model) {
|
||||
$model->uuid = Uuid\v4();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
<?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('oauth_providers', function (Blueprint $table) {
|
||||
$table->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');
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.0');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-04-20');
|
||||
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-04-24');
|
||||
defined('IN_TRACKER') || define('IN_TRACKER', false);
|
||||
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
|
||||
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");
|
||||
|
||||
@@ -35,5 +35,6 @@ $lang_login = array
|
||||
'rowhead_two_step_code' => '两步验证',
|
||||
'two_step_code_tooltip' => '如有设置必须填写',
|
||||
'text_complain' => '申诉通道',
|
||||
'other_methods' => "其他登录方式",
|
||||
);
|
||||
?>
|
||||
|
||||
@@ -35,5 +35,6 @@ $lang_login = array
|
||||
'rowhead_two_step_code' => '兩步驗證',
|
||||
'two_step_code_tooltip' => '如有設置必須填寫',
|
||||
'text_complain' => '申訴通道',
|
||||
'other_methods' => "其他登錄方式",
|
||||
);
|
||||
?>
|
||||
|
||||
@@ -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",
|
||||
);
|
||||
|
||||
?>
|
||||
|
||||
@@ -93,8 +93,20 @@ if ($useChallengeResponseAuthentication) {
|
||||
print('<input type="hidden" name="response" />');
|
||||
}
|
||||
?>
|
||||
|
||||
</form>
|
||||
<?php
|
||||
$oauthProviders = \App\Models\OauthProvider::query()
|
||||
->orderBy("priority", 'desc')
|
||||
->where('enabled', '=', 1)
|
||||
->get();
|
||||
$items = [];
|
||||
foreach ($oauthProviders as $oauthProvider) {
|
||||
$items[] = sprintf('[<b><a href="oauth/redirect/%s">%s</a></b>]', $oauthProvider->uuid, $oauthProvider->name);
|
||||
}
|
||||
if (!empty($items)) {
|
||||
echo sprintf("<p>%s: %s</p>", $lang_login['other_methods'], implode(" ", $items));
|
||||
}
|
||||
?>
|
||||
<p>[<b><a href="complains.php"><?= $lang_login['text_complain'] ?></a></b>]</p>
|
||||
<p><?php echo $lang_login['p_no_account_signup']?></p>
|
||||
<?php
|
||||
|
||||
@@ -43,6 +43,7 @@ return [
|
||||
'oauth_auth_code' => 'Auth codes',
|
||||
'oauth_refresh_token' => 'Refresh tokens',
|
||||
'token' => 'Access tokens',
|
||||
'oauth_provider' => 'Authorization provider',
|
||||
],
|
||||
'resources' => [
|
||||
'agent_allow' => [
|
||||
|
||||
@@ -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',
|
||||
];
|
||||
|
||||
@@ -41,6 +41,7 @@ return [
|
||||
'oauth_auth_code' => '授权码',
|
||||
'oauth_refresh_token' => '刷新令牌',
|
||||
'token' => '访问令牌',
|
||||
'oauth_provider' => '身份验证',
|
||||
],
|
||||
'resources' => [
|
||||
'agent_allow' => [
|
||||
|
||||
@@ -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' => '邮箱字段名',
|
||||
];
|
||||
|
||||
@@ -43,6 +43,7 @@ return [
|
||||
'oauth_auth_code' => '授權碼',
|
||||
'oauth_refresh_token' => '刷新令牌',
|
||||
'token' => '訪問令牌',
|
||||
'oauth_provider' => '身份驗證',
|
||||
],
|
||||
'resources' => [
|
||||
'agent_allow' => [
|
||||
|
||||
@@ -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' => '郵箱字段名',
|
||||
];
|
||||
|
||||
@@ -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']);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user