浏览代码

Fix oauth oob (urn:ietf:wg:oauth:2.0:oob) support. Fixes #2522

Daniel Supernault 6 月之前
父节点
当前提交
8afbdb03a8
共有 3 个文件被更改,包括 187 次插入0 次删除
  1. 99 0
      app/Http/Controllers/OAuth/OobAuthorizationController.php
  2. 1 0
      app/Providers/AuthServiceProvider.php
  3. 87 0
      routes/web.php

+ 99 - 0
app/Http/Controllers/OAuth/OobAuthorizationController.php

@@ -0,0 +1,99 @@
+<?php
+
+namespace App\Http\Controllers\OAuth;
+
+use Laravel\Passport\Http\Controllers\ApproveAuthorizationController;
+use Illuminate\Http\Request;
+use League\OAuth2\Server\Exception\OAuthServerException;
+use Nyholm\Psr7\Response as Psr7Response;
+
+class OobAuthorizationController extends ApproveAuthorizationController
+{
+    /**
+     * Approve the authorization request.
+     *
+     * @param  Request  $request
+     * @return \Illuminate\Http\Response
+     */
+    public function approve(Request $request)
+    {
+        $this->assertValidAuthToken($request);
+
+        $authRequest = $this->getAuthRequestFromSession($request);
+        $authRequest->setAuthorizationApproved(true);
+
+        return $this->withErrorHandling(function () use ($authRequest) {
+            $response = $this->server->completeAuthorizationRequest($authRequest, new Psr7Response);
+            
+            if ($this->isOutOfBandRequest($authRequest)) {
+                $code = $this->extractAuthorizationCode($response);
+                return response()->json([
+                    'code' => $code,
+                    'state' => $authRequest->getState()
+                ]);
+            }
+
+            return $this->convertResponse($response);
+        });
+    }
+
+    /**
+     * Check if the request is an out-of-band OAuth request.
+     *
+     * @param  \League\OAuth2\Server\RequestTypes\AuthorizationRequest  $authRequest
+     * @return bool
+     */
+    protected function isOutOfBandRequest($authRequest)
+    {
+        return $authRequest->getRedirectUri() === 'urn:ietf:wg:oauth:2.0:oob';
+    }
+
+    /**
+     * Extract the authorization code from the PSR-7 response.
+     *
+     * @param  \Psr\Http\Message\ResponseInterface  $response
+     * @return string
+     * @throws \League\OAuth2\Server\Exception\OAuthServerException
+     */
+    protected function extractAuthorizationCode($response)
+    {
+        $location = $response->getHeader('Location')[0] ?? '';
+        
+        if (empty($location)) {
+            throw OAuthServerException::serverError('Missing authorization code in response');
+        }
+
+        parse_str(parse_url($location, PHP_URL_QUERY), $params);
+        
+        if (!isset($params['code'])) {
+            throw OAuthServerException::serverError('Invalid authorization code format');
+        }
+
+        return $params['code'];
+    }
+
+    /**
+     * Handle OAuth errors for both redirect and OOB flows.
+     *
+     * @param  \Closure  $callback
+     * @return \Illuminate\Http\Response
+     */
+    protected function withErrorHandling($callback)
+    {
+        try {
+            return $callback();
+        } catch (OAuthServerException $e) {
+            if ($this->isOutOfBandRequest($this->getAuthRequestFromSession(request()))) {
+                return response()->json([
+                    'error' => $e->getErrorType(),
+                    'message' => $e->getMessage(),
+                    'hint' => $e->getHint()
+                ], $e->getHttpStatusCode());
+            }
+
+            return $this->convertResponse(
+                $e->generateHttpResponse(new Psr7Response)
+            );
+        }
+    }
+}

+ 1 - 0
app/Providers/AuthServiceProvider.php

@@ -25,6 +25,7 @@ class AuthServiceProvider extends ServiceProvider
     public function boot()
     {
         if(config('pixelfed.oauth_enabled') == true) {
+            Passport::ignoreRoutes();
             Passport::tokensExpireIn(now()->addDays(config('instance.oauth.token_expiration', 356)));
             Passport::refreshTokensExpireIn(now()->addDays(config('instance.oauth.refresh_expiration', 400)));
             Passport::enableImplicitGrant();

+ 87 - 0
routes/web.php

@@ -45,6 +45,93 @@ Route::domain(config('pixelfed.domain.app'))->middleware(['validemail', 'twofact
     Route::get('auth/forgot/email', 'UserEmailForgotController@index')->name('email.forgot');
     Route::post('auth/forgot/email', 'UserEmailForgotController@store')->middleware('throttle:10,900,forgotEmail');
 
+    Route::group([
+        'as' => 'passport.',
+        'prefix' => config('passport.path', 'oauth'),
+    ], function () {
+        Route::post('/token', [
+            'uses' => '\Laravel\Passport\Http\Controllers\AccessTokenController@issueToken',
+            'as' => 'token',
+            'middleware' => 'throttle',
+        ]);
+
+        Route::get('/authorize', [
+            'uses' => '\Laravel\Passport\Http\Controllers\AuthorizationController@authorize',
+            'as' => 'authorizations.authorize',
+            'middleware' => 'web',
+        ]);
+
+        $guard = config('passport.guard', null);
+
+        Route::middleware(['web', $guard ? 'auth:'.$guard : 'auth'])->group(function () {
+            Route::post('/token/refresh', [
+                'uses' => '\Laravel\Passport\Http\Controllers\TransientTokenController@refresh',
+                'as' => 'token.refresh',
+            ]);
+
+            Route::post('/authorize', [
+                'uses' => '\App\Http\Controllers\OAuth\OobAuthorizationController@approve',
+                'as' => 'authorizations.approve',
+            ]);
+
+            Route::delete('/authorize', [
+                'uses' => '\Laravel\Passport\Http\Controllers\DenyAuthorizationController@deny',
+                'as' => 'authorizations.deny',
+            ]);
+
+            Route::get('/tokens', [
+                'uses' => '\Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@forUser',
+                'as' => 'tokens.index',
+            ]);
+
+            Route::delete('/tokens/{token_id}', [
+                'uses' => '\Laravel\Passport\Http\Controllers\AuthorizedAccessTokenController@destroy',
+                'as' => 'tokens.destroy',
+            ]);
+
+            Route::get('/clients', [
+                'uses' => '\Laravel\Passport\Http\Controllers\ClientController@forUser',
+                'as' => 'clients.index',
+            ]);
+
+            Route::post('/clients', [
+                'uses' => '\Laravel\Passport\Http\Controllers\ClientController@store',
+                'as' => 'clients.store',
+            ]);
+
+            Route::put('/clients/{client_id}', [
+                'uses' => '\Laravel\Passport\Http\Controllers\ClientController@update',
+                'as' => 'clients.update',
+            ]);
+
+            Route::delete('/clients/{client_id}', [
+                'uses' => '\Laravel\Passport\Http\Controllers\ClientController@destroy',
+                'as' => 'clients.destroy',
+            ]);
+
+            Route::get('/scopes', [
+                'uses' => '\Laravel\Passport\Http\Controllers\ScopeController@all',
+                'as' => 'scopes.index',
+            ]);
+
+            Route::get('/personal-access-tokens', [
+                'uses' => '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@forUser',
+                'as' => 'personal.tokens.index',
+            ]);
+
+            Route::post('/personal-access-tokens', [
+                'uses' => '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@store',
+                'as' => 'personal.tokens.store',
+            ]);
+
+            Route::delete('/personal-access-tokens/{token_id}', [
+                'uses' => '\Laravel\Passport\Http\Controllers\PersonalAccessTokenController@destroy',
+                'as' => 'personal.tokens.destroy',
+            ]);
+        });
+
+    });
+
     Route::get('discover', 'DiscoverController@home')->name('discover');
 
     Route::get('discover/tags/{hashtag}', 'DiscoverController@showTags');