UNPKG

@atproto/oauth-client

Version:

OAuth client for ATPROTO PDS. This package serves as common base for environment-specific implementations (NodeJS, Browser, React-Native).

70 lines 3.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.OAuthAuthorizationServerMetadataResolver = void 0; const fetch_1 = require("@atproto-labs/fetch"); const simple_store_1 = require("@atproto-labs/simple-store"); const oauth_types_1 = require("@atproto/oauth-types"); const util_js_1 = require("./util.js"); /** * @see {@link https://datatracker.ietf.org/doc/html/rfc8414} */ class OAuthAuthorizationServerMetadataResolver extends simple_store_1.CachedGetter { constructor(cache, fetch, config) { super(async (issuer, options) => this.fetchMetadata(issuer, options), cache); Object.defineProperty(this, "fetch", { enumerable: true, configurable: true, writable: true, value: void 0 }); Object.defineProperty(this, "allowHttpIssuer", { enumerable: true, configurable: true, writable: true, value: void 0 }); this.fetch = (0, fetch_1.bindFetch)(fetch); this.allowHttpIssuer = config?.allowHttpIssuer === true; } async get(input, options) { const issuer = oauth_types_1.oauthIssuerIdentifierSchema.parse(input); if (!this.allowHttpIssuer && issuer.startsWith('http:')) { throw new TypeError('Unsecure issuer URL protocol only allowed in development and test environments'); } return super.get(issuer, options); } async fetchMetadata(issuer, options) { const url = new URL(`/.well-known/oauth-authorization-server`, issuer); const request = new Request(url, { headers: { accept: 'application/json' }, cache: options?.noCache ? 'no-cache' : undefined, signal: options?.signal, redirect: 'manual', // response must be 200 OK }); const response = await this.fetch(request); // https://datatracker.ietf.org/doc/html/rfc8414#section-3.2 if (response.status !== 200) { await (0, fetch_1.cancelBody)(response, 'log'); throw await fetch_1.FetchResponseError.from(response, `Unexpected status code ${response.status} for "${url}"`, undefined, { cause: request }); } if ((0, util_js_1.contentMime)(response.headers) !== 'application/json') { await (0, fetch_1.cancelBody)(response, 'log'); throw await fetch_1.FetchResponseError.from(response, `Unexpected content type for "${url}"`, undefined, { cause: request }); } const metadata = oauth_types_1.oauthAuthorizationServerMetadataValidator.parse(await response.json()); // Validate the issuer (MIX-UP attacks) // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics#name-mix-up-attacks // https://datatracker.ietf.org/doc/html/rfc8414#section-2 if (metadata.issuer !== issuer) { throw new TypeError(`Invalid issuer ${metadata.issuer}`); } // ATPROTO requires client_id_metadata_document // http://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html if (metadata.client_id_metadata_document_supported !== true) { throw new TypeError(`Authorization server "${issuer}" does not support client_id_metadata_document`); } return metadata; } } exports.OAuthAuthorizationServerMetadataResolver = OAuthAuthorizationServerMetadataResolver; //# sourceMappingURL=oauth-authorization-server-metadata-resolver.js.map