Make state optional and do not use it by default in code, pkce and webauthn flows

This commit is contained in:
Dusan Jakub
2023-10-11 17:39:37 +02:00
parent 0016f30120
commit 0c6c41f469
10 changed files with 79 additions and 48 deletions

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);

View File

@@ -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");

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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);