Added refresh token flow.
This commit is contained in:
parent
c507e6d760
commit
56d47ae840
3 changed files with 165 additions and 5 deletions
|
@ -25,7 +25,7 @@ $oatmeal = new OatmealContext((function() {
|
||||||
})());
|
})());
|
||||||
$oatmeal->register(new HomeRoutes);
|
$oatmeal->register(new HomeRoutes);
|
||||||
$oatmeal->register(new AuthzCodeRoutes($oatmeal->getCSRFP()));
|
$oatmeal->register(new AuthzCodeRoutes($oatmeal->getCSRFP()));
|
||||||
$oatmeal->register(new RefreshTokenRoutes);
|
$oatmeal->register(new RefreshTokenRoutes($oatmeal->getCSRFP()));
|
||||||
$oatmeal->register(new ClientCredsRoutes);
|
$oatmeal->register(new ClientCredsRoutes);
|
||||||
$oatmeal->register(new PasswordRoutes);
|
$oatmeal->register(new PasswordRoutes);
|
||||||
$oatmeal->register(new DeviceCodeRoutes);
|
$oatmeal->register(new DeviceCodeRoutes);
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class HomeRoutes extends RouteHandler {
|
||||||
I'm not going to bother with making this page look nice, the most you'll get it functional Javascript and/or CSS touch ups if really necessary.
|
I'm not going to bother with making this page look nice, the most you'll get it functional Javascript and/or CSS touch ups if really necessary.
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
<h2>Select A Flow</h2>
|
<h2>Select A Flow™</h2>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href=/authorization_code>Authorisation code</a></li>
|
<li><a href=/authorization_code>Authorisation code</a></li>
|
||||||
<li><a href=/refresh_token>Refresh token</a></li>
|
<li><a href=/refresh_token>Refresh token</a></li>
|
||||||
|
|
|
@ -1,11 +1,171 @@
|
||||||
<?php
|
<?php
|
||||||
namespace Oatmeal;
|
namespace Oatmeal;
|
||||||
|
|
||||||
use Index\Http\Routing\{HttpGet,RouteHandler};
|
use Index\Http\Routing\{HttpGet,HttpPost,RouteHandler};
|
||||||
|
use Index\Security\CSRFP;
|
||||||
|
|
||||||
final class RefreshTokenRoutes extends RouteHandler {
|
final class RefreshTokenRoutes extends RouteHandler {
|
||||||
|
public function __construct(
|
||||||
|
private CSRFP $csrfp
|
||||||
|
) {}
|
||||||
|
|
||||||
#[HttpGet('/refresh_token')]
|
#[HttpGet('/refresh_token')]
|
||||||
public function getIndex(): string {
|
public function getRefreshToken(): string {
|
||||||
return 'refresh token routes go here';
|
return <<<HTML
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Oatmeal / Refresh token</title>
|
||||||
|
<style>
|
||||||
|
div { margin: .5em 0; }
|
||||||
|
span { display: inline-block; min-width: 200px; text-align: right; }
|
||||||
|
input[type=text], input[type=url], input[type=password] { min-width: 500px; }
|
||||||
|
</style>
|
||||||
|
<h1>Oatmeal / Refresh token</h1>
|
||||||
|
<p>
|
||||||
|
Enter the refresh token you obtained through the <a href=/authorization_code>authorisation code flow</a> to obtain a new access token.
|
||||||
|
</p>
|
||||||
|
<form method=post action=/refresh_token>
|
||||||
|
<input type=hidden name=csrfp value="{$this->csrfp->createToken()}">
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Token URI (POST):</span>
|
||||||
|
<input type=url name=token_uri required>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Refresh token:</span>
|
||||||
|
<input type=text name=refresh_token>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Client ID:</span>
|
||||||
|
<input type=text name=client_id required>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Client secret (optional):</span>
|
||||||
|
<input type=password name=client_secret>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>Authentication:</span>
|
||||||
|
<label>
|
||||||
|
<input type=radio name=auth value=random checked>
|
||||||
|
Random
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type=radio name=auth value=header>
|
||||||
|
Authorization header
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
<input type=radio name=auth value=body>
|
||||||
|
Request body
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button>Submit</button>
|
||||||
|
<button type=reset>Reset</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<a href=/>Return</a>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[HttpPost('/refresh_token')]
|
||||||
|
public function postRefreshToken($response, $request): string {
|
||||||
|
if(!$request->isFormContent())
|
||||||
|
return 400;
|
||||||
|
$content = $request->getContent();
|
||||||
|
|
||||||
|
$csrfp = (string)$content->getParam('csrfp');
|
||||||
|
if(!$this->csrfp->verifyToken($csrfp))
|
||||||
|
return 403;
|
||||||
|
|
||||||
|
$tokenUri = (string)$content->getParam('token_uri');
|
||||||
|
if(filter_var($tokenUri, FILTER_VALIDATE_URL) === false) {
|
||||||
|
$response->setStatusCode(400);
|
||||||
|
return <<<HTML
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Oatmeal / Refresh token</title>
|
||||||
|
<h1>Oatmeal / Refresh token</h1>
|
||||||
|
<p>Provided Token URI was not a valid absolute URI.</p>
|
||||||
|
<a href="javascript:history.back();">Return</a>
|
||||||
|
HTML;
|
||||||
|
}
|
||||||
|
|
||||||
|
$clientId = (string)$content->getParam('client_id');
|
||||||
|
$clientSecret = (string)$content->getParam('client_secret');
|
||||||
|
$refreshToken = (string)$content->getParam('refresh_token');
|
||||||
|
$auth = (string)$content->getParam('auth');
|
||||||
|
|
||||||
|
$headers = [];
|
||||||
|
$body = [
|
||||||
|
'grant_type' => 'refresh_token',
|
||||||
|
'refresh_token' => $refreshToken,
|
||||||
|
];
|
||||||
|
|
||||||
|
if($clientSecret === '')
|
||||||
|
$body['client_id'] = $clientId;
|
||||||
|
elseif($auth === 'body' || ($auth !== 'header' && mt_rand(0, 10) > 5)) {
|
||||||
|
$body['client_id'] = $clientId;
|
||||||
|
$body['client_secret'] = $clientSecret;
|
||||||
|
} else
|
||||||
|
$headers[] = sprintf('Authorization: Basic %s', base64_encode(sprintf('%s:%s', $clientId, $clientSecret)));
|
||||||
|
|
||||||
|
$body = Tools::shuffleArray($body);
|
||||||
|
$response = Tools::fetch($tokenUri, headers: $headers, body: $body);
|
||||||
|
|
||||||
|
$tokenUri = htmlspecialchars($tokenUri);
|
||||||
|
$headers = htmlspecialchars(json_encode($headers, JSON_PRETTY_PRINT));
|
||||||
|
$body = htmlspecialchars(json_encode($body, JSON_PRETTY_PRINT));
|
||||||
|
|
||||||
|
$decoded = json_decode($response);
|
||||||
|
if($decoded !== null)
|
||||||
|
$response = json_encode($decoded, JSON_PRETTY_PRINT);
|
||||||
|
|
||||||
|
$response = htmlspecialchars($response);
|
||||||
|
|
||||||
|
return <<<HTML
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Oatmeal / Refresh token</title>
|
||||||
|
<style>
|
||||||
|
div { margin: .5em 0; }
|
||||||
|
span { display: inline-block; min-width: 200px; text-align: right; }
|
||||||
|
input[type=text] { min-width: 500px; }
|
||||||
|
textarea { min-width: 100%; max-width: 100%; min-height: 200px; box-sizing: border-box; }
|
||||||
|
</style>
|
||||||
|
<h1>Oatmeal / Refresh token</h1>
|
||||||
|
<p>Below is the request and response data from your request token result. If the response contains a refresh_token field, you can restart the <a href=/refresh_token>Refresh token flow</a> again and again and again and again!</p>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Token URI (POST):</span>
|
||||||
|
<input type=text value="{$tokenUri}">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Headers:</span><br>
|
||||||
|
<textarea readonly>{$headers}</textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Request body (sent as application/x-www-form-urlencoded, presented as JSON for consistency):</span><br>
|
||||||
|
<textarea readonly>{$body}</textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label>
|
||||||
|
<span>Response body:</span><br>
|
||||||
|
<textarea readonly>{$response}</textarea>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<a href=/refresh_token>Return</a>
|
||||||
|
HTML;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue