mirror of
https://github.com/teamhanko/hanko.git
synced 2025-10-28 06:37:57 +08:00
396 lines
14 KiB
HTML
396 lines
14 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/HttpClient.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="EmailClient.html">EmailClient</a></li><li><a href="PasscodeClient.html">PasscodeClient</a></li><li><a href="PasswordClient.html">PasswordClient</a></li><li><a href="ThirdPartyClient.html">ThirdPartyClient</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="EmailAddressAlreadyExistsError.html">EmailAddressAlreadyExistsError</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="MaxNumOfEmailAddressesReachedError.html">MaxNumOfEmailAddressesReachedError</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="ThirdPartyError.html">ThirdPartyError</a></li><li><a href="TooManyRequestsError.html">TooManyRequestsError</a></li><li><a href="UnauthorizedError.html">UnauthorizedError</a></li><li><a href="UserVerificationError.html">UserVerificationError</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="Email.html">Email</a></li><li><a href="EmailConfig.html">EmailConfig</a></li><li><a href="Emails.html">Emails</a></li><li><a href="Identity.html">Identity</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="WebauthnCredential.html">WebauthnCredential</a></li><li><a href="WebauthnCredentials.html">WebauthnCredentials</a></li><li><a href="WebauthnFinalized.html">WebauthnFinalized</a></li><li><a href="WebauthnTransports.html">WebauthnTransports</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/HttpClient.ts</h1>
|
|
</header>
|
|
|
|
|
|
|
|
|
|
|
|
<section>
|
|
<article>
|
|
<pre class="prettyprint source linenums"><code>import Cookies from "js-cookie";
|
|
import { RequestTimeoutError, TechnicalError } from "../Errors";
|
|
|
|
/**
|
|
* This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.
|
|
*
|
|
* @category SDK
|
|
* @subcategory Internal
|
|
* @param {XMLHttpRequest} xhr - The request to be wrapped.
|
|
* @see HttpClient
|
|
*/
|
|
class Headers {
|
|
_xhr: XMLHttpRequest;
|
|
|
|
// eslint-disable-next-line require-jsdoc
|
|
constructor(xhr: XMLHttpRequest) {
|
|
this._xhr = xhr;
|
|
}
|
|
|
|
/**
|
|
* Returns the response header with the given name.
|
|
*
|
|
* @param {string} name
|
|
* @return {string}
|
|
*/
|
|
get(name: string) {
|
|
return this._xhr.getResponseHeader(name);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This class wraps an XMLHttpRequest to maintain compatibility with the fetch API.
|
|
*
|
|
* @category SDK
|
|
* @subcategory Internal
|
|
* @param {XMLHttpRequest} xhr - The request to be wrapped.
|
|
* @see HttpClient
|
|
*/
|
|
class Response {
|
|
headers: Headers;
|
|
ok: boolean;
|
|
status: number;
|
|
statusText: string;
|
|
url: string;
|
|
_decodedJSON: any;
|
|
private xhr: XMLHttpRequest;
|
|
|
|
// eslint-disable-next-line require-jsdoc
|
|
constructor(xhr: XMLHttpRequest) {
|
|
/**
|
|
* @public
|
|
* @type {Headers}
|
|
*/
|
|
this.headers = new Headers(xhr);
|
|
/**
|
|
* @public
|
|
* @type {boolean}
|
|
*/
|
|
this.ok = xhr.status >= 200 && xhr.status <= 299;
|
|
/**
|
|
* @public
|
|
* @type {number}
|
|
*/
|
|
this.status = xhr.status;
|
|
/**
|
|
* @public
|
|
* @type {string}
|
|
*/
|
|
this.statusText = xhr.statusText;
|
|
/**
|
|
* @public
|
|
* @type {string}
|
|
*/
|
|
this.url = xhr.responseURL;
|
|
/**
|
|
* @private
|
|
* @type {XMLHttpRequest}
|
|
*/
|
|
this.xhr = xhr;
|
|
}
|
|
|
|
/**
|
|
* Returns the JSON decoded response.
|
|
*
|
|
* @return {any}
|
|
*/
|
|
json() {
|
|
if (!this._decodedJSON) {
|
|
this._decodedJSON = JSON.parse(this.xhr.response);
|
|
}
|
|
return this._decodedJSON;
|
|
}
|
|
|
|
/**
|
|
* Returns the value for Retry-After contained in the response header.
|
|
*
|
|
* @return {number}
|
|
*/
|
|
parseRetryAfterHeader(): number {
|
|
const result = parseInt(this.headers.get("Retry-After"), 10);
|
|
return isNaN(result) ? 0 : result;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Internally used for communication with the Hanko API. It also handles authorization tokens to enable authorized
|
|
* requests.
|
|
*
|
|
* Currently, there is an issue with Safari and on iOS 15 devices where decoding a JSON response via the fetch API
|
|
* breaks the user gesture and the user is not able to use the authenticator. Therefore, this class uses XMLHttpRequests
|
|
* instead of the fetch API, but maintains compatibility by wrapping the XMLHttpRequests. So, if the issues are fixed,
|
|
* we can easily return to the fetch API.
|
|
*
|
|
* @category SDK
|
|
* @subcategory Internal
|
|
* @param {string} api - The URL of your Hanko API instance
|
|
* @param {number=} timeout - The request timeout in milliseconds
|
|
*/
|
|
class HttpClient {
|
|
timeout: number;
|
|
api: string;
|
|
authCookieName = "hanko";
|
|
|
|
// eslint-disable-next-line require-jsdoc
|
|
constructor(api: string, timeout = 13000) {
|
|
this.api = api;
|
|
this.timeout = timeout;
|
|
}
|
|
|
|
// eslint-disable-next-line require-jsdoc
|
|
_fetch(path: string, options: RequestInit, xhr = new XMLHttpRequest()) {
|
|
const self = this;
|
|
const url = this.api + path;
|
|
const timeout = this.timeout;
|
|
const bearerToken = this._getAuthCookie();
|
|
|
|
return new Promise<Response>(function (resolve, reject) {
|
|
xhr.open(options.method, url, true);
|
|
xhr.setRequestHeader("Accept", "application/json");
|
|
xhr.setRequestHeader("Content-Type", "application/json");
|
|
|
|
if (bearerToken) {
|
|
xhr.setRequestHeader("Authorization", `Bearer ${bearerToken}`);
|
|
}
|
|
|
|
xhr.timeout = timeout;
|
|
xhr.withCredentials = true;
|
|
xhr.onload = () => {
|
|
const headers = xhr
|
|
.getAllResponseHeaders()
|
|
.split("\r\n")
|
|
.filter((h) => h.toLowerCase().startsWith("x-auth-token"));
|
|
|
|
if (headers.length) {
|
|
const authToken = xhr.getResponseHeader("X-Auth-Token");
|
|
if (authToken) self._setAuthCookie(authToken);
|
|
}
|
|
|
|
resolve(new Response(xhr));
|
|
};
|
|
|
|
xhr.onerror = () => {
|
|
reject(new TechnicalError());
|
|
};
|
|
|
|
xhr.ontimeout = () => {
|
|
reject(new RequestTimeoutError());
|
|
};
|
|
|
|
xhr.send(options.body ? options.body.toString() : null);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Returns the authentication token that was stored in the cookie.
|
|
*
|
|
* @return {string}
|
|
* @return {string}
|
|
*/
|
|
_getAuthCookie(): string {
|
|
return Cookies.get(this.authCookieName);
|
|
}
|
|
|
|
/**
|
|
* Stores the authentication token to the cookie.
|
|
*
|
|
* @param {string} token - The authentication token to be stored.
|
|
*/
|
|
_setAuthCookie(token: string) {
|
|
const secure = !!this.api.match("^https://");
|
|
Cookies.set(this.authCookieName, token, { secure });
|
|
}
|
|
|
|
/**
|
|
* Removes the cookie used for authentication.
|
|
*
|
|
* @param {string} token - The authorization token to be stored.
|
|
*/
|
|
removeAuthCookie() {
|
|
Cookies.remove(this.authCookieName);
|
|
}
|
|
|
|
/**
|
|
* Performs a GET request.
|
|
*
|
|
* @param {string} path - The path to the requested resource.
|
|
* @return {Promise<Response>}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
get(path: string) {
|
|
return this._fetch(path, { method: "GET" });
|
|
}
|
|
|
|
/**
|
|
* Performs a POST request.
|
|
*
|
|
* @param {string} path - The path to the requested resource.
|
|
* @param {any=} body - The request body.
|
|
* @return {Promise<Response>}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
post(path: string, body?: any) {
|
|
return this._fetch(path, {
|
|
method: "POST",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Performs a PUT request.
|
|
*
|
|
* @param {string} path - The path to the requested resource.
|
|
* @param {any=} body - The request body.
|
|
* @return {Promise<Response>}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
put(path: string, body?: any) {
|
|
return this._fetch(path, {
|
|
method: "PUT",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Performs a PATCH request.
|
|
*
|
|
* @param {string} path - The path to the requested resource.
|
|
* @param {any=} body - The request body.
|
|
* @return {Promise<Response>}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
patch(path: string, body?: any) {
|
|
return this._fetch(path, {
|
|
method: "PATCH",
|
|
body: JSON.stringify(body),
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Performs a DELETE request.
|
|
*
|
|
* @param {string} path - The path to the requested resource.
|
|
* @return {Promise<Response>}
|
|
* @throws {RequestTimeoutError}
|
|
* @throws {TechnicalError}
|
|
*/
|
|
delete(path: string) {
|
|
return this._fetch(path, {
|
|
method: "DELETE",
|
|
});
|
|
}
|
|
}
|
|
|
|
export { Headers, Response, HttpClient };
|
|
</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>
|