File

src/issuer/issuance/oid4vci/well-known/well-known.service.ts

Description

Service to handle well-known endpoints and metadata retrieval.

Index

Properties
Methods

Constructor

constructor(oid4vciService: Oid4vciService, certService: CertService, keyChainService: KeyChainService, authorizeService: AuthorizeService, cryptoImplementationService: CryptoImplementationService, chainedAsService: ChainedAsService, issuanceService: IssuanceService)

Constructor for WellKnownService.

Parameters :
Name Type Optional
oid4vciService Oid4vciService No
certService CertService No
keyChainService KeyChainService No
authorizeService AuthorizeService No
cryptoImplementationService CryptoImplementationService No
chainedAsService ChainedAsService No
issuanceService IssuanceService No

Methods

Async getAuthzMetadata
getAuthzMetadata(tenantId: string)

Returns the OAuth 2.0 Authorization Server metadata for a given tenant.

Parameters :
Name Type Optional
tenantId string No
Returns : unknown
Async getChainedAsJwks
getChainedAsJwks(tenantId: string)

Returns the JSON Web Key Set (JWKS) for the Chained Authorization Server.

Parameters :
Name Type Optional
tenantId string No
Returns : Promise<literal type>
Async getChainedAsMetadata
getChainedAsMetadata(tenantId: string)

Returns the OAuth 2.0 Authorization Server metadata for the Chained AS. This supports the RFC 8414 alternative discovery path format: /.well-known/oauth-authorization-server/:tenantId/chained-as

Parameters :
Name Type Optional
tenantId string No
Returns : Promise<Record<string, unknown>>
Async getIssuerMetadata
getIssuerMetadata(tenantId: string, contentType: MediaType)

Retrieves the issuer metadata for a given tenant for the specified content type. The metadata can be returned in two formats:

  • an unsigned JSON document using the media type application/json, or
  • a signed JSON Web Token (JWT) containing the Credential Issuer Metadata in its payload using the media type application/jwt.
Parameters :
Name Type Optional
tenantId string No
contentType MediaType No
Returns : unknown
Async getJwks
getJwks(tenantId: string)

Returns the JSON Web Key Set (JWKS) for a given tenant. Resolves the signing key from issuance config (or default) and includes the kid so that JWT verification can match keys.

Parameters :
Name Type Optional
tenantId string No

Properties

Public Readonly keyChainService
Type : KeyChainService
import { HttpStatus, Injectable } from "@nestjs/common";
import { CertService } from "../../../../crypto/key/cert/cert.service";
import { CryptoImplementationService } from "../../../../crypto/key/crypto-implementation/crypto-implementation.service";
import { KeyUsageType } from "../../../../crypto/key/entities/key-chain.entity";
import { KeyChainService } from "../../../../crypto/key/key-chain.service";
import { MediaType } from "../../../../shared/utils/mediaType/media-type.enum";
import { IssuanceService } from "../../../configuration/issuance/issuance.service";
import { AuthorizeService } from "../authorize/authorize.service";
import { ChainedAsService } from "../chained-as/chained-as.service";
import { WellKnownException } from "../exceptions";
import { Oid4vciService } from "../oid4vci.service";
import { CredentialIssuerMetadataDto } from "./dto/credential-issuer-metadata.dto";
import { EC_Public, JwksResponseDto } from "./dto/jwks-response.dto";

/**
 * Service to handle well-known endpoints and metadata retrieval.
 */
@Injectable()
export class WellKnownService {
    /**
     * Constructor for WellKnownService.
     * @param oid4vciService
     * @param certService
     * @param authorizeService
     */
    constructor(
        private readonly oid4vciService: Oid4vciService,
        private readonly certService: CertService,
        public readonly keyChainService: KeyChainService,
        private readonly authorizeService: AuthorizeService,
        private readonly cryptoImplementationService: CryptoImplementationService,
        private readonly chainedAsService: ChainedAsService,
        private readonly issuanceService: IssuanceService,
    ) {}

    /**
     * Retrieves the issuer metadata for a given tenant for the specified content type.
     * The metadata can be returned in two formats:
     * - an unsigned JSON document using the media type application/json, or
     * - a signed JSON Web Token (JWT) containing the Credential Issuer Metadata in its payload using the media type application/jwt.
     * @param tenantId
     * @param contentType
     * @returns
     */
    async getIssuerMetadata(tenantId: string, contentType: MediaType) {
        try {
            const metadata = (
                await this.oid4vciService.issuerMetadata(tenantId)
            ).credentialIssuer as unknown as CredentialIssuerMetadataDto;

            if (contentType === MediaType.APPLICATION_JWT) {
                const cert = await this.certService.find({
                    tenantId,
                    type: KeyUsageType.Access,
                });
                return this.keyChainService.signJWT(
                    {
                        ...metadata,
                        iss: metadata.credential_issuer,
                        sub: metadata.credential_issuer,
                        iat: Math.floor(Date.now() / 1000),
                        // [Review]: should we add `exp` value here?
                        //MM: the value makes sense when we cache the issuer metadata so it must not be signed on every request. Like when it is issued every hour, its lifetime is 1 hour and the jwt is in the cache.
                    },
                    {
                        typ: "openidvci-issuer-metadata+jwt",
                        alg: this.cryptoImplementationService.getAlg(),
                        x5c: this.certService.getCertChain(cert),
                    },
                    tenantId,
                    cert.keyId,
                );
            }

            return metadata;
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve issuer metadata for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    /**
     * Returns the OAuth 2.0 Authorization Server metadata for a given tenant.
     * @returns
     */
    async getAuthzMetadata(tenantId: string) {
        try {
            return await this.authorizeService.authzMetadata(tenantId);
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve authorization server metadata for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    /**
     * Returns the JSON Web Key Set (JWKS) for a given tenant.
     * Resolves the signing key from issuance config (or default) and
     * includes the `kid` so that JWT verification can match keys.
     * @returns
     */
    async getJwks(tenantId: string): Promise<JwksResponseDto> {
        try {
            const issuanceConfig =
                await this.issuanceService.getIssuanceConfiguration(tenantId);

            const signingKeyId =
                issuanceConfig.signingKeyId ||
                (await this.keyChainService.getKid(tenantId));

            const publicKey = await this.keyChainService.getPublicKey(
                "jwk",
                tenantId,
                signingKeyId,
            );

            const keyWithKid = {
                ...publicKey,
                kid: (publicKey as { kid?: string }).kid || signingKeyId,
            };

            return { keys: [keyWithKid as EC_Public] };
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve JWKS for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    /**
     * Returns the OAuth 2.0 Authorization Server metadata for the Chained AS.
     * This supports the RFC 8414 alternative discovery path format:
     * `/.well-known/oauth-authorization-server/:tenantId/chained-as`
     * @param tenantId
     * @returns
     */
    async getChainedAsMetadata(
        tenantId: string,
    ): Promise<Record<string, unknown>> {
        try {
            return await this.chainedAsService.getMetadata(tenantId);
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve Chained AS metadata for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    /**
     * Returns the JSON Web Key Set (JWKS) for the Chained Authorization Server.
     * @param tenantId
     * @returns
     */
    async getChainedAsJwks(
        tenantId: string,
    ): Promise<{ keys: Record<string, unknown>[] }> {
        try {
            return await this.chainedAsService.getJwks(tenantId);
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve Chained AS JWKS for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }
}

results matching ""

    No results matching ""