mirror of
https://github.com/ysoftdevs/oauth-playground-server.git
synced 2026-01-14 15:53:50 +01:00
Paswordless UI redone
This commit is contained in:
@@ -12,138 +12,374 @@
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
font-family: monospace;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.step {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.button-label {
|
||||
padding: 20px 0 5px 0;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.collection {
|
||||
margin: .5rem 0 1rem 0;
|
||||
border: 1px solid #e0e0e0;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
{/add-header}
|
||||
|
||||
<div class="container">
|
||||
<div class="section" id="init">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>Create a new credential or use an existing one</h6>
|
||||
<p>First the user enters their username:</p>
|
||||
<input id="userName" placeholder="User name"/><br/>
|
||||
|
||||
<p>If they are a new user, they need to generate a new credential and register it with the application</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width" id="register">Register</button>
|
||||
|
||||
<p>If they are a returning user, their authenticator must prove they own the credential.</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width" id="login">Login</button>
|
||||
<div class="row" id="init">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>1.</b> Create a new registration or use an existing one</h6>
|
||||
<p>First the user enters their username:</p>
|
||||
<input id="userName" placeholder="User name"/><br/>
|
||||
<p class="button-label">New user needs to generate a new credential and register it with the application.</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width" id="register">Register</button>
|
||||
<p class="button-label">Returning user must prove they own the credential.</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width" id="login">Login</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section step" id="server1-request">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>Request a challenge</h6>
|
||||
<p>The interaction starts with an AJAX call.</p>
|
||||
<div class="code">POST <span id="server1-url"></span><br><div id="server1-call"></div></div>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Request challenge</button>
|
||||
<div class="row step" id="server1-request">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>2.</b> Request a challenge</h6>
|
||||
<p>The interaction starts with an AJAX call:</p>
|
||||
<div class="code">POST <span id="server1-url"></span><br><div id="server1-call"></div></div>
|
||||
<p class="button-label">Basically we are just specifying the username and his display name</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Request challenge</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section step" id="server1-response">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>The challenge</h6>
|
||||
<p>The server prepares a challenge for the browser to sign.</p>
|
||||
<div class="code" id="server1-response-body"></div>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Continue</button>
|
||||
<div class="row step" id="server1-response">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>3.</b> The challenge</h6>
|
||||
<p>The server prepares a challenge for the browser to sign:</p>
|
||||
<div class="code" id="server1-response-body"></div>
|
||||
<p class="button-label">Let's break it down...</p>
|
||||
<ul id="register-challenge-breakdown" class="collection" style="display: none;">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>rp</b></p>
|
||||
<p>
|
||||
Information about the relying party. The ID of the relying party is usually the domain name of the website or service trying to authenticate the user. Name contains human-friendly name for the relying party.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">user</p>
|
||||
<p>
|
||||
Information about the user: <b>id</b> contains unique identifier in the form of base64 encoded byte array. Parameters <b>name</b> and <b>displayName</b> are taken from the initial request.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">challenge</p>
|
||||
<p>
|
||||
A random value generated by the relying party. It's used to ensure that the authentication request is fresh and not a replay of a previous one.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">pubKeyCredParams</p>
|
||||
<p>
|
||||
A list of public key credential types and cryptographic algorithm combinations supported by the relying party.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">authenticatorSelection</p>
|
||||
<p>Criteria for the authenticator:</p>
|
||||
<p>
|
||||
<b>requireResidentKey:</b> This indicates if the credential should be stored on the authenticator (like a hardware token or platform). Here, it's set to "false", meaning resident keys aren't mandatory.
|
||||
</p>
|
||||
<p>
|
||||
<b>userVerification:</b> This describes the desired user verification method. "discouraged" means the relying party does not want user verification employed during the creation process.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">timeout</p>
|
||||
<p>
|
||||
Maximum time, in milliseconds, that the caller is willing to wait for the call to complete.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">attestation</p>
|
||||
<p>
|
||||
Specifies the attestation conveyance preference. "none" means the relying party is not interested in authenticator attestation.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">extensions</p>
|
||||
<p>
|
||||
Dictionary of extension identifiers with their associated data. In this case, there's an extension "txAuthSimple" with no associated data provided.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<ul id="login-challenge-breakdown" class="collection" style="display: none;">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">challenge</p>
|
||||
<p>
|
||||
A random value generated by the relying party. It's used to ensure that the authentication request is fresh and not a replay of a previous one.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">timeout</p>
|
||||
<p>
|
||||
Specifies the time, in milliseconds, that the caller is willing to wait for the call to complete.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">rpId</p>
|
||||
<p>
|
||||
The Relying Party Identifier. It helps the client (browser) know which key to use if there's more than one for a given domain.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">userVerification</p>
|
||||
<p>
|
||||
This specifies the desired user verification method. The value "discouraged" suggests that the website doesn't require a strong verification of the user, which means if the authenticator has a user verification method (like a fingerprint scanner or PIN), it's not mandatory to use it for this authentication.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">extensions</p>
|
||||
<p>
|
||||
Dictionary of extension identifiers with their associated data. In this case, there's an extension "txAuthSimple" with no associated data provided.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">allowCredentials</p>
|
||||
<p>
|
||||
An array containing one or more credentials that are allowed for the authentication:
|
||||
</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">type</p>
|
||||
<p>
|
||||
Specifies the type of the public key credential.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">id</p>
|
||||
<p>
|
||||
The credential ID that identifies the public key to be used during the authentication.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis">transports</p>
|
||||
<p>
|
||||
Lists the allowed transports to retrieve the credential ID. This is useful for guiding the user's client (browser) about how it can communicate with the authenticator.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Continue</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section step" id="navigator-request">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>WebAuthn API</h6>
|
||||
<p>The challenge is passed to the Javascript call:</p>
|
||||
<div class="code" id="navigator-call"></div>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Call Webauthn API</button>
|
||||
<div class="row step" id="navigator-request">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>4.</b> WebAuthn API</h6>
|
||||
<p>The challenge is passed to the Javascript call:</p>
|
||||
<div class="code" id="navigator-call"></div>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Call Webauthn API</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section step" id="navigator-attestation">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>Attestation</h6>
|
||||
Which responds:
|
||||
<div class="code" id="navigator-attestation-body"></div>
|
||||
The <strong>response.clientDataJSON</strong> is base64 encoded:
|
||||
<div class="code" id="navigator-attestation-clientDataJSON"></div>
|
||||
The <strong>response.attestationObject</strong> is <a href="https://cbor.io/">CBOR</a> encoded and contains a public key and metadata:
|
||||
<div class="code" id="navigator-attestationObject" style="height: 150px;"></div>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Finish the interaction</button>
|
||||
<div class="row step" id="navigator-attestation">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>5.</b> Attestation</h6>
|
||||
<p style="padding-bottom: 5px;">Which responds:</p>
|
||||
<div class="code" id="navigator-attestation-body"></div>
|
||||
<p class="button-label">Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>id</b></p>
|
||||
<p>
|
||||
A base64url encoded string representation of the rawId. It uniquely identifies the created credential.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>rawId</b></p>
|
||||
<p>
|
||||
The raw identifier of the credential, typically a byte array. It's the same as the id but in its raw binary form.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>attestationObject</b></p>
|
||||
<p>
|
||||
This is a base64 encoded binary representation of an attestation statement. The attestation statement is produced by the authenticator to prove to the relying party (e.g., a website) that a new public key credential has been created in the authenticator. It typically contains details about the authenticator, a freshly generated public key for the user, some metadata, and a signature from the authenticator. It is <a href="https://cbor.io/">CBOR</a> encoded.
|
||||
</p>
|
||||
<div class="code" id="navigator-attestationObject" style="height: 150px; overflow-y: scroll;"></div>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>clientDataJSON</b></p>
|
||||
<p>
|
||||
This is a base64 encoded JSON string that contains information about the context in which the attestation was generated. Let's break down its decoded content:
|
||||
</p>
|
||||
<div class="code" id="navigator-attestation-clientDataJSON"></div>
|
||||
<p class="button-label">Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>challenge</b></p>
|
||||
<p>
|
||||
This is the challenge that was sent by the relying party during the registration request. The authenticator's response must include this challenge to ensure the action was based on a recent request and not a replayed one.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>origin</b></p>
|
||||
<p>
|
||||
The origin of the request, indicating where the request came from.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>type</b></p>
|
||||
<p>
|
||||
This indicates the type of operation, which in this context is "webauthn.create", meaning it's a credential creation operation.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>type</b></p>
|
||||
<p>
|
||||
This specifies the type of the public key credential, and it's set to "public-key" which means it's a public key credential.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="button-label">We finish the interaction by...</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Finish the interaction</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="section step" id="navigator-assertion">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6>Assertion</h6>
|
||||
Which responds:
|
||||
<div class="code" id="navigator-assertion-body"></div>
|
||||
The <strong>response.clientDataJSON</strong> is base64 encoded:
|
||||
<div class="code" id="navigator-assertion-clientDataJSON"></div>
|
||||
The <strong>response.signature</strong> is derived from <strong>clientDataJSON</strong> and <strong>authenticatorData</strong>, using the private key stored on authenticator and validated public key stored on the server
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Finish the interaction</button>
|
||||
<div class="row step" id="navigator-assertion">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<h6 class="card-header"><b>5.</b> Attestation</h6>
|
||||
<p style="padding-bottom: 5px;">Which responds:</p>
|
||||
<div class="code" id="navigator-assertion-body"></div>
|
||||
<p class="button-label">Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>id</b></p>
|
||||
<p>
|
||||
A base64url encoded string representation of the rawId. It uniquely identifies the created credential.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>rawId</b></p>
|
||||
<p>
|
||||
The raw identifier of the credential, typically a byte array. It's the same as the id but in its raw binary form.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>clientDataJSON</b></p>
|
||||
<p>
|
||||
This is a base64 encoded JSON string that contains information about the context in which the attestation was generated. Let's break down its decoded content:
|
||||
</p>
|
||||
<div class="code" id="navigator-assertion-clientDataJSON"></div>
|
||||
<p class="button-label">Let's break it down...</p>
|
||||
<ul class="collection">
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>challenge</b></p>
|
||||
<p>
|
||||
This is the challenge that was sent by the relying party during the registration request. The authenticator's response must include this challenge to ensure the action was based on a recent request and not a replayed one.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>origin</b></p>
|
||||
<p>
|
||||
The origin of the request, indicating where the request came from.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>type</b></p>
|
||||
<p>
|
||||
This indicates the type of operation, which in this context is "webauthn.create", meaning it's a credential creation operation.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>authenticatorData</b></p>
|
||||
<p>
|
||||
This contains information about the authentication event. It typically includes the hash of the <b>clientDataJSON</b>, a sign count (to protect against clone attacks), and other data relevant to the authentication process.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>signature</b></p>
|
||||
<p>
|
||||
This is the authenticator's digital signature over the combination of the <b>authenticatorData</b> and the hash of the <b>clientDataJSON</b>. The website will use this to verify that the response came from the user's authenticator and corresponds to the challenge it issued.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>userHandle</b></p>
|
||||
<p>
|
||||
An optional field which, if present, represents a unique identifier for the user, established during registration. It helps websites identify which user is trying to authenticate, especially in scenarios where the user doesn't explicitly provide a username during the login process.
|
||||
</p>
|
||||
</li>
|
||||
<li class="collection-item">
|
||||
<p class="emphasis"><b>type</b></p>
|
||||
<p>
|
||||
This specifies the type of the public key credential, and it's set to "public-key" which means it's a public key credential.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
<p class="button-label">We finish the interaction by...</p>
|
||||
<button class="nextBtn waves-effect waves-light btn full-width">Finish the interaction</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section step" id="errors">
|
||||
<div class="col s12 m7">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<div id="result"></div>
|
||||
<div id="trace"></div>
|
||||
<button class="resetBtn waves-effect waves-light btn full-width">Start over</button>
|
||||
<div class="row step" id="errors">
|
||||
<div class="col s12 m8 offset-m2">
|
||||
<div class="card horizontal">
|
||||
<div class="card-stacked">
|
||||
<div class="card-content">
|
||||
<div id="result"></div>
|
||||
<div id="trace"></div>
|
||||
<button class="resetBtn waves-effect waves-light btn full-width">Start over</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form action="#" method="POST">
|
||||
<input name="sessionId" type="hidden" value="{sessionId}">
|
||||
<div id="form-generated"></div>
|
||||
</form>
|
||||
<form action="#" method="POST">
|
||||
<input name="sessionId" type="hidden" value="{sessionId}">
|
||||
<div id="form-generated"></div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -216,6 +452,8 @@
|
||||
function traceRegisterResponse(params) {
|
||||
$("#server1-response").showInViewport();
|
||||
$("#server1-response-body").html(JSON.stringify(params, null, 2));
|
||||
$("#register-challenge-breakdown").show();
|
||||
|
||||
return continueButton("#server1-response", params);
|
||||
}
|
||||
|
||||
@@ -230,6 +468,7 @@
|
||||
function traceLoginResponse(params) {
|
||||
$("#server1-response").showInViewport();
|
||||
$("#server1-response-body").html(JSON.stringify(params, null, 2));
|
||||
$("#login-challenge-breakdown").show();
|
||||
return continueButton("#server1-response", params);
|
||||
}
|
||||
|
||||
@@ -287,8 +526,6 @@
|
||||
callbackPath: '/q/webauthn/callback',
|
||||
registerPath: '/q/webauthn/register',
|
||||
loginPath: '/q/webauthn/login',
|
||||
// loginCallbackPath: '/webauthn/login',
|
||||
// registerCallbackPath: '/webauthn/register',
|
||||
debuggingFunction: tracer
|
||||
});
|
||||
|
||||
@@ -323,12 +560,9 @@
|
||||
|
||||
registerButton.onclick = () => {
|
||||
var userName = document.getElementById('userName').value;
|
||||
// var firstName = document.getElementById('firstName').value;
|
||||
// var lastName = document.getElementById('lastName').value;
|
||||
result.replaceChildren();
|
||||
|
||||
|
||||
webAuthn.registerOnly({ name: userName, displayName: userName /*firstName + " " + lastName*/})
|
||||
webAuthn.registerOnly({ name: userName, displayName: userName})
|
||||
.then(body => {
|
||||
form("/auth/passwordless/register", {
|
||||
'webAuthnId': body.id,
|
||||
@@ -347,4 +581,3 @@
|
||||
};
|
||||
</script>
|
||||
{/include}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
background-color: #007BFF;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
.btn:hover, .btn:focus {
|
||||
background-color: #0056b3
|
||||
}
|
||||
|
||||
@@ -56,6 +56,11 @@
|
||||
.collection-item {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.emphasis {
|
||||
color: #0056b3;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
{#insert add-header}{/}
|
||||
</head>
|
||||
|
||||
Reference in New Issue
Block a user