124 lines
4.1 KiB
PHP
124 lines
4.1 KiB
PHP
<?php
|
|||
|
|
|
||
|
|
namespace App\Http\Controllers;
|
||
|
|
|
||
|
|
use App\Models\OauthClient;
|
||
|
|
use App\Services\OAuthService;
|
||
|
|
use Illuminate\Http\Request;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
|
||
|
|
class OauthController extends Controller
|
||
|
|
{
|
||
|
|
public function __construct(private readonly OAuthService $oauth) {}
|
||
|
|
|
||
|
|
public function authorize(Request $request)
|
||
|
|
{
|
||
|
|
$data = $request->validate([
|
||
|
|
'response_type' => ['required', 'in:code'],
|
||
|
|
'client_id' => ['required', 'string'],
|
||
|
|
'redirect_uri' => ['required', 'string'],
|
||
|
|
'scope' => ['nullable', 'string'],
|
||
|
|
'state' => ['required', 'string', 'max:200'],
|
||
|
|
'nonce' => ['nullable', 'string', 'max:200'],
|
||
|
|
'code_challenge' => ['required', 'string', 'max:200'],
|
||
|
|
'code_challenge_method' => ['required', 'in:S256'],
|
||
|
|
'confirm' => ['nullable'],
|
||
|
|
]);
|
||
|
|
$client = OauthClient::query()->where('client_id', $data['client_id'])->where('status', 'active')->first();
|
||
|
|
if (! $client || ! $client->allowsRedirect($data['redirect_uri'])) {
|
||
|
|
abort(400, '客户端或回调地址无效');
|
||
|
|
}
|
||
|
|
if (! Auth::check()) {
|
||
|
|
$login = rtrim((string) config('qz.frontend_url'), '/').'/login?redirect='.urlencode($request->fullUrl());
|
||
|
|
|
||
|
|
return redirect()->away($login);
|
||
|
|
}
|
||
|
|
$user = $request->user();
|
||
|
|
if ($user->status !== 'active') {
|
||
|
|
return redirect()->away($this->withQuery($data['redirect_uri'], [
|
||
|
|
'error' => 'access_denied',
|
||
|
|
'state' => $data['state'],
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
$scopes = $this->oauth->scopes($data['scope'] ?? 'openid');
|
||
|
|
if (! $client->is_first_party && ! $request->boolean('confirm')) {
|
||
|
|
return view('oauth.consent', [
|
||
|
|
'client' => $client,
|
||
|
|
'scopes' => $scopes,
|
||
|
|
'query' => $request->except(['_token', 'confirm']),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
$code = $this->oauth->issueCode($client, $user, $data, $scopes);
|
||
|
|
|
||
|
|
return redirect()->away($this->withQuery($data['redirect_uri'], [
|
||
|
|
'code' => $code,
|
||
|
|
'state' => $data['state'],
|
||
|
|
]));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function token(Request $request)
|
||
|
|
{
|
||
|
|
$client = $this->oauth->clientFromRequest($request);
|
||
|
|
if (! $client) {
|
||
|
|
return response()->json(['error' => 'invalid_client'], 401);
|
||
|
|
}
|
||
|
|
$result = match ($request->input('grant_type')) {
|
||
|
|
'authorization_code' => $this->oauth->exchange($request, $client),
|
||
|
|
'refresh_token' => $this->oauth->refresh($request, $client),
|
||
|
|
default => ['error' => 'unsupported_grant_type'],
|
||
|
|
};
|
||
|
|
$status = isset($result['error']) ? 400 : 200;
|
||
|
|
|
||
|
|
return response()->json($result, $status);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function userinfo(Request $request)
|
||
|
|
{
|
||
|
|
$token = $this->bearer($request);
|
||
|
|
$info = $token ? $this->oauth->userinfo($token) : null;
|
||
|
|
if (! $info) {
|
||
|
|
return response()->json(['error' => 'invalid_token'], 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
return response()->json($info);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function introspect(Request $request)
|
||
|
|
{
|
||
|
|
$client = $this->oauth->clientFromRequest($request);
|
||
|
|
if (! $client) {
|
||
|
|
return response()->json(['active' => false], 401);
|
||
|
|
}
|
||
|
|
|
||
|
|
return response()->json($this->oauth->introspect((string) $request->input('token')));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function revoke(Request $request)
|
||
|
|
{
|
||
|
|
$client = $this->oauth->clientFromRequest($request);
|
||
|
|
if (! $client) {
|
||
|
|
return response()->json(['error' => 'invalid_client'], 401);
|
||
|
|
}
|
||
|
|
$this->oauth->revoke((string) $request->input('token'));
|
||
|
|
|
||
|
|
return response()->noContent();
|
||
|
|
}
|
||
|
|
|
||
|
|
private function bearer(Request $request): ?string
|
||
|
|
{
|
||
|
|
$header = (string) $request->header('Authorization');
|
||
|
|
if (! str_starts_with($header, 'Bearer ')) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
return trim(substr($header, 7));
|
||
|
|
}
|
||
|
|
|
||
|
|
private function withQuery(string $uri, array $params): string
|
||
|
|
{
|
||
|
|
$joiner = str_contains($uri, '?') ? '&' : '?';
|
||
|
|
|
||
|
|
return $uri.$joiner.http_build_query($params);
|
||
|
|
}
|
||
|
|
}
|