@contentgrid/fetch-hook-authentication
Version:
109 lines (102 loc) • 4.24 kB
JavaScript
;
Object.defineProperty(exports, '__esModule', { value: true });
var fetchHooks = require('@contentgrid/fetch-hooks');
var MimeType = require('whatwg-mimetype');
var valueProvider = require('@contentgrid/fetch-hooks/value-provider');
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
var MimeType__default = /*#__PURE__*/_interopDefault(MimeType);
function createTokenRequestBody(resource) {
const formData = new URLSearchParams();
formData.append("grant_type", "https://contentgrid.cloud/oauth2/grant/extension-token");
formData.append("resource", resource);
return formData;
}
function createAuthenticationToken(responseBody) {
// Verify response validity: https://datatracker.ietf.org/doc/html/rfc6749#section-5.1
if (!("token_type" in responseBody)) {
throw new TokenExchangeProtocolViolationError(`Missing 'token_type' in response`);
}
if (!("access_token" in responseBody)) {
throw new TokenExchangeProtocolViolationError(`Missing 'access_token' in response`);
}
if (String(responseBody["token_type"]).toLowerCase() !== "bearer") {
throw new TokenExchangeProtocolViolationError(`Unsupported token type ${responseBody["token_type"]}`);
}
const expiry = "expires_in" in responseBody ? new Date(Date.now() + responseBody["expires_in"] * 1000) : null;
return {
token: responseBody["access_token"],
expiresAt: expiry
};
}
function isJsonContentType(contentType) {
if (!contentType) {
return false;
}
const mimetype = new MimeType__default.default(contentType);
// https://mimesniff.spec.whatwg.org/#json-mime-type
return (mimetype.type === "application" || mimetype.type === "text") && mimetype.subtype === "json" || mimetype.subtype.endsWith("+json");
}
function createOAuth2Error(responseBody) {
if (!("error" in responseBody)) {
return null; // 'error' field is required in OAuth error responses: https://datatracker.ietf.org/doc/html/rfc6749#section-5.2
}
return new OAuth2AuthenticationError(responseBody["error"], responseBody["error_description"]);
}
function createContentgridTokenExchangeTokenSupplier(config) {
const exchangeUrlResolver = valueProvider.ValueProviderResolver.fromValueProvider(config.exchangeUrl);
return async (uri, opts) => {
var _a;
const exchangeUrl = await exchangeUrlResolver.resolve(opts);
const response = await opts.fetch(exchangeUrl, {
signal: (_a = opts === null || opts === void 0 ? void 0 : opts.signal) !== null && _a !== void 0 ? _a : null,
method: "POST",
body: createTokenRequestBody(uri)
});
if (response.ok) {
if (isJsonContentType(response.headers.get("content-type"))) {
return createAuthenticationToken(await response.json());
} else {
throw new TokenExchangeProtocolViolationError(`Content type ${response.headers.get('content-type')} is not JSON`);
}
} else {
if (isJsonContentType(response.headers.get("content-type"))) {
const oauthError = createOAuth2Error(await response.json());
if (oauthError) {
throw oauthError;
}
}
throw new TokenExchangeProtocolViolationError(`Non-OAuth response error: ${response.status} ${response.statusText}`);
}
};
}
/**
* Base class for token exchange errors
*/
class TokenExchangeError extends fetchHooks.FetchHooksError {
constructor(message) {
super(`Token exchange failed: ${message}`);
}
}
/**
* Unexpected deviation from the expected token exchange flow
*/
class TokenExchangeProtocolViolationError extends TokenExchangeError {
constructor(message) {
super(`Protocol violation: ${message}`);
}
}
/**
* A potentially-expected OAuth error response
*/
class OAuth2AuthenticationError extends TokenExchangeError {
constructor(code, description) {
super(`OAuth2 error ${code}: ${description}`);
this.code = code;
this.description = description;
}
}
exports.OAuth2AuthenticationError = OAuth2AuthenticationError;
exports.TokenExchangeError = TokenExchangeError;
exports.TokenExchangeProtocolViolationError = TokenExchangeProtocolViolationError;
exports.default = createContentgridTokenExchangeTokenSupplier;
//# sourceMappingURL=contentgridTokenExchange.js.map