Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | n/a |
0 / 0 |
n/a |
0 / 0 |
CRAP | n/a |
0 / 0 |
1 | <?php declare(strict_types=1); |
2 | |
3 | namespace 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 `Server::__construct`) and `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 | */ |
37 | interface 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 `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 `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 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 | * Additonally, it SHOULD contain the following keys: |
105 | * * `scope`, if the token grants any scope |
106 | * And MAY contain additional keys, such as: |
107 | * * `profile` |
108 | * * `expires_at` |
109 | * |
110 | * If the authorization code was redeemed at the authorization endpoint, Server will |
111 | * only pass the `me` and `profile` keys onto the client. In both cases, it will filter |
112 | * out `code_challenge` keys to prevent that data from accidentally being leaked to |
113 | * clients. If an access token is present, the server will add `token_type: Bearer` |
114 | * automatically. |
115 | * |
116 | * A typical implementation might look like this: |
117 | * |
118 | * ```php |
119 | * function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array { |
120 | * if (is_null($authCodeData = $this->fetchAuthCode($code))) { |
121 | * return null; |
122 | * } |
123 | * |
124 | * if (isExpired($authCodeData)) { |
125 | * return null; |
126 | * } |
127 | * |
128 | * try { |
129 | * $validateAuthCode($authCodeData); |
130 | * } catch (IndieAuthException $e) { |
131 | * $this->deleteAuthCode($code); |
132 | * throw $e; |
133 | * } |
134 | * |
135 | * return $this->newTokenFromAuthCodeData($authCodeData); |
136 | * } |
137 | * ``` |
138 | * |
139 | * Refer to reference implementations in the `Taproot\IndieAuth\Storage` namespace for |
140 | * reference. |
141 | * |
142 | * @param string $code The Authorization Code to attempt to exchange. |
143 | * @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. |
144 | * @return array|null An array of access token data to return to the client on success, null on any error. |
145 | */ |
146 | public function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array; |
147 | |
148 | /** |
149 | * Get Access Token |
150 | * |
151 | * Fetch access token data identified by the token `$token`, returning |
152 | * null if it is expired or invalid. |
153 | */ |
154 | public function getAccessToken(string $token): ?array; |
155 | |
156 | /** |
157 | * Revoke Access Token |
158 | * |
159 | * Revoke the access token identified by `$token`. Return true on success, |
160 | * or false on error, including if the token did not exist. |
161 | */ |
162 | public function revokeAccessToken(string $token): bool; |
163 | } |