55 lines
2.8 KiB
JavaScript
55 lines
2.8 KiB
JavaScript
import camelize from "camelize-ts";
|
|
import { defaultBaseUrl, defaultRealm } from "./constants.js";
|
|
import { fetchWithError } from "./fetchWithError.js";
|
|
import { stringifyQueryParams } from "./stringifyQueryParams.js";
|
|
// See: https://developer.mozilla.org/en-US/docs/Glossary/Base64
|
|
const bytesToBase64 = (bytes) => btoa(Array.from(bytes, (byte) => String.fromCodePoint(byte)).join(""));
|
|
const toBase64 = (input) => bytesToBase64(new TextEncoder().encode(input));
|
|
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986
|
|
const encodeRFC3986URIComponent = (input) => encodeURIComponent(input).replace(/[!'()*]/g, (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}`);
|
|
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent
|
|
// Specifically, the section on encoding `application/x-www-form-urlencoded`.
|
|
const encodeFormURIComponent = (data) => encodeRFC3986URIComponent(data).replaceAll("%20", "+");
|
|
export const getToken = async (settings) => {
|
|
// Construct URL
|
|
const baseUrl = settings.baseUrl || defaultBaseUrl;
|
|
const realmName = settings.realmName || defaultRealm;
|
|
const url = `${baseUrl}/realms/${realmName}/protocol/openid-connect/token`;
|
|
// Prepare credentials for openid-connect token request
|
|
// ref: http://openid.net/specs/openid-connect-core-1_0.html#TokenEndpoint
|
|
const credentials = settings.credentials || {};
|
|
const payload = stringifyQueryParams({
|
|
username: credentials.username,
|
|
password: credentials.password,
|
|
grant_type: credentials.grantType,
|
|
client_id: credentials.clientId,
|
|
totp: credentials.totp,
|
|
...(credentials.offlineToken ? { scope: "offline_access" } : {}),
|
|
...(credentials.scopes ? { scope: credentials.scopes.join(" ") } : {}),
|
|
...(credentials.refreshToken
|
|
? {
|
|
refresh_token: credentials.refreshToken,
|
|
client_secret: credentials.clientSecret,
|
|
}
|
|
: {}),
|
|
});
|
|
const options = settings.requestOptions ?? {};
|
|
const headers = new Headers(options.headers);
|
|
if (credentials.clientSecret) {
|
|
// See: https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1
|
|
const username = encodeFormURIComponent(credentials.clientId);
|
|
const password = encodeFormURIComponent(credentials.clientSecret);
|
|
// See: https://datatracker.ietf.org/doc/html/rfc2617#section-2
|
|
headers.set("authorization", `Basic ${toBase64(`${username}:${password}`)}`);
|
|
}
|
|
headers.set("content-type", "application/x-www-form-urlencoded");
|
|
const response = await fetchWithError(url, {
|
|
...options,
|
|
method: "POST",
|
|
headers,
|
|
body: payload,
|
|
});
|
|
const data = (await response.json());
|
|
return camelize(data);
|
|
};
|