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%
26 / 26
DefaultAuthorizationForm
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
4 / 4
6
100.00% covered (success)
100.00%
26 / 26
 __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%
17 / 17
 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    public string $csrfKey;
37
38    public string $formTemplatePath;
39
40    public LoggerInterface $logger;
41
42    /**
43     * Constructor
44     * 
45     * @param string|null $formTemplatePath The path to a custom template. Uses the default if null.
46     * @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.
47     * @param LoggerInterface|null $logger A logger.
48     */
49    public function __construct(?string $formTemplatePath=null, ?string $csrfKey=null, ?LoggerInterface $logger=null) {
50        $this->formTemplatePath = $formTemplatePath ?? __DIR__ . '/../../templates/default_authorization_page.html.php';
51        $this->csrfKey = $csrfKey ?? \Taproot\IndieAuth\Server::DEFAULT_CSRF_KEY;
52        $this->logger = $logger ?? new NullLogger;
53    }
54
55    public function showForm(ServerRequestInterface $request, array $authenticationResult, string $formAction, ?array $clientHApp): ResponseInterface {
56        // Show an authorization page. List all requested scopes, as this default
57        // function has now way of knowing which scopes are supported by the consumer.
58        $scopes = [];
59        foreach(explode(' ', $request->getQueryParams()['scope'] ?? '') as $s) {
60            $scopes[$s] = null; // Ideally there would be a description of the scope here, we don’t have one though.
61        }
62
63        if (is_null($clientHApp)) {
64            $clientHApp = [
65                'type' => ['h-app'],
66                'properties' => []
67            ];
68        }
69
70        $hApp = [
71            'name' => M\getProp($clientHApp, 'name'),
72            'url' => M\getProp($clientHApp, 'url'),
73            'photo' => M\getProp($clientHApp, 'photo')
74        ];
75
76        return new Response(200, ['content-type' => 'text/html'], renderTemplate($this->formTemplatePath, [
77            'scopes' => $scopes,
78            'user' => $authenticationResult,
79            'formAction' => $formAction,
80            'request' => $request,
81            'clientHApp' => $hApp,
82            'clientId' => $request->getQueryParams()['client_id'],
83            'clientRedirectUri' => $request->getQueryParams()['redirect_uri'],
84            'csrfFormElement' => '<input type="hidden" name="' . htmlentities($this->csrfKey) . '" value="' . htmlentities($request->getAttribute($this->csrfKey)) . '" />'
85        ]));
86    }
87
88    public function transformAuthorizationCode(ServerRequestInterface $request, array $code): array {
89        // Add any granted scopes from the form to the code.
90        $grantedScopes = $request->getParsedBody()['taproot_indieauth_server_scope[]'] ?? [];
91
92        // This default implementation naievely accepts any scopes it receives from the form.
93        // You may wish to perform some sort of validation.
94        $code['scope'] = join(' ', $grantedScopes);
95
96        // You may wish to additionally make any other necessary changes to the the code based on
97        // the form submission, e.g. if the user set a custom token lifetime, or wanted extra data
98        // stored on the token to affect how it behaves.
99        return $code;
100    }
101
102    public function setLogger(LoggerInterface $logger) {
103        $this->logger = $logger;
104    }
105}