@atproto/oauth-client
Version:
OAuth client for ATPROTO PDS. This package serves as common base for environment-specific implementations (NodeJS, Browser, React-Native).
113 lines • 5.07 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.OAuthResolver = void 0;
const oauth_types_1 = require("@atproto/oauth-types");
const oauth_resolver_error_js_1 = require("./oauth-resolver-error.js");
class OAuthResolver {
constructor(identityResolver, protectedResourceMetadataResolver, authorizationServerMetadataResolver) {
Object.defineProperty(this, "identityResolver", {
enumerable: true,
configurable: true,
writable: true,
value: identityResolver
});
Object.defineProperty(this, "protectedResourceMetadataResolver", {
enumerable: true,
configurable: true,
writable: true,
value: protectedResourceMetadataResolver
});
Object.defineProperty(this, "authorizationServerMetadataResolver", {
enumerable: true,
configurable: true,
writable: true,
value: authorizationServerMetadataResolver
});
}
/**
* @param input - A handle, DID, PDS URL or Entryway URL
*/
async resolve(input, options) {
// Allow using an entryway, or PDS url, directly as login input (e.g.
// when the user forgot their handle, or when the handle does not
// resolve to a DID)
return /^https?:\/\//.test(input)
? this.resolveFromService(input, options)
: this.resolveFromIdentity(input, options);
}
/**
* @note this method can be used to verify if a particular uri supports OAuth
* based sign-in (for compatibility with legacy implementation).
*/
async resolveFromService(input, options) {
try {
// Assume first that input is a PDS URL (as required by ATPROTO)
const metadata = await this.getResourceServerMetadata(input, options);
return { metadata };
}
catch (err) {
if (!options?.signal?.aborted && err instanceof oauth_resolver_error_js_1.OAuthResolverError) {
try {
// Fallback to trying to fetch as an issuer (Entryway)
const result = oauth_types_1.oauthIssuerIdentifierSchema.safeParse(input);
if (result.success) {
const metadata = await this.getAuthorizationServerMetadata(result.data, options);
return { metadata };
}
}
catch {
// Fallback failed, throw original error
}
}
throw err;
}
}
async resolveFromIdentity(input, options) {
const identity = await this.resolveIdentity(input, options);
options?.signal?.throwIfAborted();
const metadata = await this.getResourceServerMetadata(identity.pds, options);
return { identity, metadata };
}
async resolveIdentity(input, options) {
try {
return await this.identityResolver.resolve(input, options);
}
catch (cause) {
throw oauth_resolver_error_js_1.OAuthResolverError.from(cause, `Failed to resolve identity: ${input}`);
}
}
async getAuthorizationServerMetadata(issuer, options) {
try {
return await this.authorizationServerMetadataResolver.get(issuer, options);
}
catch (cause) {
throw oauth_resolver_error_js_1.OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for issuer: ${issuer}`);
}
}
async getResourceServerMetadata(pdsUrl, options) {
try {
const rsMetadata = await this.protectedResourceMetadataResolver.get(pdsUrl, options);
// ATPROTO requires one, and only one, authorization server entry
if (rsMetadata.authorization_servers?.length !== 1) {
throw new oauth_resolver_error_js_1.OAuthResolverError(rsMetadata.authorization_servers?.length
? `Unable to determine authorization server for PDS: ${pdsUrl}`
: `No authorization servers found for PDS: ${pdsUrl}`);
}
const issuer = rsMetadata.authorization_servers[0];
options?.signal?.throwIfAborted();
const asMetadata = await this.getAuthorizationServerMetadata(issuer, options);
// https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-05#section-4
if (asMetadata.protected_resources) {
if (!asMetadata.protected_resources.includes(rsMetadata.resource)) {
throw new oauth_resolver_error_js_1.OAuthResolverError(`PDS "${pdsUrl}" not protected by issuer "${issuer}"`);
}
}
return asMetadata;
}
catch (cause) {
throw oauth_resolver_error_js_1.OAuthResolverError.from(cause, `Failed to resolve OAuth server metadata for resource: ${pdsUrl}`);
}
}
}
exports.OAuthResolver = OAuthResolver;
//# sourceMappingURL=oauth-resolver.js.map