Refactored TokenStorageInterface for an improved flow
* Now passing an auth code data validation callback to the exchange method * Removed Token, it’s no longer necessary * Simplified interface where possible * All tests passing * Updated docblocks
This commit is contained in:
parent
645ab833c5
commit
61aa7f55f9
@ -148,6 +148,8 @@
|
||||
self::INVALID_STATE => ['statusCode' => 302, 'name' => 'Invalid state Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_CODE_CHALLENGE => ['statusCode' => 302, 'name' => 'Invalid code_challenge Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_SCOPE => ['statusCode' => 302, 'name' => 'Invalid scope Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_GRANT => ['statusCode' => 400, 'name' => 'The provided credentials were not valid.', 'error' => 'invalid_grant'],
|
||||
self::INVALID_REQUEST => ['statusCode' => 400, 'name' => 'Invalid Request', 'error' => 'invalid_request'],
|
||||
] </span>
|
||||
</dt>
|
||||
<dd></dd>
|
||||
@ -192,6 +194,13 @@
|
||||
<span>
|
||||
= 10 </span>
|
||||
</dt>
|
||||
<dd></dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -constant -public">
|
||||
<a href="classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_GRANT">INVALID_GRANT</a>
|
||||
<span>
|
||||
= 12 </span>
|
||||
</dt>
|
||||
<dd></dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -constant -public">
|
||||
@ -199,6 +208,13 @@
|
||||
<span>
|
||||
= 7 </span>
|
||||
</dt>
|
||||
<dd></dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -constant -public">
|
||||
<a href="classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_REQUEST">INVALID_REQUEST</a>
|
||||
<span>
|
||||
= 13 </span>
|
||||
</dt>
|
||||
<dd></dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -constant -public">
|
||||
@ -358,7 +374,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">23</span>
|
||||
<span class="phpdocumentor-element-found-in__line">25</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -380,6 +396,8 @@
|
||||
self::INVALID_STATE => ['statusCode' => 302, 'name' => 'Invalid state Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_CODE_CHALLENGE => ['statusCode' => 302, 'name' => 'Invalid code_challenge Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_SCOPE => ['statusCode' => 302, 'name' => 'Invalid scope Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_GRANT => ['statusCode' => 400, 'name' => 'The provided credentials were not valid.', 'error' => 'invalid_grant'],
|
||||
self::INVALID_REQUEST => ['statusCode' => 400, 'name' => 'Invalid Request', 'error' => 'invalid_request'],
|
||||
]</span>
|
||||
</code>
|
||||
|
||||
@ -537,6 +555,31 @@
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
<article class="phpdocumentor-element -constant -public ">
|
||||
<h4 class="phpdocumentor-element__name" id="constant_INVALID_GRANT">
|
||||
INVALID_GRANT
|
||||
<a href="classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_GRANT" class="headerlink"><i class="fas fa-link"></i></a>
|
||||
</h4>
|
||||
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">22</span>
|
||||
</aside>
|
||||
|
||||
|
||||
<code class="phpdocumentor-signature phpdocumentor-code ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__type">mixed</span>
|
||||
<span class="phpdocumentor-signature__name">INVALID_GRANT</span>
|
||||
= <span class="phpdocumentor-signature__default-value">12</span>
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
<article class="phpdocumentor-element -constant -public ">
|
||||
<h4 class="phpdocumentor-element__name" id="constant_INVALID_REDIRECT_URI">
|
||||
@ -562,6 +605,31 @@
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
<article class="phpdocumentor-element -constant -public ">
|
||||
<h4 class="phpdocumentor-element__name" id="constant_INVALID_REQUEST">
|
||||
INVALID_REQUEST
|
||||
<a href="classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_REQUEST" class="headerlink"><i class="fas fa-link"></i></a>
|
||||
</h4>
|
||||
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">23</span>
|
||||
</aside>
|
||||
|
||||
|
||||
<code class="phpdocumentor-signature phpdocumentor-code ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__type">mixed</span>
|
||||
<span class="phpdocumentor-signature__name">INVALID_REQUEST</span>
|
||||
= <span class="phpdocumentor-signature__default-value">13</span>
|
||||
</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</article>
|
||||
<article class="phpdocumentor-element -constant -public ">
|
||||
<h4 class="phpdocumentor-element__name" id="constant_INVALID_SCOPE">
|
||||
@ -637,7 +705,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">39</span>
|
||||
<span class="phpdocumentor-element-found-in__line">43</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -671,7 +739,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">41</span>
|
||||
<span class="phpdocumentor-element-found-in__line">45</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -727,7 +795,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">56</span>
|
||||
<span class="phpdocumentor-element-found-in__line">60</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -759,7 +827,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">60</span>
|
||||
<span class="phpdocumentor-element-found-in__line">64</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -791,7 +859,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">76</span>
|
||||
<span class="phpdocumentor-element-found-in__line">80</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -823,7 +891,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">52</span>
|
||||
<span class="phpdocumentor-element-found-in__line">56</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -855,7 +923,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/IndieAuthException.php"><a href="files/src-indieauthexception.html"><abbr title="src/IndieAuthException.php">IndieAuthException.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">71</span>
|
||||
<span class="phpdocumentor-element-found-in__line">75</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Trust Query Params</p>
|
||||
|
@ -1072,7 +1072,7 @@ error behaviour, one way to do so is to subclass <code class="prettyprint">Serve
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Server.php"><a href="files/src-server.html"><abbr title="src/Server.php">Server.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">769</span>
|
||||
<span class="phpdocumentor-element-found-in__line">771</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Handle Exception</p>
|
||||
|
@ -93,7 +93,7 @@
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">25</span>
|
||||
<span class="phpdocumentor-element-found-in__line">26</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Filesystem JSON Token Storage</p>
|
||||
@ -199,7 +199,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-FilesystemJsonStorage.html#method_createAuthCode">createAuthCode()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: string|null </span>
|
||||
</dt>
|
||||
<dd>Create Auth Code</dd>
|
||||
|
||||
@ -220,7 +220,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-FilesystemJsonStorage.html#method_exchangeAuthCodeForAccessToken">exchangeAuthCodeForAccessToken()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: array<string|int, mixed>|null </span>
|
||||
</dt>
|
||||
<dd>Exchange Authorization Code for Access Token</dd>
|
||||
|
||||
@ -234,7 +234,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-FilesystemJsonStorage.html#method_getAccessToken">getAccessToken()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: array<string|int, mixed>|null </span>
|
||||
</dt>
|
||||
<dd>Get Access Token</dd>
|
||||
|
||||
@ -299,7 +299,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">27</span>
|
||||
<span class="phpdocumentor-element-found-in__line">28</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -324,7 +324,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">26</span>
|
||||
<span class="phpdocumentor-element-found-in__line">27</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -349,7 +349,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">29</span>
|
||||
<span class="phpdocumentor-element-found-in__line">30</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -389,7 +389,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">33</span>
|
||||
<span class="phpdocumentor-element-found-in__line">34</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -419,7 +419,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">32</span>
|
||||
<span class="phpdocumentor-element-found-in__line">33</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -449,7 +449,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">36</span>
|
||||
<span class="phpdocumentor-element-found-in__line">37</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -479,7 +479,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">31</span>
|
||||
<span class="phpdocumentor-element-found-in__line">32</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -509,7 +509,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">34</span>
|
||||
<span class="phpdocumentor-element-found-in__line">35</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -543,7 +543,7 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">39</span>
|
||||
<span class="phpdocumentor-element-found-in__line">40</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -620,14 +620,14 @@ will likely be superceded by the SQLite version.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">67</span>
|
||||
<span class="phpdocumentor-element-found-in__line">68</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Create Auth Code</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">createAuthCode</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">array<string|int, mixed> </span><span class="phpdocumentor-signature__argument__name">$data</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">createAuthCode</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">array<string|int, mixed> </span><span class="phpdocumentor-signature__argument__name">$data</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">string|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>This method is called on a valid authorization token request. The <code class="prettyprint">$data</code>
|
||||
array is guaranteed to have the following keys:</p>
|
||||
@ -659,9 +659,8 @@ key, which is a valid space-separated scope string listing the scopes granted by
|
||||
the consent screen. Other implementations of <code class="prettyprint">AuthorizationFormInterface</code> may add additional
|
||||
data, such as custom token-specific settings, or a custom token lifetime.</li>
|
||||
</ul>
|
||||
<p>This method should store the data passed to it, generate a corresponding authorization code,
|
||||
and return an instance of <code class="prettyprint">Storage\Token</code> containing both. Implementations will usually add
|
||||
an expiry time, usually under the <code class="prettyprint">valid_until</code> key.</p>
|
||||
<p>This method should store the data passed to it, generate a corresponding authorization code
|
||||
string, and return it.</p>
|
||||
<p>The method call and data is structured such that implementations have a lot of flexibility
|
||||
about how to store authorization code data. It could be a record in an auth code database
|
||||
table, a record in a table which is used for both auth codes and access tokens, or even
|
||||
@ -687,7 +686,7 @@ throw exceptions.</p>
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">string|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
|
||||
@ -706,13 +705,13 @@ throw exceptions.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">208</span>
|
||||
<span class="phpdocumentor-element-found-in__line">228</span>
|
||||
</aside>
|
||||
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">delete</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$key</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">bool</span></code>
|
||||
<span class="phpdocumentor-signature__name">delete</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$key</span></span><span class="phpdocumentor-signature__argument"><span>[</span><span>, </span><span class="phpdocumentor-signature__argument__return-type">mixed </span><span class="phpdocumentor-signature__argument__name">$observeLock</span><span> = </span><span class="phpdocumentor-signature__argument__default-value">true</span><span> ]</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">bool</span></code>
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-argument-list__heading">Parameters</h5>
|
||||
@ -723,6 +722,13 @@ throw exceptions.</p>
|
||||
</dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
|
||||
</dd>
|
||||
<dt class="phpdocumentor-argument-list__entry">
|
||||
<span class="phpdocumentor-signature__argument__name">$observeLock</span>
|
||||
: <span class="phpdocumentor-signature__argument__return-type">mixed</span>
|
||||
= <span class="phpdocumentor-signature__argument__default-value">true</span> </dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
@ -748,7 +754,7 @@ throw exceptions.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">149</span>
|
||||
<span class="phpdocumentor-element-found-in__line">169</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -780,29 +786,56 @@ throw exceptions.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">81</span>
|
||||
<span class="phpdocumentor-element-found-in__line">82</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Exchange Authorization Code for Access Token</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">exchangeAuthCodeForAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$code</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">exchangeAuthCodeForAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$code</span></span><span class="phpdocumentor-signature__argument"><span>, </span><span class="phpdocumentor-signature__argument__return-type">callable </span><span class="phpdocumentor-signature__argument__name">$validateAuthCode</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>Attempt to exchange an authorization code identified by <code class="prettyprint">$code</code> for
|
||||
an access token, returning it in a <code class="prettyprint">Token</code> on success and null on error.</p>
|
||||
an access token. Return an array of access token data to be passed onto
|
||||
the client app on success, and null on error.</p>
|
||||
<p>This method is called at the beginning of a code exchange request, before
|
||||
further error checking or validation is applied. On an error, the created
|
||||
access token is immediately revoked via <code class="prettyprint">revokeAccessToken()</code>.</p>
|
||||
<p>For this reason, the token data in the returned Token object MUST include
|
||||
the <code class="prettyprint">client_id</code> and <code class="prettyprint">redirect_uri</code> parameters associated with the
|
||||
authorization code, as these are used by the IndieAuth Server for further
|
||||
validation.</p>
|
||||
<p>This method is responsible for ensuring that the matched auth code is
|
||||
not expired. If it is, it should return null, presumably after deleting
|
||||
the corresponding authorization code record.</p>
|
||||
<p>If the exchanged access token should expire, this method should set its
|
||||
expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
further error checking or validation is applied. It should proceed as
|
||||
follows.</p>
|
||||
<ul>
|
||||
<li>Attempt to fetch the authorization code data identified by $code. If
|
||||
it does not exist or has expired, return null;</li>
|
||||
<li>Pass the authorization code data array to $validateAuthCode for validation.
|
||||
If there is a problem with the code, a <code class="prettyprint">Taproot\IndieAuth\IndieAuthException</code>
|
||||
will be thrown. This method should catch it, invalidate the authorization
|
||||
code data, then re-throw the exception for handling by Server.</li>
|
||||
<li>If the authorization code data passed all checks, convert it into an access
|
||||
token, invalidate the auth code to prevent re-use, and store the access token
|
||||
data internally.</li>
|
||||
<li>Return an array of access token data to be passed onto the client app. It MUST
|
||||
contain the following keys:
|
||||
<ul>
|
||||
<li>
|
||||
<code class="prettyprint">me</code>
|
||||
</li>
|
||||
<li>
|
||||
<code class="prettyprint">access_token</code>
|
||||
Additonally, it SHOULD contain the following keys:</li>
|
||||
<li>
|
||||
<code class="prettyprint">scope</code>, if the token grants any scope
|
||||
And MAY contain additional keys, such as:</li>
|
||||
<li>
|
||||
<code class="prettyprint">profile</code>
|
||||
</li>
|
||||
<li>
|
||||
<code class="prettyprint">expires_at</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>If the authorization code was redeemed at the authorization endpoint, Server will
|
||||
only pass the <code class="prettyprint">me</code> and <code class="prettyprint">profile</code> keys onto the client. In both cases, it will filter
|
||||
out <code class="prettyprint">code_challenge</code> keys to prevent that data from accidentally being leaked to
|
||||
clients.</p>
|
||||
</section>
|
||||
|
||||
<h5 class="phpdocumentor-argument-list__heading">Parameters</h5>
|
||||
@ -812,16 +845,28 @@ expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
: <span class="phpdocumentor-signature__argument__return-type">string</span>
|
||||
</dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
|
||||
<section class="phpdocumentor-description"><p>The Authorization Code to attempt to exchange.</p>
|
||||
</section>
|
||||
|
||||
</dd>
|
||||
<dt class="phpdocumentor-argument-list__entry">
|
||||
<span class="phpdocumentor-signature__argument__name">$validateAuthCode</span>
|
||||
: <span class="phpdocumentor-signature__argument__return-type">callable</span>
|
||||
</dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
<section class="phpdocumentor-description"><p>A callable to perform additional validation if valid auth code data is found. Takes <code class="prettyprint">array $authCodeData</code>, raises <code class="prettyprint">Taproot\IndieAuth\IndieAuthException</code> on invalid data, which should be bubbled up to the caller after any clean-up. Returns void.</p>
|
||||
</section>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
<section class="phpdocumentor-description"><p>An array of access token data to return to the client on success, null on any error.</p>
|
||||
</section>
|
||||
|
||||
|
||||
</article>
|
||||
@ -838,7 +883,7 @@ expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">177</span>
|
||||
<span class="phpdocumentor-element-found-in__line">197</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -880,18 +925,17 @@ expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">127</span>
|
||||
<span class="phpdocumentor-element-found-in__line">147</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Get Access Token</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">getAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$token</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">getAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$token</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>Fetch access token data identified by the token <code class="prettyprint">$token</code>, returning
|
||||
null if it is expired or invalid. The data should be structured in
|
||||
exactly the same way it was stored by <code class="prettyprint">exchangeAuthCodeForAccessToken</code>.</p>
|
||||
null if it is expired or invalid.</p>
|
||||
</section>
|
||||
|
||||
<h5 class="phpdocumentor-argument-list__heading">Parameters</h5>
|
||||
@ -908,7 +952,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
|
||||
@ -927,7 +971,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">218</span>
|
||||
<span class="phpdocumentor-element-found-in__line">242</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -969,7 +1013,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">199</span>
|
||||
<span class="phpdocumentor-element-found-in__line">219</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -1018,7 +1062,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">143</span>
|
||||
<span class="phpdocumentor-element-found-in__line">163</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Revoke Access Token</p>
|
||||
@ -1064,7 +1108,7 @@ or false on error, including if the token did not exist.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">61</span>
|
||||
<span class="phpdocumentor-element-found-in__line">62</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -1106,7 +1150,7 @@ or false on error, including if the token did not exist.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">247</span>
|
||||
<span class="phpdocumentor-element-found-in__line">271</span>
|
||||
</aside>
|
||||
|
||||
|
||||
@ -1148,7 +1192,7 @@ or false on error, including if the token did not exist.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/FilesystemJsonStorage.php"><a href="files/src-storage-filesystemjsonstorage.html"><abbr title="src/Storage/FilesystemJsonStorage.php">FilesystemJsonStorage.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">223</span>
|
||||
<span class="phpdocumentor-element-found-in__line">247</span>
|
||||
</aside>
|
||||
|
||||
|
||||
|
@ -133,21 +133,21 @@ reason, is indicated by returning either <code class="prettyprint">null</code> o
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-TokenStorageInterface.html#method_createAuthCode">createAuthCode()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: string|null </span>
|
||||
</dt>
|
||||
<dd>Create Auth Code</dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-TokenStorageInterface.html#method_exchangeAuthCodeForAccessToken">exchangeAuthCodeForAccessToken()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: array<string|int, mixed>|null </span>
|
||||
</dt>
|
||||
<dd>Exchange Authorization Code for Access Token</dd>
|
||||
|
||||
<dt class="phpdocumentor-table-of-contents__entry -method -public">
|
||||
<a href="classes/Taproot-IndieAuth-Storage-TokenStorageInterface.html#method_getAccessToken">getAccessToken()</a>
|
||||
<span>
|
||||
: <a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null </span>
|
||||
: array<string|int, mixed>|null </span>
|
||||
</dt>
|
||||
<dd>Get Access Token</dd>
|
||||
|
||||
@ -182,14 +182,14 @@ reason, is indicated by returning either <code class="prettyprint">null</code> o
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/TokenStorageInterface.php"><a href="files/src-storage-tokenstorageinterface.html"><abbr title="src/Storage/TokenStorageInterface.php">TokenStorageInterface.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">79</span>
|
||||
<span class="phpdocumentor-element-found-in__line">78</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Create Auth Code</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">createAuthCode</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">array<string|int, mixed> </span><span class="phpdocumentor-signature__argument__name">$data</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">createAuthCode</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">array<string|int, mixed> </span><span class="phpdocumentor-signature__argument__name">$data</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">string|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>This method is called on a valid authorization token request. The <code class="prettyprint">$data</code>
|
||||
array is guaranteed to have the following keys:</p>
|
||||
@ -221,9 +221,8 @@ key, which is a valid space-separated scope string listing the scopes granted by
|
||||
the consent screen. Other implementations of <code class="prettyprint">AuthorizationFormInterface</code> may add additional
|
||||
data, such as custom token-specific settings, or a custom token lifetime.</li>
|
||||
</ul>
|
||||
<p>This method should store the data passed to it, generate a corresponding authorization code,
|
||||
and return an instance of <code class="prettyprint">Storage\Token</code> containing both. Implementations will usually add
|
||||
an expiry time, usually under the <code class="prettyprint">valid_until</code> key.</p>
|
||||
<p>This method should store the data passed to it, generate a corresponding authorization code
|
||||
string, and return it.</p>
|
||||
<p>The method call and data is structured such that implementations have a lot of flexibility
|
||||
about how to store authorization code data. It could be a record in an auth code database
|
||||
table, a record in a table which is used for both auth codes and access tokens, or even
|
||||
@ -249,7 +248,7 @@ throw exceptions.</p>
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">string|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
|
||||
@ -268,29 +267,56 @@ throw exceptions.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/TokenStorageInterface.php"><a href="files/src-storage-tokenstorageinterface.html"><abbr title="src/Storage/TokenStorageInterface.php">TokenStorageInterface.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">103</span>
|
||||
<span class="phpdocumentor-element-found-in__line">119</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Exchange Authorization Code for Access Token</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">exchangeAuthCodeForAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$code</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">exchangeAuthCodeForAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$code</span></span><span class="phpdocumentor-signature__argument"><span>, </span><span class="phpdocumentor-signature__argument__return-type">callable </span><span class="phpdocumentor-signature__argument__name">$validateAuthCode</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>Attempt to exchange an authorization code identified by <code class="prettyprint">$code</code> for
|
||||
an access token, returning it in a <code class="prettyprint">Token</code> on success and null on error.</p>
|
||||
an access token. Return an array of access token data to be passed onto
|
||||
the client app on success, and null on error.</p>
|
||||
<p>This method is called at the beginning of a code exchange request, before
|
||||
further error checking or validation is applied. On an error, the created
|
||||
access token is immediately revoked via <code class="prettyprint">revokeAccessToken()</code>.</p>
|
||||
<p>For this reason, the token data in the returned Token object MUST include
|
||||
the <code class="prettyprint">client_id</code> and <code class="prettyprint">redirect_uri</code> parameters associated with the
|
||||
authorization code, as these are used by the IndieAuth Server for further
|
||||
validation.</p>
|
||||
<p>This method is responsible for ensuring that the matched auth code is
|
||||
not expired. If it is, it should return null, presumably after deleting
|
||||
the corresponding authorization code record.</p>
|
||||
<p>If the exchanged access token should expire, this method should set its
|
||||
expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
further error checking or validation is applied. It should proceed as
|
||||
follows.</p>
|
||||
<ul>
|
||||
<li>Attempt to fetch the authorization code data identified by $code. If
|
||||
it does not exist or has expired, return null;</li>
|
||||
<li>Pass the authorization code data array to $validateAuthCode for validation.
|
||||
If there is a problem with the code, a <code class="prettyprint">Taproot\IndieAuth\IndieAuthException</code>
|
||||
will be thrown. This method should catch it, invalidate the authorization
|
||||
code data, then re-throw the exception for handling by Server.</li>
|
||||
<li>If the authorization code data passed all checks, convert it into an access
|
||||
token, invalidate the auth code to prevent re-use, and store the access token
|
||||
data internally.</li>
|
||||
<li>Return an array of access token data to be passed onto the client app. It MUST
|
||||
contain the following keys:
|
||||
<ul>
|
||||
<li>
|
||||
<code class="prettyprint">me</code>
|
||||
</li>
|
||||
<li>
|
||||
<code class="prettyprint">access_token</code>
|
||||
Additonally, it SHOULD contain the following keys:</li>
|
||||
<li>
|
||||
<code class="prettyprint">scope</code>, if the token grants any scope
|
||||
And MAY contain additional keys, such as:</li>
|
||||
<li>
|
||||
<code class="prettyprint">profile</code>
|
||||
</li>
|
||||
<li>
|
||||
<code class="prettyprint">expires_at</code>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<p>If the authorization code was redeemed at the authorization endpoint, Server will
|
||||
only pass the <code class="prettyprint">me</code> and <code class="prettyprint">profile</code> keys onto the client. In both cases, it will filter
|
||||
out <code class="prettyprint">code_challenge</code> keys to prevent that data from accidentally being leaked to
|
||||
clients.</p>
|
||||
</section>
|
||||
|
||||
<h5 class="phpdocumentor-argument-list__heading">Parameters</h5>
|
||||
@ -300,16 +326,28 @@ expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
: <span class="phpdocumentor-signature__argument__return-type">string</span>
|
||||
</dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
|
||||
<section class="phpdocumentor-description"><p>The Authorization Code to attempt to exchange.</p>
|
||||
</section>
|
||||
|
||||
</dd>
|
||||
<dt class="phpdocumentor-argument-list__entry">
|
||||
<span class="phpdocumentor-signature__argument__name">$validateAuthCode</span>
|
||||
: <span class="phpdocumentor-signature__argument__return-type">callable</span>
|
||||
</dt>
|
||||
<dd class="phpdocumentor-argument-list__definition">
|
||||
<section class="phpdocumentor-description"><p>A callable to perform additional validation if valid auth code data is found. Takes <code class="prettyprint">array $authCodeData</code>, raises <code class="prettyprint">Taproot\IndieAuth\IndieAuthException</code> on invalid data, which should be bubbled up to the caller after any clean-up. Returns void.</p>
|
||||
</section>
|
||||
|
||||
</dd>
|
||||
</dl>
|
||||
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
<section class="phpdocumentor-description"><p>An array of access token data to return to the client on success, null on any error.</p>
|
||||
</section>
|
||||
|
||||
|
||||
</article>
|
||||
@ -326,18 +364,17 @@ expiry time, usually in a <code class="prettyprint">valid_until</code> key.</p>
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/TokenStorageInterface.php"><a href="files/src-storage-tokenstorageinterface.html"><abbr title="src/Storage/TokenStorageInterface.php">TokenStorageInterface.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">112</span>
|
||||
<span class="phpdocumentor-element-found-in__line">127</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Get Access Token</p>
|
||||
|
||||
<code class="phpdocumentor-code phpdocumentor-signature ">
|
||||
<span class="phpdocumentor-signature__visibility">public</span>
|
||||
<span class="phpdocumentor-signature__name">getAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$token</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span></code>
|
||||
<span class="phpdocumentor-signature__name">getAccessToken</span><span>(</span><span class="phpdocumentor-signature__argument"><span class="phpdocumentor-signature__argument__return-type">string </span><span class="phpdocumentor-signature__argument__name">$token</span></span><span>)</span><span> : </span><span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span></code>
|
||||
|
||||
<section class="phpdocumentor-description"><p>Fetch access token data identified by the token <code class="prettyprint">$token</code>, returning
|
||||
null if it is expired or invalid. The data should be structured in
|
||||
exactly the same way it was stored by <code class="prettyprint">exchangeAuthCodeForAccessToken</code>.</p>
|
||||
null if it is expired or invalid.</p>
|
||||
</section>
|
||||
|
||||
<h5 class="phpdocumentor-argument-list__heading">Parameters</h5>
|
||||
@ -354,7 +391,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
|
||||
|
||||
<h5 class="phpdocumentor-return-value__heading">Return values</h5>
|
||||
<span class="phpdocumentor-signature__response_type"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a>|null</span>
|
||||
<span class="phpdocumentor-signature__response_type">array<string|int, mixed>|null</span>
|
||||
—
|
||||
<section class="phpdocumentor-description"></section>
|
||||
|
||||
@ -373,7 +410,7 @@ exactly the same way it was stored by <code class="prettyprint">exchangeAuthCode
|
||||
<aside class="phpdocumentor-element-found-in">
|
||||
<abbr class="phpdocumentor-element-found-in__file" title="src/Storage/TokenStorageInterface.php"><a href="files/src-storage-tokenstorageinterface.html"><abbr title="src/Storage/TokenStorageInterface.php">TokenStorageInterface.php</abbr></a></abbr>
|
||||
:
|
||||
<span class="phpdocumentor-element-found-in__line">120</span>
|
||||
<span class="phpdocumentor-element-found-in__line">135</span>
|
||||
</aside>
|
||||
|
||||
<p class="phpdocumentor-summary">Revoke Access Token</p>
|
||||
|
@ -112,7 +112,6 @@
|
||||
</ul>
|
||||
<h3>T</h3>
|
||||
<ul class="phpdocumentor-list">
|
||||
<li><a href="files/src-storage-token.html"><abbr title="src/Storage/Token.php">Token.php</abbr></a></li>
|
||||
<li><a href="files/src-storage-tokenstorageinterface.html"><abbr title="src/Storage/TokenStorageInterface.php">TokenStorageInterface.php</abbr></a></li>
|
||||
</ul>
|
||||
<section data-search-results class="phpdocumentor-search-results phpdocumentor-search-results--hidden">
|
||||
|
@ -280,6 +280,16 @@ Search.appendIndex(
|
||||
"name": "INVALID_SCOPE",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_SCOPE"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\IndieAuthException\u003A\u003AINVALID_GRANT",
|
||||
"name": "INVALID_GRANT",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_GRANT"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\IndieAuthException\u003A\u003AINVALID_REQUEST",
|
||||
"name": "INVALID_REQUEST",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-IndieAuthException.html#constant_INVALID_REQUEST"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\IndieAuthException\u003A\u003AEXC_INFO",
|
||||
"name": "EXC_INFO",
|
||||
@ -640,46 +650,6 @@ Search.appendIndex(
|
||||
"name": "Sqlite3Storage",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Sqlite3Storage.html"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token",
|
||||
"name": "Token",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003A__construct\u0028\u0029",
|
||||
"name": "__construct",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#method___construct"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003AgetData\u0028\u0029",
|
||||
"name": "getData",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#method_getData"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003AgetKey\u0028\u0029",
|
||||
"name": "getKey",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#method_getKey"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003A__toString\u0028\u0029",
|
||||
"name": "__toString",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#method___toString"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003AjsonSerialize\u0028\u0029",
|
||||
"name": "jsonSerialize",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#method_jsonSerialize"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003A\u0024key",
|
||||
"name": "key",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#property_key"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\Token\u003A\u003A\u0024data",
|
||||
"name": "data",
|
||||
"summary": "",
|
||||
"url": "classes/Taproot-IndieAuth-Storage-Token.html#property_data"
|
||||
}, {
|
||||
"fqsen": "\\Taproot\\IndieAuth\\Storage\\TokenStorageInterface",
|
||||
"name": "TokenStorageInterface",
|
||||
|
@ -95,8 +95,6 @@
|
||||
<dd>Filesystem JSON Token Storage</dd>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -class"><a href="classes/Taproot-IndieAuth-Storage-Sqlite3Storage.html"><abbr title="\Taproot\IndieAuth\Storage\Sqlite3Storage">Sqlite3Storage</abbr></a></dt>
|
||||
<dd></dd>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -class"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a></dt>
|
||||
<dd></dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
@ -111,8 +111,6 @@
|
||||
<dd>Filesystem JSON Token Storage</dd>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -class"><a href="classes/Taproot-IndieAuth-Storage-Sqlite3Storage.html"><abbr title="\Taproot\IndieAuth\Storage\Sqlite3Storage">Sqlite3Storage</abbr></a></dt>
|
||||
<dd></dd>
|
||||
<dt class="phpdocumentor-table-of-contents__entry -class"><a href="classes/Taproot-IndieAuth-Storage-Token.html"><abbr title="\Taproot\IndieAuth\Storage\Token">Token</abbr></a></dt>
|
||||
<dd></dd>
|
||||
|
||||
</dl>
|
||||
|
||||
|
@ -19,6 +19,8 @@ class IndieAuthException extends Exception {
|
||||
const INVALID_STATE = 9;
|
||||
const INVALID_CODE_CHALLENGE = 10;
|
||||
const INVALID_SCOPE = 11;
|
||||
const INVALID_GRANT = 12;
|
||||
const INVALID_REQUEST = 13;
|
||||
|
||||
const EXC_INFO = [
|
||||
self::INTERNAL_ERROR => ['statusCode' => 500, 'name' => 'Internal Server Error', 'explanation' => 'An internal server error occurred.'],
|
||||
@ -34,6 +36,8 @@ class IndieAuthException extends Exception {
|
||||
self::INVALID_STATE => ['statusCode' => 302, 'name' => 'Invalid state Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_CODE_CHALLENGE => ['statusCode' => 302, 'name' => 'Invalid code_challenge Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_SCOPE => ['statusCode' => 302, 'name' => 'Invalid scope Parameter', 'error' => 'invalid_request'],
|
||||
self::INVALID_GRANT => ['statusCode' => 400, 'name' => 'The provided credentials were not valid.', 'error' => 'invalid_grant'],
|
||||
self::INVALID_REQUEST => ['statusCode' => 400, 'name' => 'Invalid Request', 'error' => 'invalid_request'],
|
||||
];
|
||||
|
||||
protected ServerRequestInterface $request;
|
||||
|
208
src/Server.php
208
src/Server.php
@ -324,65 +324,63 @@ class Server {
|
||||
|
||||
$bodyParams = $request->getParsedBody();
|
||||
|
||||
if (!isset($bodyParams['code'])) {
|
||||
$this->logger->warning('The exchange request was missing the code parameter. Returning an error response.');
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'The code parameter was missing.'
|
||||
]));
|
||||
}
|
||||
|
||||
// Attempt to internally exchange the provided auth code for an access token.
|
||||
// We do this before anything else so that the auth code is invalidated as soon as the request starts,
|
||||
// and the resulting access token is revoked if we encounter an error. This ends up providing a simpler
|
||||
// and more flexible interface for TokenStorage implementors.
|
||||
if (array_key_exists('code', $bodyParams)) {
|
||||
$token = $this->tokenStorage->exchangeAuthCodeForAccessToken($bodyParams['code']);
|
||||
try {
|
||||
// Call the token exchange method, passing in a callback which performs additional validation
|
||||
// on the auth code before it gets exchanged.
|
||||
$tokenData = $this->tokenStorage->exchangeAuthCodeForAccessToken($bodyParams['code'], function (array $authCode) use ($request, $bodyParams) {
|
||||
// Verify that all required parameters are included.
|
||||
$requiredParameters = ['client_id', 'redirect_uri', 'code_verifier'];
|
||||
$missingRequiredParameters = array_filter($requiredParameters, function ($p) use ($bodyParams) {
|
||||
return !array_key_exists($p, $bodyParams) || empty($bodyParams[$p]);
|
||||
});
|
||||
if (!empty($missingRequiredParameters)) {
|
||||
$this->logger->warning('The exchange request was missing required parameters. Returning an error response.', ['missing' => $missingRequiredParameters]);
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_REQUEST, $request);
|
||||
}
|
||||
|
||||
if (is_null($token)) {
|
||||
$this->logger->error('Attempting to exchange an auth code for a token resulted in null.', $bodyParams);
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
} // The else case is handled by the code below.
|
||||
// Verify that it was issued for the same client_id and redirect_uri
|
||||
if ($authCode['client_id'] !== $bodyParams['client_id']
|
||||
|| $authCode['redirect_uri'] !== $bodyParams['redirect_uri']) {
|
||||
$this->logger->error("The provided client_id and/or redirect_uri did not match those stored in the token.");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
|
||||
// Verify that all required parameters are included.
|
||||
$requiredParameters = ['client_id', 'redirect_uri', 'code', 'code_verifier'];
|
||||
$missingRequiredParameters = array_filter($requiredParameters, function ($p) use ($bodyParams) {
|
||||
return !array_key_exists($p, $bodyParams) || empty($bodyParams[$p]);
|
||||
});
|
||||
if (!empty($missingRequiredParameters)) {
|
||||
if (isset($token)) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
}
|
||||
$this->logger->warning('The exchange request was missing required parameters. Returning an error response.', ['missing' => $missingRequiredParameters]);
|
||||
// Check that the supplied code_verifier hashes to the stored code_challenge
|
||||
// TODO: support method = plain as well as S256.
|
||||
if (!hash_equals($authCode['code_challenge'], generatePKCECodeChallenge($bodyParams['code_verifier']))) {
|
||||
$this->logger->error("The provided code_verifier did not hash to the stored code_challenge");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
|
||||
// Check that this token either grants at most the profile scope.
|
||||
$requestedScopes = explode(' ', $authCode['scope'] ?? '');
|
||||
if (!empty($requestedScopes) && $requestedScopes != ['profile']) {
|
||||
$this->logger->error("An exchange request for a token granting scopes other than “profile” was sent to the authorization endpoint.");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
});
|
||||
} catch (IndieAuthException $e) {
|
||||
// If an exception was thrown, return a corresponding error response.
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'The following required parameters were missing or empty: ' . join(', ', $missingRequiredParameters)
|
||||
'error' => $e->getInfo()['error'],
|
||||
'error_description' => $e->getMessage()
|
||||
]));
|
||||
}
|
||||
|
||||
// Verify that it was issued for the same client_id and redirect_uri
|
||||
if ($token->getData()['client_id'] !== $bodyParams['client_id']
|
||||
|| $token->getData()['redirect_uri'] !== $bodyParams['redirect_uri']) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("The provided client_id and/or redirect_uri did not match those stored in the token.");
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
|
||||
// Check that the supplied code_verifier hashes to the stored code_challenge
|
||||
// TODO: support method = plain as well as S256.
|
||||
if (!hash_equals($token->getData()['code_challenge'], generatePKCECodeChallenge($bodyParams['code_verifier']))) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("The provided code_verifier did not hash to the stored code_challenge");
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
|
||||
// Check that this token either grants at most the profile scope.
|
||||
$requestedScopes = explode(' ', $token->getData()['scope'] ?? '');
|
||||
if (!empty($requestedScopes) && $requestedScopes != ['profile']) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("An exchange request for a token granting scopes other than “profile” was sent to the authorization endpoint.");
|
||||
if (is_null($tokenData)) {
|
||||
$this->logger->error('Attempting to exchange an auth code for a token resulted in null.', $bodyParams);
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
@ -395,7 +393,9 @@ class Server {
|
||||
return new Response(200, [
|
||||
'content-type' => 'application/json',
|
||||
'cache-control' => 'no-store',
|
||||
], json_encode(array_filter($token->getData(), function ($k) {
|
||||
], json_encode(array_filter($tokenData, function ($k) {
|
||||
// Prevent codes exchanged at the authorization endpoint from returning any information other than
|
||||
// me and profile.
|
||||
return in_array($k, ['me', 'profile']);
|
||||
}, ARRAY_FILTER_USE_KEY)));
|
||||
}
|
||||
@ -586,7 +586,7 @@ class Server {
|
||||
// Return a redirect to the client app.
|
||||
return new Response(302, [
|
||||
'Location' => appendQueryParams($queryParams['redirect_uri'], [
|
||||
'code' => $authCode->getKey(),
|
||||
'code' => $authCode,
|
||||
'state' => $code['state']
|
||||
]),
|
||||
'Cache-control' => 'no-cache'
|
||||
@ -679,79 +679,81 @@ class Server {
|
||||
|
||||
$bodyParams = $request->getParsedBody();
|
||||
|
||||
if (!isset($bodyParams['code'])) {
|
||||
$this->logger->warning('The exchange request was missing the code parameter. Returning an error response.');
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'The code parameter was missing.'
|
||||
]));
|
||||
}
|
||||
|
||||
// Attempt to internally exchange the provided auth code for an access token.
|
||||
// We do this before anything else so that the auth code is invalidated as soon as the request starts,
|
||||
// and the resulting access token is revoked if we encounter an error. This ends up providing a simpler
|
||||
// and more flexible interface for TokenStorage implementors.
|
||||
if (array_key_exists('code', $bodyParams)) {
|
||||
$token = $this->tokenStorage->exchangeAuthCodeForAccessToken($bodyParams['code']);
|
||||
try {
|
||||
// Call the token exchange method, passing in a callback which performs additional validation
|
||||
// on the auth code before it gets exchanged.
|
||||
$tokenData = $this->tokenStorage->exchangeAuthCodeForAccessToken($bodyParams['code'], function (array $authCode) use ($request, $bodyParams) {
|
||||
// Verify that all required parameters are included.
|
||||
$requiredParameters = ['client_id', 'redirect_uri', 'code_verifier'];
|
||||
$missingRequiredParameters = array_filter($requiredParameters, function ($p) use ($bodyParams) {
|
||||
return !array_key_exists($p, $bodyParams) || empty($bodyParams[$p]);
|
||||
});
|
||||
if (!empty($missingRequiredParameters)) {
|
||||
$this->logger->warning('The exchange request was missing required parameters. Returning an error response.', ['missing' => $missingRequiredParameters]);
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_REQUEST, $request);
|
||||
}
|
||||
|
||||
if (is_null($token)) {
|
||||
$this->logger->error('Attempting to exchange an auth code for a token resulted in null.', $bodyParams);
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
}
|
||||
// Verify that it was issued for the same client_id and redirect_uri
|
||||
if ($authCode['client_id'] !== $bodyParams['client_id']
|
||||
|| $authCode['redirect_uri'] !== $bodyParams['redirect_uri']) {
|
||||
$this->logger->error("The provided client_id and/or redirect_uri did not match those stored in the token.");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
|
||||
// Verify that all required parameters are included.
|
||||
$requiredParameters = ['client_id', 'redirect_uri', 'code', 'code_verifier'];
|
||||
$missingRequiredParameters = array_filter($requiredParameters, function ($p) use ($bodyParams) {
|
||||
return !array_key_exists($p, $bodyParams) || empty($bodyParams[$p]);
|
||||
});
|
||||
if (!empty($missingRequiredParameters)) {
|
||||
if (isset($token)) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
}
|
||||
$this->logger->warning('The exchange request was missing required parameters. Returning an error response.', ['missing' => $missingRequiredParameters]);
|
||||
// Check that the supplied code_verifier hashes to the stored code_challenge
|
||||
// TODO: support method = plain as well as S256.
|
||||
if (!hash_equals($authCode['code_challenge'], generatePKCECodeChallenge($bodyParams['code_verifier']))) {
|
||||
$this->logger->error("The provided code_verifier did not hash to the stored code_challenge");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
|
||||
// Check that scope is not empty.
|
||||
if (empty($authCode['scope'])) {
|
||||
$this->logger->error("An exchange request for a token with an empty scope was sent to the token endpoint.");
|
||||
throw IndieAuthException::create(IndieAuthException::INVALID_GRANT, $request);
|
||||
}
|
||||
});
|
||||
} catch (IndieAuthException $e) {
|
||||
// If an exception was thrown, return a corresponding error response.
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_request',
|
||||
'error_description' => 'The following required parameters were missing or empty: ' . join(', ', $missingRequiredParameters)
|
||||
'error' => $e->getInfo()['error'],
|
||||
'error_description' => $e->getMessage()
|
||||
]));
|
||||
}
|
||||
|
||||
// Verify that it was issued for the same client_id and redirect_uri
|
||||
if ($token->getData()['client_id'] !== $bodyParams['client_id']
|
||||
|| $token->getData()['redirect_uri'] !== $bodyParams['redirect_uri']) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("The provided client_id and/or redirect_uri did not match those stored in the token.");
|
||||
if (is_null($tokenData)) {
|
||||
$this->logger->error('Attempting to exchange an auth code for a token resulted in null.', $bodyParams);
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
|
||||
// Check that the supplied code_verifier hashes to the stored code_challenge
|
||||
// TODO: support method = plain as well as S256.
|
||||
if (!hash_equals($token->getData()['code_challenge'], generatePKCECodeChallenge($bodyParams['code_verifier']))) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("The provided code_verifier did not hash to the stored code_challenge");
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
// TODO: return an error if the token doesn’t contain a me key.
|
||||
|
||||
// If the auth code was issued with no scope, return an error.
|
||||
if (empty($token->getData()['scope'])) {
|
||||
$this->tokenStorage->revokeAccessToken($token->getKey());
|
||||
$this->logger->error("Cannot issue an access token with no scopes.");
|
||||
return new Response(400, ['content-type' => 'application/json'], json_encode([
|
||||
'error' => 'invalid_grant',
|
||||
'error_description' => 'The provided credentials were not valid.'
|
||||
]));
|
||||
}
|
||||
|
||||
// If everything checks out, generate an access token and return it.
|
||||
// If everything checked out, return {"me": "https://example.com"} response
|
||||
return new Response(200, [
|
||||
'content-type' => 'application/json',
|
||||
'cache-control' => 'no-store'
|
||||
'cache-control' => 'no-store',
|
||||
], json_encode(array_merge([
|
||||
'access_token' => $token->getKey(),
|
||||
// Ensure that the token_type key is present, if tokenStorage doesn’t include it.
|
||||
'token_type' => 'Bearer'
|
||||
], array_filter($token->getData(), function ($k) {
|
||||
return in_array($k, ['me', 'profile', 'scope']);
|
||||
], array_filter($tokenData, function ($k) {
|
||||
// We should be able to trust the return data from tokenStorage, but there’s no harm in
|
||||
// preventing code_challenges from leaking, per OAuth2.
|
||||
return !in_array($k, ['code_challenge', 'code_challenge_method']);
|
||||
}, ARRAY_FILTER_USE_KEY))));
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ use Exception;
|
||||
use Psr\Log\LoggerAwareInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use Psr\Log\NullLogger;
|
||||
use Taproot\IndieAuth\IndieAuthException;
|
||||
|
||||
use function Taproot\IndieAuth\generateRandomString;
|
||||
|
||||
@ -64,7 +65,7 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
|
||||
// TokenStorageInterface Methods.
|
||||
|
||||
public function createAuthCode(array $data): ?Token {
|
||||
public function createAuthCode(array $data): ?string {
|
||||
$authCode = generateRandomString(self::TOKEN_LENGTH);
|
||||
$accessToken = $this->hash($authCode);
|
||||
|
||||
@ -75,17 +76,17 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
if (!$this->put($accessToken, $data)) {
|
||||
return null;
|
||||
}
|
||||
return new Token($authCode, $data);
|
||||
return $authCode;
|
||||
}
|
||||
|
||||
public function exchangeAuthCodeForAccessToken(string $code): ?Token {
|
||||
public function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array {
|
||||
// Hash the auth code to get the theoretical matching access token filename.
|
||||
$accessToken = $this->hash($code);
|
||||
|
||||
// Prevent the token file from being read, modified or deleted while we’re working with it.
|
||||
// r+ to allow reading and writing, but to make sure we don’t create the file if it doesn’t
|
||||
// already exist.
|
||||
return $this->withLock($this->getPath($accessToken), 'r+', function ($fp) use ($accessToken) {
|
||||
return $this->withLock($this->getPath($accessToken), 'r+', function ($fp) use ($accessToken, $validateAuthCode) {
|
||||
// Read the file contents.
|
||||
$fileContents = '';
|
||||
while ($d = fread($fp, 1024)) { $fileContents .= $d; }
|
||||
@ -100,6 +101,18 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
// Make sure the auth code isn’t expired.
|
||||
if (($data['valid_until'] ?? 0) < time()) { return null; }
|
||||
|
||||
// The auth code is valid as far as we know, pass it to the validation callback passed from the
|
||||
// Server.
|
||||
try {
|
||||
$validateAuthCode($data);
|
||||
} catch (IndieAuthException $e) {
|
||||
// If there was an issue with the auth code, delete it before bubbling the exception
|
||||
// up to the Server for handling. We currently have a lock on the file path, so pass
|
||||
// false to $observeLock to prevent a deadlock.
|
||||
$this->delete($accessToken, false);
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// If the access token is valid, mark it as redeemed and set a new expiry time.
|
||||
$data['exchanged_at'] = time();
|
||||
|
||||
@ -120,11 +133,18 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
if (fwrite($fp, $jsonData) === false) { return null; }
|
||||
if (ftruncate($fp, strlen($jsonData)) === false) { return null; }
|
||||
|
||||
return new Token($accessToken, $data);
|
||||
// Return the OAuth2-compatible access token data to the Server for passing onto
|
||||
// the client app. Passed via array_filter to remove the scope key if scope is null.
|
||||
return array_filter([
|
||||
'access_token' => $accessToken,
|
||||
'scope' => $data['scope'] ?? null,
|
||||
'me' => $data['me'],
|
||||
'profile' => $data['profile'] ?? null
|
||||
]);
|
||||
});
|
||||
}
|
||||
|
||||
public function getAccessToken(string $token): ?Token {
|
||||
public function getAccessToken(string $token): ?array {
|
||||
$data = $this->get($token);
|
||||
|
||||
if (!is_array($data)) { return null; }
|
||||
@ -137,7 +157,7 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
if (is_int($data['valid_until']) && $data['valid_until'] < time()) { return null; }
|
||||
|
||||
// The token is valid!
|
||||
return new Token($token, $data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function revokeAccessToken(string $token): bool {
|
||||
@ -205,12 +225,16 @@ class FilesystemJsonStorage implements TokenStorageInterface, LoggerAwareInterfa
|
||||
});
|
||||
}
|
||||
|
||||
public function delete(string $key): bool {
|
||||
public function delete(string $key, $observeLock=true): bool {
|
||||
$path = $this->getPath($key);
|
||||
if (file_exists($path)) {
|
||||
return $this->withLock($path, 'r', function ($fp) use ($path) {
|
||||
if ($observeLock) {
|
||||
return $this->withLock($path, 'r', function ($fp) use ($path) {
|
||||
return unlink($path);
|
||||
});
|
||||
} else {
|
||||
return unlink($path);
|
||||
});
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php declare(strict_types=1);
|
||||
|
||||
namespace Taproot\IndieAuth\Storage;
|
||||
|
||||
use JsonSerializable;
|
||||
|
||||
class Token implements JsonSerializable {
|
||||
protected string $key;
|
||||
protected array $data;
|
||||
|
||||
public function __construct(string $key, array $data) {
|
||||
$this->key = $key;
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
public function getData(): array {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
public function getKey(): string {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function __toString() {
|
||||
return $this->key;
|
||||
}
|
||||
|
||||
public function jsonSerialize() {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
@ -61,9 +61,8 @@ interface TokenStorageInterface {
|
||||
* the consent screen. Other implementations of `AuthorizationFormInterface` may add additional
|
||||
* data, such as custom token-specific settings, or a custom token lifetime.
|
||||
*
|
||||
* This method should store the data passed to it, generate a corresponding authorization code,
|
||||
* and return an instance of `Storage\Token` containing both. Implementations will usually add
|
||||
* an expiry time, usually under the `valid_until` key.
|
||||
* This method should store the data passed to it, generate a corresponding authorization code
|
||||
* string, and return it.
|
||||
*
|
||||
* The method call and data is structured such that implementations have a lot of flexibility
|
||||
* about how to store authorization code data. It could be a record in an auth code database
|
||||
@ -76,40 +75,56 @@ interface TokenStorageInterface {
|
||||
* recommended to log it internally for reference. For the same reason, this method should not
|
||||
* throw exceptions.
|
||||
*/
|
||||
public function createAuthCode(array $data): ?Token;
|
||||
public function createAuthCode(array $data): ?string;
|
||||
|
||||
/**
|
||||
* Exchange Authorization Code for Access Token
|
||||
*
|
||||
* Attempt to exchange an authorization code identified by `$code` for
|
||||
* an access token, returning it in a `Token` on success and null on error.
|
||||
* an access token. Return an array of access token data to be passed onto
|
||||
* the client app on success, and null on error.
|
||||
*
|
||||
* This method is called at the beginning of a code exchange request, before
|
||||
* further error checking or validation is applied. On an error, the created
|
||||
* access token is immediately revoked via `revokeAccessToken()`.
|
||||
* further error checking or validation is applied. It should proceed as
|
||||
* follows.
|
||||
*
|
||||
* For this reason, the token data in the returned Token object MUST include
|
||||
* the `client_id` and `redirect_uri` parameters associated with the
|
||||
* authorization code, as these are used by the IndieAuth Server for further
|
||||
* validation.
|
||||
* * Attempt to fetch the authorization code data identified by $code. If
|
||||
* it does not exist or has expired, return null;
|
||||
* * Pass the authorization code data array to $validateAuthCode for validation.
|
||||
* If there is a problem with the code, a `Taproot\IndieAuth\IndieAuthException`
|
||||
* will be thrown. This method should catch it, invalidate the authorization
|
||||
* code data, then re-throw the exception for handling by Server.
|
||||
* * If the authorization code data passed all checks, convert it into an access
|
||||
* token, invalidate the auth code to prevent re-use, and store the access token
|
||||
* data internally.
|
||||
* * Return an array of access token data to be passed onto the client app. It MUST
|
||||
* contain the following keys:
|
||||
* * `me`
|
||||
* * `access_token`
|
||||
* Additonally, it SHOULD contain the following keys:
|
||||
* * `scope`, if the token grants any scope
|
||||
* And MAY contain additional keys, such as:
|
||||
* * `profile`
|
||||
* * `expires_at`
|
||||
*
|
||||
* This method is responsible for ensuring that the matched auth code is
|
||||
* not expired. If it is, it should return null, presumably after deleting
|
||||
* the corresponding authorization code record.
|
||||
* If the authorization code was redeemed at the authorization endpoint, Server will
|
||||
* only pass the `me` and `profile` keys onto the client. In both cases, it will filter
|
||||
* out `code_challenge` keys to prevent that data from accidentally being leaked to
|
||||
* clients.
|
||||
*
|
||||
* If the exchanged access token should expire, this method should set its
|
||||
* expiry time, usually in a `valid_until` key.
|
||||
* @param string $code The Authorization Code to attempt to exchange.
|
||||
* @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.
|
||||
* @return array|null An array of access token data to return to the client on success, null on any error.
|
||||
*/
|
||||
public function exchangeAuthCodeForAccessToken(string $code): ?Token;
|
||||
public function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array;
|
||||
|
||||
/**
|
||||
* Get Access Token
|
||||
*
|
||||
* Fetch access token data identified by the token `$token`, returning
|
||||
* null if it is expired or invalid. The data should be structured in
|
||||
* exactly the same way it was stored by `exchangeAuthCodeForAccessToken`.
|
||||
* null if it is expired or invalid.
|
||||
*/
|
||||
public function getAccessToken(string $token): ?Token;
|
||||
public function getAccessToken(string $token): ?array;
|
||||
|
||||
/**
|
||||
* Revoke Access Token
|
||||
|
@ -7,7 +7,6 @@ use PHPUnit\Framework\TestCase;
|
||||
use Taproot\IndieAuth\Storage\FilesystemJsonStorage;
|
||||
|
||||
const SECRET = '1111111111111111111111111111111111111111111111111111111111111111';
|
||||
const TMP_DIR = __DIR__ . '/tmp';
|
||||
|
||||
class FilesystemJsonStorageTest extends TestCase {
|
||||
protected function setUp(): void {
|
||||
|
@ -613,20 +613,21 @@ EOT
|
||||
] as $endpointHandler) {
|
||||
// Create an auth code.
|
||||
$codeVerifier = generateRandomString(32);
|
||||
$authCode = $storage->createAuthCode([
|
||||
$authCodeData = [
|
||||
'client_id' => 'https://client.example.com/',
|
||||
'redirect_uri' => 'https://client.example.com/auth',
|
||||
'code_challenge' => generatePKCECodeChallenge($codeVerifier),
|
||||
'state' => '12345',
|
||||
'code_challenge_method' => 'S256'
|
||||
]);
|
||||
];
|
||||
$authCode = $storage->createAuthCode($authCodeData);
|
||||
|
||||
// Create what would by default be a successful request, then merge specific error-inducing params.
|
||||
$req = (new ServerRequest('POST', 'https://example.com'))->withParsedBody(array_merge([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authCode->getKey(),
|
||||
'client_id' => $authCode->getData()['client_id'],
|
||||
'redirect_uri' => $authCode->getData()['redirect_uri'],
|
||||
'code' => $authCode,
|
||||
'client_id' => $authCodeData['client_id'],
|
||||
'redirect_uri' => $authCodeData['redirect_uri'],
|
||||
'code_verifier' => $codeVerifier
|
||||
], $params));
|
||||
|
||||
@ -644,21 +645,22 @@ EOT
|
||||
|
||||
// Create an auth code.
|
||||
$codeVerifier = generateRandomString(32);
|
||||
$authCode = $storage->createAuthCode([
|
||||
$authCodeData = [
|
||||
'client_id' => 'https://client.example.com/',
|
||||
'redirect_uri' => 'https://client.example.com/auth',
|
||||
'code_challenge' => generatePKCECodeChallenge($codeVerifier),
|
||||
'state' => '12345',
|
||||
'code_challenge_method' => 'S256',
|
||||
'scope' => 'create update'
|
||||
]);
|
||||
];
|
||||
$authCode = $storage->createAuthCode($authCodeData);
|
||||
|
||||
// Create what would by default be a successful request, then merge specific error-inducing params.
|
||||
$req = (new ServerRequest('POST', 'https://example.com'))->withParsedBody([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authCode->getKey(),
|
||||
'client_id' => $authCode->getData()['client_id'],
|
||||
'redirect_uri' => $authCode->getData()['redirect_uri'],
|
||||
'code' => $authCode,
|
||||
'client_id' => $authCodeData['client_id'],
|
||||
'redirect_uri' => $authCodeData['redirect_uri'],
|
||||
'code_verifier' => $codeVerifier
|
||||
]);
|
||||
|
||||
@ -675,7 +677,7 @@ EOT
|
||||
|
||||
// Create an auth code.
|
||||
$codeVerifier = generateRandomString(32);
|
||||
$authCode = $storage->createAuthCode([
|
||||
$authCodeData = [
|
||||
'client_id' => 'https://client.example.com/',
|
||||
'redirect_uri' => 'https://client.example.com/auth',
|
||||
'code_challenge' => generatePKCECodeChallenge($codeVerifier),
|
||||
@ -686,14 +688,15 @@ EOT
|
||||
'profile' => [
|
||||
'name' => 'Me'
|
||||
]
|
||||
]);
|
||||
];
|
||||
$authCode = $storage->createAuthCode($authCodeData);
|
||||
|
||||
// Create what would by default be a successful request, then merge specific error-inducing params.
|
||||
$req = (new ServerRequest('POST', 'https://example.com'))->withParsedBody([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authCode->getKey(),
|
||||
'client_id' => $authCode->getData()['client_id'],
|
||||
'redirect_uri' => $authCode->getData()['redirect_uri'],
|
||||
'code' => $authCode,
|
||||
'client_id' => $authCodeData['client_id'],
|
||||
'redirect_uri' => $authCodeData['redirect_uri'],
|
||||
'code_verifier' => $codeVerifier
|
||||
]);
|
||||
|
||||
@ -732,20 +735,22 @@ EOT
|
||||
|
||||
// Create an auth code.
|
||||
$codeVerifier = generateRandomString(32);
|
||||
$authCode = $storage->createAuthCode([
|
||||
$authCodeData = [
|
||||
'client_id' => 'https://client.example.com/',
|
||||
'redirect_uri' => 'https://client.example.com/auth',
|
||||
'code_challenge' => generatePKCECodeChallenge($codeVerifier),
|
||||
'state' => '12345',
|
||||
'code_challenge_method' => 'S256'
|
||||
]);
|
||||
'code_challenge_method' => 'S256',
|
||||
'me' => 'https://me.example.com'
|
||||
];
|
||||
$authCode = $storage->createAuthCode($authCodeData);
|
||||
|
||||
// Create what would by default be a successful request, then merge specific error-inducing params.
|
||||
$req = (new ServerRequest('POST', 'https://example.com'))->withParsedBody([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authCode->getKey(),
|
||||
'client_id' => $authCode->getData()['client_id'],
|
||||
'redirect_uri' => $authCode->getData()['redirect_uri'],
|
||||
'code' => $authCode,
|
||||
'client_id' => $authCodeData['client_id'],
|
||||
'redirect_uri' => $authCodeData['redirect_uri'],
|
||||
'code_verifier' => $codeVerifier
|
||||
]);
|
||||
|
||||
@ -762,7 +767,7 @@ EOT
|
||||
|
||||
// Create an auth code.
|
||||
$codeVerifier = generateRandomString(32);
|
||||
$authCode = $storage->createAuthCode([
|
||||
$authCodeData = [
|
||||
'client_id' => 'https://client.example.com/',
|
||||
'redirect_uri' => 'https://client.example.com/auth',
|
||||
'code_challenge' => generatePKCECodeChallenge($codeVerifier),
|
||||
@ -773,14 +778,15 @@ EOT
|
||||
'profile' => [
|
||||
'name' => 'Me'
|
||||
]
|
||||
]);
|
||||
];
|
||||
$authCode = $storage->createAuthCode($authCodeData);
|
||||
|
||||
// Create what would by default be a successful request, then merge specific error-inducing params.
|
||||
$req = (new ServerRequest('POST', 'https://example.com'))->withParsedBody([
|
||||
'grant_type' => 'authorization_code',
|
||||
'code' => $authCode->getKey(),
|
||||
'client_id' => $authCode->getData()['client_id'],
|
||||
'redirect_uri' => $authCode->getData()['redirect_uri'],
|
||||
'code' => $authCode,
|
||||
'client_id' => $authCodeData['client_id'],
|
||||
'redirect_uri' => $authCodeData['redirect_uri'],
|
||||
'code_verifier' => $codeVerifier
|
||||
]);
|
||||
|
||||
@ -789,11 +795,11 @@ EOT
|
||||
$this->assertEquals(200, $res->getStatusCode());
|
||||
$this->assertEquals('no-store', $res->getHeaderLine('cache-control'));
|
||||
$resJson = json_decode((string) $res->getBody(), true);
|
||||
$this->assertEquals(hash_hmac('sha256', $authCode->getKey(), SERVER_SECRET), $resJson['access_token']);
|
||||
$this->assertEquals(hash_hmac('sha256', $authCode, SERVER_SECRET), $resJson['access_token']);
|
||||
$this->assertEquals('Bearer', $resJson['token_type']);
|
||||
$this->assertEquals($authCode->getData()['me'], $resJson['me']);
|
||||
$this->assertEquals($authCode->getData()['profile'], $resJson['profile']);
|
||||
$this->assertTrue(scopeEquals($authCode->getData()['scope'], $resJson['scope']));
|
||||
$this->assertEquals($authCodeData['me'], $resJson['me']);
|
||||
$this->assertEquals($authCodeData['profile'], $resJson['profile']);
|
||||
$this->assertTrue(scopeEquals($authCodeData['scope'], $resJson['scope']));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -832,15 +838,15 @@ function scopeEquals($scope1, $scope2): bool {
|
||||
}
|
||||
|
||||
class NullTokenStorage implements TokenStorageInterface {
|
||||
public function createAuthCode(array $data): ?Token {
|
||||
public function createAuthCode(array $data): ?string {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAccessToken(string $token): ?Token {
|
||||
public function getAccessToken(string $token): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function exchangeAuthCodeForAccessToken(string $code): ?Token {
|
||||
public function exchangeAuthCodeForAccessToken(string $code, callable $validateAuthCode): ?array {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user