Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
4 / 4
CRAP
100.00% covered (success)
100.00%
27 / 27
DefaultAuthorizationForm
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
4 / 4
6
100.00% covered (success)
100.00%
27 / 27
 __construct
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 showForm
100.00% covered (success)
100.00%
1 / 1
3
100.00% covered (success)
100.00%
18 / 18
 transformAuthorizationCode
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
3 / 3
 setLogger
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
2 / 2
1<?php declare(strict_types=1);
2
3namespace Taproot\IndieAuth\Callback;
4
5use BarnabyWalters\Mf2 as M;
6use Psr\Http\Message\ServerRequestInterface;
7use Nyholm\Psr7\Response;
8use Psr\Http\Message\ResponseInterface;
9use Psr\Log\LoggerAwareInterface;
10use Psr\Log\LoggerInterface;
11use Psr\Log\NullLogger;
12
13use function Taproot\IndieAuth\renderTemplate;
14
15/**
16 * Default Authorization Form
17 * 
18 * This implementation of `AuthorizationFormInterface` is used by `Server` if the user doesn’t 
19 * provide one of their own. It presents the user with a simple consent screen, showing any
20 * available details about the client app, and allowing the user to grant any requested scopes.
21 * 
22 * When the form is submitted, any granted scopes are then added to the authorization code data.
23 * 
24 * You can customise the authorization template used by passing a path to your own template to
25 * the constructor. Refer to the default template `/templates/default_authorization_page.html.php`
26 * as a starting point.
27 * 
28 * If you want to add additional form controls (e.g. configurable token lifetimes), as well as
29 * making a new template, you’ll need to make a subclass which overrides `transformAuthorizationCode()`
30 * to additionally handle your new form data.
31 * 
32 * For any more involved customisation (for example using a templating library of your choice), it
33 * may make sense to create your own implementation of `AuthorizationFormInterface`.
34 */
35class DefaultAuthorizationForm implements AuthorizationFormInterface, LoggerAwareInterface {
36    /** @var string $csrfKey */
37    public $csrfKey;
38
39    /** @var string $formTemplatePath */
40    public $formTemplatePath;
41
42    /** @var LoggerInterface $logger */
43    public $logger;
44
45    /**
46     * Constructor
47     * 
48     * @param string|null $formTemplatePath The path to a custom template. Uses the default if null.
49     * @param string|null $csrfKey The key used to retrieve a CSRF token from the request attributes, and as its form data name. Uses the default defined in Server if null. Only change this if you’re using a custom CSRF middleware.
50     * @param LoggerInterface|null $logger A logger.
51     */
52    public function __construct(?string $formTemplatePath=null, ?string $csrfKey=null, ?LoggerInterface $logger=null) {
53        $this->formTemplatePath = $formTemplatePath ?? __DIR__ . '/../../templates/default_authorization_page.html.php';
54        $this->csrfKey = $csrfKey ?? \Taproot\IndieAuth\Server::DEFAULT_CSRF_KEY;
55        $this->logger = $logger ?? new NullLogger;
56    }
57
58    public function showForm(ServerRequestInterface $request, array $authenticationResult, string $formAction, ?array $clientHApp): ResponseInterface {
59        // Show an authorization page. List all requested scopes, as this default
60        // function has now way of knowing which scopes are supported by the consumer.
61        $scopes = [];
62        foreach(explode(' ', $request->getQueryParams()['scope'] ?? '') as $s) {
63            $scopes[$s] = null; // Ideally there would be a description of the scope here, we don’t have one though.
64        }
65
66        if (is_null($clientHApp)) {
67            $clientHApp = [
68                'type' => ['h-app'],
69                'properties' => []
70            ];
71        }
72
73        $hApp = [
74            'name' => M\getProp($clientHApp, 'name'),
75            'url' => M\getProp($clientHApp, 'url'),
76            'photo' => M\getProp($clientHApp, 'photo')
77        ];
78
79        return new Response(200, ['content-type' => 'text/html'], renderTemplate($this->formTemplatePath, [
80            'scopes' => $scopes,
81            'user' => $authenticationResult,
82            'formAction' => $formAction,
83            'request' => $request,
84            'clientHApp' => $hApp,
85            'clientId' => $request->getQueryParams()['client_id'],
86            'clientRedirectUri' => $request->getQueryParams()['redirect_uri'],
87            'csrfFormElement' => '<input type="hidden" name="' . htmlentities($this->csrfKey) . '" value="' . htmlentities($request->getAttribute($this->csrfKey)) . '" />'
88        ]));
89    }
90
91    public function transformAuthorizationCode(ServerRequestInterface $request, array $code): array {
92        // Add any granted scopes from the form to the code.
93        $grantedScopes = $request->getParsedBody()['taproot_indieauth_server_scope'] ?? [];
94
95        // This default implementation naievely accepts any scopes it receives from the form.
96        // You may wish to perform some sort of validation.
97        $code['scope'] = join(' ', $grantedScopes);
98
99        // You may wish to additionally make any other necessary changes to the the code based on
100        // the form submission, e.g. if the user set a custom token lifetime, or wanted extra data
101        // stored on the token to affect how it behaves.
102        return $code;
103    }
104
105    public function setLogger(LoggerInterface $logger) {
106        $this->logger = $logger;
107    }
108}