+ // Set metadata field.
+ if (mfData && 'indieauth-metadata' in mfData.rels) {
+ profile.indieauthMetadata = mfData.rels['indieauth-metadata'][0];
+ }
+
+ // Attempt to populate metadata from authorization server.
+ if (profile.indieauthMetadata) {
+ let mdURL;
+ try {
+ mdURL = new URL(profile.indieauthMetadata);
+ } catch (e) /* istanbul ignore next */ {
+ this.logger.error(_scope, 'invalid authorization server metadata url', { profile });
+ }
+ /* istanbul ignore else */
+ if (mdURL) {
+ const metadataResponse = await this.fetchJSON(mdURL);
+ if (metadataResponse) {
+ // Map snake_case fields to camelCase.
+ Object.entries({
+ issuer: 'issuer',
+ authorizationEndpoint: 'authorization_endpoint',
+ tokenEndpoint: 'token_endpoint',
+ introspectionEndpoint: 'introspection_endpoint',
+ introspectionEndpointAuthMethodsSupported: 'introspection_endpoint_auth_methods_supported',
+ revocationEndpoint: 'revocation_endpoint',
+ revocationEndpointAuthMethodsSupported: 'revocation_endpoint_auth_methods_supported',
+ scopesSupported: 'scopes_supported',
+ responseTypesSupported: 'response_types_supported',
+ grantTypesSupported: 'grant_types_supported',
+ serviceDocumentation: 'service_documentation',
+ codeChallengeMethodsSupported: 'code_challenge_methods_supported',
+ authorizationResponseIssParameterSupported: 'authorization_response_iss_parameter_supported',
+ userinfoEndpoint: 'userinfo_endpoint',
+ }).forEach(([c, s]) => {
+ if (s in metadataResponse) {
+ profile.metadata[c] = metadataResponse[s]; // eslint-disable-line security/detect-object-injection
+ }
+ });
+
+ // Populate legacy profile fields.
+ ['authorizationEndpoint', 'tokenEndpoint'].forEach((f) => {
+ if (f in profile.metadata) {
+ profile[f] = profile.metadata[f]; // eslint-disable-line security/detect-object-injection
+ }
+ });
+ }
+ }
+ }
+