Files
firefly-iii/app/Http/Controllers/Profile/OAuthController.php

211 lines
7.0 KiB
PHP
Raw Normal View History

2026-04-15 08:31:06 +02:00
<?php
2026-04-15 08:31:06 +02:00
/*
* OAuthController.php
* Copyright (c) 2026 james@firefly-iii.org
*
* This file is part of Firefly III (https://github.com/firefly-iii).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
2026-04-18 05:25:05 +02:00
declare(strict_types=1);
2026-04-15 08:31:06 +02:00
namespace FireflyIII\Http\Controllers\Profile;
use FireflyIII\Http\Controllers\Controller;
2026-04-16 17:30:25 +02:00
use Illuminate\Contracts\Validation\Factory as ValidationFactory;
use Illuminate\Contracts\View\Factory;
2026-04-16 17:30:25 +02:00
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\DB;
2026-04-15 08:31:06 +02:00
use Illuminate\Support\Facades\Log;
use Illuminate\View\View;
2026-04-18 05:17:03 +02:00
use Laravel\Passport\Client;
2026-04-16 17:30:25 +02:00
use Laravel\Passport\ClientRepository;
use Laravel\Passport\Token;
use SensitiveParameter;
2026-04-15 08:31:06 +02:00
2026-04-18 05:32:54 +02:00
final class OAuthController extends Controller
2026-04-15 08:31:06 +02:00
{
protected bool $internalAuth;
2026-04-16 17:30:25 +02:00
public function __construct(
2026-04-18 14:04:12 +02:00
protected ClientRepository $clients,
protected ValidationFactory $validation
2026-04-18 14:04:12 +02:00
)
{
2026-04-15 08:31:06 +02:00
parent::__construct();
$this->middleware(static function ($request, $next) {
2026-04-18 14:04:12 +02:00
app('view')->share('title', (string)trans('firefly.oauth_tokens'));
2026-04-15 08:31:06 +02:00
app('view')->share('mainTitleIcon', 'fa-user');
return $next($request);
});
$authGuard = config('firefly.authentication_guard');
$this->internalAuth = 'web' === $authGuard;
Log::debug(sprintf('ProfileController::__construct(). Authentication guard is "%s"', $authGuard));
}
public function destroyClient(Request $request, string $clientId): Response
{
/** @var null|Client $client */
$client = auth()->user()->oauthApps()->where('revoked', false)->find($clientId);
if (null === $client) {
return new Response('', 404);
}
$client
->tokens()
->with('refreshToken')
->each(function (#[SensitiveParameter] Token $token): void {
$token->refreshToken?->revoke();
$token->revoke();
2026-04-18 14:04:12 +02:00
});
$client->forceFill(['revoked' => true])->save();
return new Response('', Response::HTTP_NO_CONTENT);
}
public function destroyPersonalAccessToken(Request $request, string $tokenId): Response
{
$token = auth()->user()->tokens()->where('revoked', false)->find($tokenId);
if (null === $token) {
return new Response('', 404);
}
$token->revoke();
return new Response('', Response::HTTP_NO_CONTENT);
}
2026-04-18 05:25:05 +02:00
/**
* @return Factory|\Illuminate\Contracts\View\View|View
2026-04-18 05:25:05 +02:00
*/
2026-04-16 17:30:25 +02:00
public function index()
{
$count = DB::table('oauth_clients')->where('grant_types', '["personal_access"]')->whereNull('owner_id')->count();
if (0 === $count) {
/** @var ClientRepository $repository */
$repository = app(ClientRepository::class);
$repository->createPersonalAccessGrantClient('Firefly III Personal Access Grant Client', null);
}
2026-04-18 14:04:12 +02:00
$link = route('index');
2026-04-18 05:17:03 +02:00
return view('profile.oauth.index', compact('link'));
2026-04-15 08:31:06 +02:00
}
2026-04-16 17:30:25 +02:00
public function listClients(): JsonResponse
{
2026-04-18 14:04:12 +02:00
Log::debug('Now in listClients()');
2026-04-16 17:30:25 +02:00
// Retrieving all the OAuth app clients that belong to the user...
2026-04-18 05:17:03 +02:00
$clients = auth()->user()->oauthApps()->where('revoked', false)->get();
$array = [];
2026-04-18 06:24:13 +02:00
/** @var Client $client */
foreach ($clients as $client) {
$item = $client->toArray();
2026-04-18 06:24:13 +02:00
$item['confidential'] = $client->confidential();
$array[] = $item;
2026-04-18 06:24:13 +02:00
}
2026-04-18 06:24:13 +02:00
return response()->json($array);
2026-04-16 17:30:25 +02:00
}
public function listPersonalAccessTokens(): JsonResponse
2026-04-18 05:17:03 +02:00
{
// Retrieving all the OAuth app clients that belong to the user...
$tokens = auth()
->user()
->tokens()
->with('client')
->where('revoked', false)
->where('expires_at', '>', Date::now())
->get()
2026-04-18 14:04:12 +02:00
->filter(fn(#[SensitiveParameter] Token $token) => $token->client->hasGrantType('personal_access'));
2026-04-18 05:17:03 +02:00
return response()->json($tokens);
2026-04-18 05:17:03 +02:00
}
2026-04-18 14:04:12 +02:00
public function regenerateClientSecret(Request $request, string $clientId): JsonResponse | Response
2026-04-18 05:17:03 +02:00
{
2026-04-18 14:04:12 +02:00
$client = auth()->user()->oauthApps()->where('revoked', false)->find($clientId);
2026-04-18 05:25:05 +02:00
if (null === $client) {
2026-04-18 05:17:03 +02:00
return new Response('', 404);
}
// $client->
2026-04-18 05:17:03 +02:00
$this->clients->regenerateSecret($client);
$arr = $client->toArray();
$arr['plainSecret'] = $client->plainSecret;
return response()->json($arr);
2026-04-18 05:17:03 +02:00
}
public function storeClient(Request $request): JsonResponse
2026-04-18 05:17:03 +02:00
{
$this->validation->make($request->only(['name', 'redirect_uris', 'confidential']), [
2026-04-18 05:17:03 +02:00
'name' => ['required', 'string', 'max:255'],
'redirect_uris' => ['required', 'url'],
'confidential' => 'boolean',
2026-04-18 05:17:03 +02:00
])->validate();
// Creating an OAuth app client that belongs to the given user...
$client = app(ClientRepository::class)->createAuthorizationCodeGrantClient(
2026-04-18 14:04:12 +02:00
name : $request->input('name'),
redirectUris: [$request->input('redirect_uris')],
confidential: $request->input('confidential'),
2026-04-18 14:04:12 +02:00
user : auth()->user()
);
$arr = $client->toArray();
$arr['plainSecret'] = $client->plainSecret;
2026-04-18 05:17:03 +02:00
return response()->json($arr);
2026-04-18 05:17:03 +02:00
}
2026-04-16 17:30:25 +02:00
public function storePersonalAccessToken(Request $request): JsonResponse
{
2026-04-18 05:32:54 +02:00
$this->validation->make($request->only(['name']), [
'name' => ['required', 'max:255'],
])->validate();
2026-04-16 17:30:25 +02:00
return response()->json($request->user()->createToken($request->name));
}
2026-04-18 14:04:12 +02:00
public function updateClient(Request $request, string $clientId): Client | Response
2026-04-16 17:30:25 +02:00
{
$client = auth()->user()->oauthApps()->where('revoked', false)->find($clientId);
2026-04-16 17:30:25 +02:00
if (null === $client) {
2026-04-16 17:30:25 +02:00
return new Response('', 404);
}
$this->validation->make($request->only(['name', 'redirect_uris']), [
'name' => ['required', 'string', 'max:255'],
'redirect_uris' => ['required', 'url'],
])->validate();
2026-04-16 17:30:25 +02:00
$this->clients->update($client, $request->input('name'), explode(',', $request->input('redirect_uris'))); // FIXME replace
2026-04-16 17:30:25 +02:00
return $client;
2026-04-16 17:30:25 +02:00
}
2026-04-15 08:31:06 +02:00
}