Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
n/a
0 / 0
n/a
0 / 0
CRAP
n/a
0 / 0
1<?php declare(strict_types=1);
2
3namespace Taproot\IndieAuth\Storage;
4
5/**
6 * Token Storage Interface
7 * 
8 * This interface defines the bare minimum methods required by the Server class in order to 
9 * implement auth code issuing and exchange flows, as well as to let external code get access
10 * tokens (for validating requests authenticated by an access_token) and revoke access tokens.
11 * 
12 * The contract made between Server and implementations of TokenStorageInterface can broadly
13 * be summarized as follows:
14 * 
15 * * The Server class is responsible for performing all validation which is
16 *   defined in the IndieAuth spec and is not implementation-specific. For example: checking
17 *   validity of all the authorization request parameters, checking that client_id, request_uri
18 *   and code_verifier parameters in token exchange requests match with the stored data.
19 * * The TokenStorageInterface class is responsible for performing implementation-specific
20 *   validation, such as assigning and checking expiry times for auth codes and access tokens.
21 * 
22 * Implementations of TokenStorageInterface will usually implement additional methods to allow
23 * for lower-level querying, saving, updating and deletion of token data. These can be used to,
24 * for example, implement a UI for users to review and revoke currently valid access tokens.
25 * 
26 * The behaviour of `TokenStorageInterface` is somewhat coupled with the implementation of your
27 * authentication handler callback (documented in {@see Server::__construct()}) and {@see AuthorizationFormInterface},
28 * so you should refer to the documentation for both while implementing `TokenStorageInterface`.
29 * 
30 * Periodic deletion of expired tokens is out of the scope of this interface. Implementations may
31 * choose to offer a clean-up method, and potentially the option to call it once automatically 
32 * on instantiation.
33 * 
34 * None of the methods defined on TokenStorageInterface should throw exceptions. Failure, for any
35 * reason, is indicated by returning either `null` or `false`, depending on the method.
36 */
37interface TokenStorageInterface {
38    /**
39     * Create Authorization Code
40     * 
41     * This method is called on a valid authorization token request. The `$data`
42     * array is guaranteed to have the following keys:
43     * 
44     * * `client_id`: the validated `client_id` request parameter
45     * * `redirect_uri`: the validated `redirect_uri` request parameter
46     * * `state`: the `state` request parameter
47     * * `code_challenge`: the `code_challenge` request parameter
48     * * `code_challenge_method`: the `code_challenge_method` request parameter
49     * * `requested_scope`: the value of the `scope` request parameter
50     * * `me`: the value of the `me` key from the authentication result returned from the authentication request handler callback
51     * 
52     * It may also have additional keys, which can come from the following locations:
53     * 
54     * * All keys from the the authentication request handler callback result which do not clash 
55     *   with the keys listed above (with the exception of `me`, which is always present). Usually
56     *   this is a `profile` key, but you may choose to return additional data from the authentication
57     *   callback, which will be present in `$data`.
58     * * Any keys added by the `transformAuthorizationCode` method on the currently active instance
59     *   of {@see Taproot\IndieAuth\Callback\AuthorizationFormInterface}. Typically this is the `scope`
60     *   key, which is a valid space-separated scope string listing the scopes granted by the user on
61     *   the consent screen. Other implementations of `AuthorizationFormInterface` may add additional 
62     *   data, such as custom token-specific settings, or a custom token lifetime.
63     * 
64     * This method should store the data passed to it, generate a corresponding authorization code
65     * string, and return it.
66     * 
67     * The method call and data is structured such that implementations have a lot of flexibility
68     * about how to store authorization code data. It could be a record in an auth code database
69     * table, a record in a table which is used for both auth codes and access tokens, or even
70     * a stateless self-encrypted token — note that in the latter case, you must persist a copy
71     * of the auth code with its exchanged access token to check against, in order to prevent it 
72     * being exchanged more than once.
73     * 
74     * On an error, return null. The reason for the error is irrelevant for calling code, but it’s
75     * recommended to log it internally for reference. For the same reason, this method should not 
76     * throw exceptions.
77     */
78    public function createAuthCode(array $data): ?string;
79
80    /**
81     * Exchange Authorization Code for Access Token
82     * 
83     * Attempt to exchange an authorization code identified by `$code` for
84     * an access token. Return an array of access token data to be passed onto
85     * the client app on success, and null on error.
86     * 
87     * This method is called at the beginning of a code exchange request, before
88     * further error checking or validation is applied. It should proceed as
89     * follows.
90     * 
91     * * Attempt to fetch the authorization code data identified by $code. If
92     *   it does not exist or has expired, return null;
93     * * Pass the authorization code data array to $validateAuthCode for validation.
94     *   If there is a problem with the code, a {@see \Taproot\IndieAuth\IndieAuthException}
95     *   will be thrown. This method should catch it, invalidate the authorization
96     *   code data, then re-throw the exception for handling by the Server.
97     * * If the authorization code data passed all checks, convert it into an access
98     *   token, invalidate the auth code to prevent re-use, and store the access token
99     *   data internally.
100     * * Return an array of access token data to be passed onto the client app. It MUST
101     *   contain the following keys:
102     *     * `me`
103     *     * `access_token`
104     *   
105     *   Additonally, it SHOULD contain the following keys:
106     *     * `scope`, if the token grants any scope
107     *   
108     *   And MAY contain additional keys, such as:
109     *     * `profile`
110     *     * `expires_at`
111     * 
112     * If the authorization code was redeemed at the authorization endpoint, Server will
113     * only pass the `me` and `profile` keys onto the client. In both cases, it will filter
114     * out `code_challenge` keys to prevent that data from accidentally being leaked to
115     * clients. If an access token is present, the server will add `token_type: Bearer`
116     * automatically.
117     * 
118     * A typical implementation might look like this (where `isExpired` and the various methods
119     * on `$this` are provided by your implementation):
120     * 
121     * ```php
122     * function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array {
123     *   if (is_null($authCodeData = $this->fetchAuthCode($code))) {
124     *     return null;
125     *   }
126     *   
127     *   if (isExpired($authCodeData)) {
128     *     return null;
129     *   }
130     *   
131     *   try {
132     *     $validateAuthCode($authCodeData);
133     *   } catch (IndieAuthException $e) {
134     *     $this->deleteAuthCode($code);
135     *     throw $e;
136     *   }
137     *   
138     *   return $this->newTokenFromAuthCodeData($authCodeData);
139     * }
140     * ```
141     * 
142     * Refer to reference implementations in the `Taproot\IndieAuth\Storage` namespace for
143     * reference.
144     * 
145     * @param string $code The Authorization Code to attempt to exchange.
146     * @param callable $validateAuthCode A callable to perform additional validation if valid auth code data is found. Takes `array $authCodeData`, raises `Taproot\IndieAuth\IndieAuthException` on invalid data, which should be bubbled up to the caller after any clean-up. Returns void.
147     * @return array|null An array of access token data to return to the client on success, null on any error.
148     */
149    public function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array;
150
151    /**
152     * Get Access Token
153     * 
154     * Fetch access token data identified by the token `$token`, returning 
155     * null if it is expired or invalid.
156     */
157    public function getAccessToken(string $token): ?array;
158
159    /**
160     * Revoke Access Token
161     * 
162     * Revoke the access token identified by `$token`. Return true on success,
163     * or false on error, including if the token did not exist.
164     */
165    public function revokeAccessToken(string $token): bool;
166}