UNPKG

@contentgrid/fetch-hook-authentication

Version:
109 lines (102 loc) 4.24 kB
'use strict'; 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