1<?php
2
3// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the GNU Affero General Public License v3.0.
4// See the LICENCE file in the repository root for full licence text.
5
6namespace App\Http\Controllers\Passport;
7
8use Illuminate\Http\Request;
9use Laravel\Passport\ClientRepository;
10use Laravel\Passport\Exceptions\AuthenticationException;
11use Laravel\Passport\Http\Controllers\AuthorizationController as PassportAuthorizationController;
12use Laravel\Passport\Passport;
13use Laravel\Passport\TokenRepository;
14use Psr\Http\Message\ServerRequestInterface;
15
16/**
17 * Extension of Laravel\Passport\Http\Controllers\AuthorizationController
18 * to add support for scope normalization when requesting token scopes.
19 */
20class AuthorizationController extends PassportAuthorizationController
21{
22 /**
23 * Authorize a client to access the user's account.
24 * This overrides the default implementation to normalize scope requests
25 * and sort the scopes by key order.
26 *
27 * @param \Psr\Http\Message\ServerRequestInterface $psrRequest
28 * @param \Illuminate\Http\Request $request
29 * @param \Laravel\Passport\ClientRepository $clients
30 * @param \Laravel\Passport\TokenRepository $tokens
31 * @return \Illuminate\Http\Response
32 */
33 public function authorize(
34 ServerRequestInterface $psrRequest,
35 Request $request,
36 ClientRepository $clients,
37 TokenRepository $tokens
38 ) {
39 try {
40 return parent::authorize($this->normalizeRequestScopes($psrRequest), $request, $clients, $tokens);
41 } catch (AuthenticationException $_e) {
42 $cancelUrl = $request->fullUrl();
43 $cancelUrl .= strpos($cancelUrl, '?') === false ? '?' : '&';
44 $cancelUrl .= 'prompt=none';
45
46 return ext_view('sessions.create', [
47 'cancelUrl' => $cancelUrl,
48 ]);
49 }
50 }
51
52 /**
53 * Normalizes the authorization request's scopes.
54 *
55 * @param ServerRequestInterface $request
56 * @return ServerRequestInterface
57 */
58 private function normalizeRequestScopes(ServerRequestInterface $request): ServerRequestInterface
59 {
60 $params = $request->getQueryParams();
61 $scopes = $this->normalizeScopes(
62 explode(' ', $params['scope'] ?? '')
63 );
64
65 // temporary non-persisted token to validate with.
66 $token = Passport::token()->forceFill([
67 'client_id' => $params['client_id'] ?? null,
68 'revoked' => false,
69 'scopes' => $scopes,
70 ]);
71 $token->user()->associate(auth()->user());
72 $token->validate();
73
74 $params['scope'] = implode(' ', $scopes);
75
76 return $request->withQueryParams($params);
77 }
78
79 /**
80 * Normalizes and sorts scopes.
81 *
82 * @param array $scopes
83 * @return array
84 */
85 private function normalizeScopes(array $scopes): array
86 {
87 if (!in_array('identify', $scopes, true)) {
88 $scopes[] = 'identify';
89 }
90
91 sort($scopes);
92
93 return $scopes;
94 }
95}