mirror of
https://github.com/ysoftdevs/oauth-playground-client.git
synced 2026-01-11 22:41:29 +01:00
Make state optional and do not use it by default in code, pkce and webauthn flows
This commit is contained in:
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
@@ -56,7 +56,7 @@
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>2. Verify the state parameter</h6>
|
||||
<h6>2. Parse response</h6>
|
||||
<p>
|
||||
You have now been redirected back to the application, to the page that was specified in the <b>redirect-url</b> parameter.
|
||||
In the URL you can notice, that there are addtional query parameters:
|
||||
@@ -64,7 +64,7 @@
|
||||
<pre class="code-block"><code id="queryParams"></code></pre>
|
||||
<p>Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<li class="collection-item has-state">
|
||||
<p><b><span class="emphasis">state</span>=<span id="state"></span></b></p>
|
||||
<p>
|
||||
The state parameter is an opaque value used by the client to maintain state between the request and the callback.
|
||||
@@ -81,10 +81,10 @@
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<p class="has-state">
|
||||
Now we have everything necessary to obtain token for the user. But is the state we have sent (<b><span id="sent-state"></span></b>) equivalent to the one we received back (<b><span id="received-state"></span></b>)?
|
||||
</p>
|
||||
<div class="row flow-submit-container">
|
||||
<div class="row flow-submit-container has-state">
|
||||
<div class="col m6 s12" style="margin-bottom: 5px;">
|
||||
<a class="waves-effect waves-light btn btn-success full-width" onclick="proceedToNextStep()">States are matching</a>
|
||||
</div>
|
||||
@@ -92,6 +92,10 @@
|
||||
<a class="waves-effect waves-light btn btn-error full-width" href="/flow/code">States are not matching</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row flow-submit-container no-state">
|
||||
<a id="get-token-btn" class="waves-effect waves-light btn full-width" onClick="proceedToNextStep()">Continue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -111,9 +115,9 @@
|
||||
const state = urlParams.get("state");
|
||||
const sentState = getCookie("state");
|
||||
|
||||
if (!code || !state || !sentState) {
|
||||
window.location = "/flow/expired";
|
||||
}
|
||||
// if (!code || !state || !sentState) {
|
||||
// window.location = "/flow/expired";
|
||||
// }
|
||||
|
||||
$("#queryParams").text(window.location.search)
|
||||
$("#state").text(state);
|
||||
@@ -121,6 +125,9 @@
|
||||
$("#received-state").text(state);
|
||||
$("#code").text(code);
|
||||
|
||||
$(".has-state").toggle(!!state);
|
||||
$(".no-state").toggle(!state);
|
||||
|
||||
function proceedToNextStep() {
|
||||
window.location.href = "/flow/code-3" + window.location.search;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
@@ -100,7 +100,7 @@
|
||||
token. In this case, we are requesting access to <b>photos</b> and <b>files</b>.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<!-- <li class="collection-item">
|
||||
<p><b><span class="emphasis">state</span>=<span id="state"></span></b></p>
|
||||
<p>
|
||||
State parameter. This is an <b>optional parameter</b> that the client can use to maintain
|
||||
@@ -108,7 +108,7 @@
|
||||
redirecting the user back to the client, allowing the client to verify that the response is coming from the
|
||||
server and not a malicious third party (<a href="https://owasp.org/www-community/attacks/csrf" target="_blank">CSRF attack</a>).
|
||||
</p>
|
||||
</li>
|
||||
</li> -->
|
||||
</ul>
|
||||
<p>
|
||||
All that we now need to do is click the button below and login with our credentials.
|
||||
@@ -144,7 +144,8 @@
|
||||
+ "&" + "client_id=" + getClientId()
|
||||
+ "&" + "redirect_uri=" + redirectUri
|
||||
+ "&" + "scope=" + scope
|
||||
+ "&" + "state=" + state;
|
||||
//+ "&" + "state=" + state
|
||||
;
|
||||
}
|
||||
|
||||
function fillExample() {
|
||||
@@ -153,7 +154,8 @@
|
||||
+ "&client_id=" + getClientId() + "\n"
|
||||
+ "&redirect_uri=" + redirectUri + "\n"
|
||||
+ "&scope=" + scope + "\n"
|
||||
+ "&state=" + state;
|
||||
//+ "&state=" + state
|
||||
;
|
||||
|
||||
$("#requestUriExample").text(requestExample);
|
||||
$("#baseUrl").text(baseUrl);
|
||||
@@ -172,7 +174,7 @@
|
||||
|
||||
const responseType = "code";
|
||||
const redirectUri = getRedirectUri();
|
||||
const scope = "photos%20files";
|
||||
const scope = "photos files";
|
||||
const state = generateSessionState();
|
||||
setCookie("state", state, 5);
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
After the user is redirected back to the client, verify the state
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
Exchange the authorization code and code verifier for an access token
|
||||
@@ -103,7 +103,7 @@
|
||||
token. In this case, we are requesting access to <b>photos</b> and <b>files</b>.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<!-- <li class="collection-item">
|
||||
<p><b><span class="emphasis">state</span>=<span id="state"></span></b></p>
|
||||
<p>
|
||||
State parameter. This is an <b>optional parameter</b> that the client can use to maintain
|
||||
@@ -111,7 +111,7 @@
|
||||
user back to the client, allowing the client to verify that the response is coming from the
|
||||
server and not a malicious third party (<a href="https://owasp.org/www-community/attacks/csrf" target="_blank">CSRF attack</a>).
|
||||
</p>
|
||||
</li>
|
||||
</li> -->
|
||||
<li class="collection-item">
|
||||
<p><b><span class="emphasis">code_challenge</span>=<span id="codeChallenge"></span></b></p>
|
||||
<p>
|
||||
@@ -159,7 +159,7 @@
|
||||
+ "&" + "client_id=" + getClientId()
|
||||
+ "&" + "redirect_uri=" + redirectUri
|
||||
+ "&" + "scope=" + scope
|
||||
+ "&" + "state=" + state
|
||||
//+ "&" + "state=" + state
|
||||
+ "&" + "code_challenge=" + codeChallenge
|
||||
+ "&" + "code_challenge_method=S256";
|
||||
}
|
||||
@@ -170,7 +170,7 @@
|
||||
+ "&client_id=" + getClientId() + "\n"
|
||||
+ "&redirect_uri=" + redirectUri + "\n"
|
||||
+ "&scope=" + scope + "\n"
|
||||
+ "&state=" + state + "\n"
|
||||
//+ "&state=" + state + "\n"
|
||||
+ "&code_challenge=" + codeChallenge + "\n"
|
||||
+ "&code_challenge_method=S256";
|
||||
|
||||
@@ -191,7 +191,7 @@
|
||||
|
||||
const responseType = "code";
|
||||
const redirectUri = getRedirectUri();
|
||||
const scope = "photos%20files";
|
||||
const scope = "photos files";
|
||||
const state = generateSessionState();
|
||||
const codeChallenge = getCookie("code_challenge");
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
After the user is redirected back to the client, verify the state
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
Exchange the authorization code and code verifier for an access token
|
||||
@@ -63,14 +63,14 @@
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>3. Verify the state parameter</h6>
|
||||
<h6>3. Parse response</h6>
|
||||
<p>
|
||||
You have now been redirected back to the application, to the page that was specified in the <b>redirect-url</b> parameter. In the URL you can notice, that there are addtional query parameters:
|
||||
</p>
|
||||
<pre class="code-block"><code id="queryParams"></code></pre>
|
||||
<p>Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<li class="collection-item has-state">
|
||||
<p><b><span class="emphasis">state</span>=<span id="state"></span></b></p>
|
||||
<p>
|
||||
The state parameter is an opaque value used by the client to maintain state between the request and the callback.
|
||||
@@ -93,11 +93,11 @@
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<p class="has-state">
|
||||
Now we have everything necessary to obtain token for the user. But is the state we have sent (<b><span id="sent-state"></span></b>) equivalent to
|
||||
the one we received back (<b><span id="received-state"></span></b>)?
|
||||
</p>
|
||||
<div class="row flow-submit-container">
|
||||
<div class="row flow-submit-container has-state">
|
||||
<div class="col m6 s12" style="margin-bottom: 5px;">
|
||||
<a class="waves-effect waves-light btn btn-success full-width" onclick="proceedToNextStep()">States are matching</a>
|
||||
</div>
|
||||
@@ -105,6 +105,10 @@
|
||||
<a class="waves-effect waves-light btn btn-error full-width" href="/flow/code">States are not matching</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row flow-submit-container no-state">
|
||||
<a id="get-token-btn" class="waves-effect waves-light btn full-width" onClick="proceedToNextStep()">Continue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -124,15 +128,18 @@
|
||||
const state = urlParams.get('state');
|
||||
const sentState = getCookie("pkce-state");
|
||||
|
||||
if (!code || !state || !sentState) {
|
||||
window.location = "/flow/expired";
|
||||
}
|
||||
// if (!code || !state || !sentState) {
|
||||
// window.location = "/flow/expired";
|
||||
// }
|
||||
|
||||
$("#queryParams").text(window.location.search)
|
||||
$("#state").text(state);
|
||||
$("#sent-state").text(sentState);
|
||||
$("#received-state").text(state);
|
||||
$("#code").text(code);
|
||||
|
||||
$(".has-state").toggle(!!state);
|
||||
$(".no-state").toggle(!state);
|
||||
|
||||
function proceedToNextStep() {
|
||||
window.location.href = "/flow/pkce-4" + window.location.search;
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
After the user is redirected back to the client, verify the state
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
Exchange the authorization code and code verifier for an access token
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
After the user is redirected back to the client, verify the state
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s3 circle-text">
|
||||
Exchange the authorization code and code verifier for an access token
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
@@ -56,7 +56,7 @@
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>2. Verify the state parameter</h6>
|
||||
<h6>2. Parse response</h6>
|
||||
<p>
|
||||
You have now been redirected back to the application, to the page that was specified in the <b>redirect-url</b> parameter. In the URL
|
||||
you can notice, that there are addtional query parameters:
|
||||
@@ -64,7 +64,7 @@
|
||||
<pre class="code-block"><code id="queryParams"></code></pre>
|
||||
<p>Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<li class="collection-item has-state">
|
||||
<p><b><span class="emphasis">state</span>=<span id="state"></span></b></p>
|
||||
<p>
|
||||
The state parameter is an opaque value used by the client to maintain state between the request and the callback.
|
||||
@@ -82,10 +82,10 @@
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
<p class="has-state">
|
||||
Now we have everything necessary to obtain token for the user. But is the state we have sent (<b><span id="sent-state"></span></b>) equivalent to the one we received back (<b><span id="received-state"></span></b>)?
|
||||
</p>
|
||||
<div class="row flow-submit-container">
|
||||
<div class="row flow-submit-container has-state">
|
||||
<div class="col m6 s12" style="margin-bottom: 5px;">
|
||||
<a class="waves-effect waves-light btn btn-success full-width" onclick="proceedToNextStep()">States are matching</a>
|
||||
</div>
|
||||
@@ -93,6 +93,10 @@
|
||||
<a class="waves-effect waves-light btn btn-error full-width" href="/flow/code">States are not matching</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row flow-submit-container no-state">
|
||||
<a id="get-token-btn" class="waves-effect waves-light btn full-width" onClick="proceedToNextStep()">Continue</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,9 +116,9 @@
|
||||
const state = urlParams.get("state");
|
||||
const sentState = getCookie("webauth-state");
|
||||
|
||||
if (!code || !state || !sentState) {
|
||||
window.location = "/flow/expired";
|
||||
}
|
||||
// if (!code || !state || !sentState) {
|
||||
// window.location = "/flow/expired";
|
||||
// }
|
||||
|
||||
$("#queryParams").text(window.location.search)
|
||||
$("#state").text(state);
|
||||
@@ -122,6 +126,9 @@
|
||||
$("#received-state").text(state);
|
||||
$("#code").text(code);
|
||||
|
||||
$(".has-state").toggle(!!state);
|
||||
$(".no-state").toggle(!state);
|
||||
|
||||
function proceedToNextStep() {
|
||||
window.location.href = "/flow/webauthn-3" + window.location.search;
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
Build the authorization URL and redirect the user to the authorization server
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
After the user is redirected back to the client, verify that the state matches
|
||||
The user is redirected back to the client
|
||||
</div>
|
||||
<div class="col s4 circle-text">
|
||||
Exchange the authorization code for an access token
|
||||
@@ -58,11 +58,17 @@
|
||||
<div class="card-content">
|
||||
<h6>1. Build the Authorization URL</h6>
|
||||
<p>
|
||||
In order to initiate the <b>WebAuthn</b>, we proceed the same way as we would if we would be using <b>Authorization Code Flow</b> we need to build the
|
||||
authorization URL and redirect the user to the authorization server. All the passwordless logic is handled completely by the authorization server. The URL is constructed as follows:
|
||||
WebAuthn is not an OAuth grant, but rather a new browser API to facilitate passwordless login.
|
||||
</p>
|
||||
<p>It can be used with any grant that renders a login form,
|
||||
such as <a href="/flow/code">Authorization Code Flow</a> or <a href="/flow/dag">Device Authorization Grant</a>, or even outside OAuth for your application's custom login.
|
||||
</p>
|
||||
<p>
|
||||
For this demostration, we'll use an <a href="/flow/code">Authorization Code Flow</a> with a custom URL. In practice, if and when Webauthn is used instead of passwords passwords depends on the Authority Server settings.
|
||||
</p>
|
||||
</p>
|
||||
<pre class="code-block"><code id="requestUriExample"></code></pre>
|
||||
<p>Let's break it down...</p>
|
||||
<!-- <p>Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p><b><span id="baseUrl"></span></b></p>
|
||||
@@ -111,7 +117,7 @@
|
||||
server and not a malicious third party (<a href="https://owasp.org/www-community/attacks/csrf" target="_blank">CSRF attack</a>).
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</ul> -->
|
||||
<p>
|
||||
All that we now need to do is click the button below and perform the <b>Web Authentication Flow</b> on the server. After your account will be successfully verified, you will be redirected back to this playground, to the URL we have specified in the <b>redirect_uri</b> query parameter of the request.
|
||||
</p>
|
||||
@@ -143,7 +149,8 @@
|
||||
+ "&" + "client_id=" + getClientId()
|
||||
+ "&" + "redirect_uri=" + redirectUri
|
||||
+ "&" + "scope=" + scope
|
||||
+ "&" + "state=" + state;
|
||||
//+ "&" + "state=" + state
|
||||
;
|
||||
}
|
||||
|
||||
function fillExample() {
|
||||
@@ -152,7 +159,8 @@
|
||||
+ "&client_id=" + getClientId() + "\n"
|
||||
+ "&redirect_uri=" + redirectUri + "\n"
|
||||
+ "&scope=" + scope + "\n"
|
||||
+ "&state=" + state;
|
||||
//+ "&state=" + state
|
||||
;
|
||||
|
||||
$("#requestUriExample").text(requestExample);
|
||||
$("#baseUrl").text(authUrl);
|
||||
@@ -172,7 +180,7 @@
|
||||
const authUrl = baseUrl + "/passwordless"
|
||||
const responseType = "code";
|
||||
const redirectUri = getRedirectUri();
|
||||
const scope = "photos%20files";
|
||||
const scope = "photos files";
|
||||
const state = generateSessionState();
|
||||
setCookie("webauth-state", state, 5);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user