Files
oauth-playground-client/src/flow/webauthn.html
2023-10-10 14:29:20 +02:00

184 lines
10 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>OAuth 2.0 Playground - WebAuthn</title>
<meta name="description"
content="Step into the future of authentication with our WebAuthn guide. Experience firsthand how this web-based API redefines security, enabling passwordless and biometric verifications. Crucial for developers aiming to elevate user experience and protection in web apps." />
<link rel="icon" href="../favicon.ico" type="image/x-icon">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<link type="text/css" rel="stylesheet" href="../css/style.css" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;600;700&display=swap" />
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
<script async src="https://www.googletagmanager.com/gtag/js?id=G-CVH4GP5T69"></script>
<script src="../js/analytics.js"></script>
</head>
<body>
<header id="page-header"></header>
<main>
<div class="container">
<div class="section">
<h3 class="header centered">WebAuthn</h3>
<div class="circle-container circle-3">
<div class="circle">
1
</div>
<div class="line line-inactive"></div>
<div class="circle circle-inactive">
2
</div>
<div class="line line-inactive"></div>
<div class="circle circle-inactive">
3
</div>
</div>
<div class="row">
<div class="col s4 circle-text">
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
</div>
<div class="col s4 circle-text">
Exchange the authorization code for an access token
</div>
</div>
</div>
<div class="section">
<div class="col s12 m7">
<div class="card horizontal">
<div class="card-stacked">
<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:
</p>
<pre class="code-block"><code id="requestUriExample"></code></pre>
<p>Let's break it down...</p>
<ul class="collection">
<li class="collection-item">
<p><b><span id="baseUrl"></span></b></p>
<p>
URL of the authorization endpoint on the server. How is this path constructed
will differ between OAuth providers (such as Keycloak, Okta, etc.).
</p>
</li>
<li class="collection-item">
<p><b><span class="emphasis">response_type</span>=<span id="responseType"></span></b></p>
<p>
OAuth 2.0 response type. In this case, we are using the Authorization Code
flow, so we are requesting the authorization <b>code</b>.
</p>
</li>
<li class="collection-item">
<p><b><span class="emphasis">client_id</span>=<span id="clientId"></span></b></p>
<p>
Client ID of the application. This is a public identifier for the client, and
it is used by the authorization server to identify the application when redirecting the user back to the client.
</p>
</li>
<li class="collection-item">
<p><b><span class="emphasis">redirect_uri</span>=<span id="redirectUri"></span></b></p>
<p>
Redirect URI of the client. This is the URL that the authorization server
will redirect the user back to after the user has logged in and
granted permissions. The redirect URI must match one of the URIs registered
for the client ID.
</p>
</li>
<li class="collection-item">
<p><b><span class="emphasis">scope</span>=<span id="scope"></span></b></p>
<p>
Scopes requested by the client. Scopes are used to limit the access of the access
token. In this case, we are requesting the <b>offline_access</b> scope,
which allows the client to obtain a refresh token, <b>photos</b> to access photos
and <b>files</b> to have access to user files.
</p>
</li>
<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 state between the request and callback. The authorization
server includes this parameter when 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>
</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>
<div class="row flow-submit-container">
<a id="sendRequestBtn" class="waves-effect waves-light btn full-width" href="#">Authenticate</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="section centered">
<a href="/">[ Take me home ]</a>
</div>
</div>
</main>
<footer class="page-footer"></footer>
<script src="../js/load-layout.js"></script>
<script src="../js/cookies.js"></script>
<script src="../js/env-config.js"></script>
<script>
function generateSessionState() {
return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
}
function constructRequestUrl() {
return authUrl
+ "?" + "response_type=" + responseType
+ "&" + "client_id=" + getClientId()
+ "&" + "redirect_uri=" + redirectUri
+ "&" + "scope=" + scope
+ "&" + "state=" + state;
}
function fillExample() {
const requestExample = authUrl + "\n"
+ "?response_type=" + responseType + "\n"
+ "&client_id=" + getClientId() + "\n"
+ "&redirect_uri=" + redirectUri + "\n"
+ "&scope=" + scope + "\n"
+ "&state=" + state;
$("#requestUriExample").text(requestExample);
$("#baseUrl").text(authUrl);
$("#responseType").text(responseType);
$("#clientId").text(getClientId());
$("#redirectUri").text(redirectUri);
$("#scope").text(scope);
$("#state").text(state);
$("#sendRequestBtn").attr("href", constructRequestUrl());
}
function getRedirectUri() {
return window.location.protocol + "//" + window.location.host + "/flow/webauthn-2";
}
const authUrl = baseUrl + "/passwordless"
const responseType = "code";
const redirectUri = getRedirectUri();
const scope = "offline_access%20photos%20files";
const state = generateSessionState();
setCookie("webauth-state", state, 5);
fillExample();
</script>
</body>
</html>