mirror of
https://github.com/teamhanko/hanko.git
synced 2025-10-29 07:40:07 +08:00
326 lines
13 KiB
HTML
326 lines
13 KiB
HTML
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title> lib/client/WebauthnClient.ts</title>
|
|
|
|
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
|
|
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
|
|
<script src="./build/entry.js"></script>
|
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
|
<!--[if lt IE 9]>
|
|
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
|
|
<![endif]-->
|
|
<link href="https://fonts.googleapis.com/css?family=Roboto:100,400,700|Inconsolata,700" rel="stylesheet">
|
|
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css" integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
|
|
<link type="text/css" rel="stylesheet" href="https://jmblog.github.io/color-themes-for-google-code-prettify/themes/tomorrow-night.min.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/app.min.css">
|
|
<link type="text/css" rel="stylesheet" href="styles/iframe.css">
|
|
<link type="text/css" rel="stylesheet" href="">
|
|
<script async defer src="https://buttons.github.io/buttons.js"></script>
|
|
|
|
|
|
</head>
|
|
|
|
|
|
|
|
<body class="layout small-header">
|
|
<div id="stickyNavbarOverlay"></div>
|
|
|
|
|
|
<div class="top-nav">
|
|
<div class="inner">
|
|
<a id="hamburger" role="button" class="navbar-burger" aria-label="menu" aria-expanded="false">
|
|
<span aria-hidden="true"></span>
|
|
<span aria-hidden="true"></span>
|
|
<span aria-hidden="true"></span>
|
|
</a>
|
|
<div class="logo">
|
|
|
|
|
|
</div>
|
|
<div class="menu">
|
|
|
|
<div class="navigation">
|
|
<a
|
|
href="index.html"
|
|
class="link"
|
|
>
|
|
Documentation
|
|
</a>
|
|
|
|
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div id="main">
|
|
<div
|
|
class="sidebar "
|
|
id="sidebarNav"
|
|
>
|
|
|
|
<nav>
|
|
|
|
<h2><a href="index.html">Documentation</a></h2><div class="category"><h3>Classes</h3><ul><li><a href="Hanko.html">Hanko</a></li></ul></div><div class="category"><h2>SDK</h2><h3>Classes / Internal</h3><ul><li><a href="Client.html">Client</a></li><li><a href="Headers.html">Headers</a></li><li><a href="HttpClient.html">HttpClient</a></li><li><a href="PasscodeState.html">PasscodeState</a></li><li><a href="PasswordState.html">PasswordState</a></li><li><a href="Response.html">Response</a></li><li><a href="State.html">State</a></li><li><a href="UserState.html">UserState</a></li><li><a href="WebauthnState.html">WebauthnState</a></li></ul><h3>Classes / Clients</h3><ul><li><a href="ConfigClient.html">ConfigClient</a></li><li><a href="PasscodeClient.html">PasscodeClient</a></li><li><a href="PasswordClient.html">PasswordClient</a></li><li><a href="UserClient.html">UserClient</a></li><li><a href="WebauthnClient.html">WebauthnClient</a></li></ul><h3>Classes / Errors</h3><ul><li><a href="ConflictError.html">ConflictError</a></li><li><a href="HankoError.html">HankoError</a></li><li><a href="InvalidPasscodeError.html">InvalidPasscodeError</a></li><li><a href="InvalidPasswordError.html">InvalidPasswordError</a></li><li><a href="InvalidWebauthnCredentialError.html">InvalidWebauthnCredentialError</a></li><li><a href="MaxNumOfPasscodeAttemptsReachedError.html">MaxNumOfPasscodeAttemptsReachedError</a></li><li><a href="NotFoundError.html">NotFoundError</a></li><li><a href="PasscodeExpiredError.html">PasscodeExpiredError</a></li><li><a href="RequestTimeoutError.html">RequestTimeoutError</a></li><li><a href="TechnicalError.html">TechnicalError</a></li><li><a href="TooManyRequestsError.html">TooManyRequestsError</a></li><li><a href="UnauthorizedError.html">UnauthorizedError</a></li><li><a href="WebauthnRequestCancelledError.html">WebauthnRequestCancelledError</a></li></ul><h3>Classes / Utilities</h3><ul><li><a href="WebauthnSupport.html">WebauthnSupport</a></li></ul><h3>Interfaces / DTO</h3><ul><li><a href="Config.html">Config</a></li><li><a href="Credential.html">Credential</a></li><li><a href="Passcode.html">Passcode</a></li><li><a href="PasswordConfig.html">PasswordConfig</a></li><li><a href="User.html">User</a></li><li><a href="UserInfo.html">UserInfo</a></li><li><a href="WebauthnFinalized.html">WebauthnFinalized</a></li></ul><h3>Interfaces / Internal</h3><ul><li><a href="LocalStorage.html">LocalStorage</a></li><li><a href="LocalStoragePasscode.html">LocalStoragePasscode</a></li><li><a href="LocalStoragePassword.html">LocalStoragePassword</a></li><li><a href="LocalStorageUser.html">LocalStorageUser</a></li><li><a href="LocalStorageUsers.html">LocalStorageUsers</a></li><li><a href="LocalStorageWebauthn.html">LocalStorageWebauthn</a></li></ul></div>
|
|
|
|
</nav>
|
|
</div>
|
|
<div class="core" id="main-content-wrapper">
|
|
<div class="content">
|
|
<header class="page-title">
|
|
<p>Source</p>
|
|
<h1>lib/client/WebauthnClient.ts</h1>
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>import { WebauthnState } from "../state/WebauthnState";
|
|
import {
|
|
InvalidWebauthnCredentialError,
|
|
TechnicalError,
|
|
UnauthorizedError,
|
|
WebauthnRequestCancelledError,
|
|
} from "../Errors";
|
|
import {
|
|
create as createWebauthnCredential,
|
|
get as getWebauthnCredential,
|
|
CredentialCreationOptionsJSON,
|
|
CredentialRequestOptionsJSON,
|
|
PublicKeyCredentialWithAssertionJSON,
|
|
} from "@github/webauthn-json";
|
|
import { Attestation, User, WebauthnFinalized } from "../Dto";
|
|
import { WebauthnSupport } from "../WebauthnSupport";
|
|
import { Client } from "./Client";
|
|
|
|
/**
|
|
* A class that handles WebAuthn authentication and registration.
|
|
*
|
|
* @constructor
|
|
* @category SDK
|
|
* @subcategory Clients
|
|
* @extends {Client}
|
|
*/
|
|
class WebauthnClient extends Client {
|
|
private state: WebauthnState;
|
|
controller: AbortController;
|
|
|
|
// eslint-disable-next-line require-jsdoc
|
|
constructor(api: string, timeout: number) {
|
|
super(api, timeout);
|
|
/**
|
|
* @private
|
|
* @type {WebauthnState}
|
|
*/
|
|
this.state = new WebauthnState();
|
|
/**
|
|
* @public
|
|
* @type {AbortController}
|
|
*/
|
|
this.controller = new AbortController();
|
|
}
|
|
|
|
/**
|
|
* Performs a WebAuthn authentication ceremony. When 'userID' is specified, the API provides a list of
|
|
* allowed credentials and the browser is able to present a list of suitable credentials to the user.
|
|
*
|
|
* @param {string=} userID - The user's UUID.
|
|
* @param {boolean=} useConditionalMediation - Enables autofill assisted login.
|
|
* @return {Promise<void>}
|
|
* @throws {WebauthnRequestCancelledError}
|
|
* @throws {InvalidWebauthnCredentialError}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
* @see https://docs.hanko.io/api/public#tag/WebAuthn/operation/webauthnLoginInit
|
|
* @see https://docs.hanko.io/api/public#tag/WebAuthn/operation/webauthnLoginFinal
|
|
* @see https://www.w3.org/TR/webauthn-2/#authentication-ceremony
|
|
*/
|
|
login(userID?: string, useConditionalMediation?: boolean): Promise<void> {
|
|
return new Promise<void>((resolve, reject) => {
|
|
this.client
|
|
.post("/webauthn/login/initialize", { user_id: userID })
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
return response.json();
|
|
}
|
|
|
|
throw new TechnicalError();
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
})
|
|
.then((challenge: CredentialRequestOptionsJSON) => {
|
|
if (useConditionalMediation) {
|
|
challenge.mediation =
|
|
"conditional" as CredentialMediationRequirement;
|
|
}
|
|
challenge.signal = this.controller.signal;
|
|
return getWebauthnCredential(challenge);
|
|
})
|
|
.catch((e) => {
|
|
throw new WebauthnRequestCancelledError(e);
|
|
})
|
|
.then((assertion: PublicKeyCredentialWithAssertionJSON) => {
|
|
return this.client.post("/webauthn/login/finalize", assertion);
|
|
})
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
return response.json();
|
|
} else if (response.status === 400 || response.status === 401) {
|
|
throw new InvalidWebauthnCredentialError();
|
|
} else {
|
|
throw new TechnicalError();
|
|
}
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
})
|
|
.then((w: WebauthnFinalized) => {
|
|
this.state.read().addCredential(w.user_id, w.credential_id).write();
|
|
return resolve();
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Performs a WebAuthn registration ceremony.
|
|
*
|
|
* @return {Promise<void>}
|
|
* @throws {WebauthnRequestCancelledError}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {UnauthorizedError}
|
|
* @throws {TechnicalError}
|
|
* @see https://docs.hanko.io/api/public#tag/WebAuthn/operation/webauthnRegInit
|
|
* @see https://docs.hanko.io/api/public#tag/WebAuthn/operation/webauthnRegFinal
|
|
* @see https://www.w3.org/TR/webauthn-2/#sctn-registering-a-new-credential
|
|
*/
|
|
register(): Promise<void> {
|
|
return new Promise((resolve, reject) => {
|
|
this.client
|
|
.post("/webauthn/registration/initialize")
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
return response.json();
|
|
} else if (response.status >= 400 && response.status <= 499) {
|
|
throw new UnauthorizedError();
|
|
}
|
|
|
|
throw new TechnicalError();
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
})
|
|
.then((challenge: CredentialCreationOptionsJSON) => {
|
|
return createWebauthnCredential(challenge);
|
|
})
|
|
.catch((e) => {
|
|
reject(new WebauthnRequestCancelledError(e));
|
|
})
|
|
.then((attestation: Attestation) => {
|
|
// The generated PublicKeyCredentialWithAttestationJSON object does not align with the API. The list of
|
|
// supported transports must be available under a different path.
|
|
attestation.transports = attestation.response.transports;
|
|
|
|
return this.client.post(
|
|
"/webauthn/registration/finalize",
|
|
attestation
|
|
);
|
|
})
|
|
.then((response) => {
|
|
if (response.ok) {
|
|
return response.json();
|
|
} else if (response.status >= 400 && response.status <= 499) {
|
|
throw new UnauthorizedError();
|
|
}
|
|
|
|
throw new TechnicalError();
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
})
|
|
.then((w: WebauthnFinalized) => {
|
|
this.state.read().addCredential(w.user_id, w.credential_id).write();
|
|
|
|
return resolve();
|
|
})
|
|
.catch((e) => {
|
|
reject(e);
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Determines whether a credential registration ceremony should be performed. Returns 'true' when a platform
|
|
* authenticator is available and the user's credentials do not intersect with the credentials already known on the
|
|
* current browser/device.
|
|
*
|
|
* @param {User} user - The user object.
|
|
* @return {Promise<boolean>}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
shouldRegister(user: User): Promise<boolean> {
|
|
return new Promise<boolean>((resolve, reject) => {
|
|
WebauthnSupport.isPlatformAuthenticatorAvailable()
|
|
.then((supported) => {
|
|
if (!user.webauthn_credentials || !user.webauthn_credentials.length) {
|
|
return resolve(supported);
|
|
}
|
|
|
|
const matches = this.state
|
|
.read()
|
|
.matchCredentials(user.id, user.webauthn_credentials);
|
|
|
|
return resolve(supported && !matches.length);
|
|
})
|
|
.catch((e) => {
|
|
reject(new TechnicalError(e));
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
export { WebauthnClient };
|
|
</code></pre>
|
|
</article>
|
|
</section>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
<footer class="footer">
|
|
<div class="content has-text-centered">
|
|
<p>Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.11</a></p>
|
|
<p class="sidebar-created-by">
|
|
<a href="https://github.com/SoftwareBrothers/better-docs" target="_blank">BetterDocs theme</a> provided with <i class="fas fa-heart"></i> by
|
|
<a href="http://softwarebrothers.co" target="_blank">SoftwareBrothers - JavaScript Development Agency</a>
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
|
|
</div>
|
|
<div id="side-nav" class="side-nav">
|
|
</div>
|
|
</div>
|
|
<script src="scripts/app.min.js"></script>
|
|
<script>PR.prettyPrint();</script>
|
|
<script src="scripts/linenumber.js"> </script>
|
|
|
|
|
|
</body>
|
|
</html>
|