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

Description

Service to handle well-known endpoints and metadata retrieval.

Relationships

Used by

Depends on

No results matching.

Index

Properties
Methods

Constructor

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

Constructor for WellKnownService.

Parameters :
Name Type Optional
oid4vciService Oid4vciService No
certService CertService No
keyChainService KeyChainService No
authorizeService AuthorizeService No
cryptoImplementationService CryptoImplementationService No
authorizationServersService AuthorizationServersService No
chainedAsService ChainedAsService No
chainedAsVpService ChainedAsVpService 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<{ keys: Record[] }>
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 getChainedAsVpJwks
getChainedAsVpJwks(tenantId: string)

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

Parameters :
Name Type Optional
tenantId string No
Returns : Promise<{ keys: Record[] }>
Async getChainedAsVpMetadata
getChainedAsVpMetadata(tenantId: string)

Returns the OAuth 2.0 Authorization Server metadata for the VP-backed 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
Async getManagedAuthorizationServerJwks
getManagedAuthorizationServerJwks(tenantId: string, authorizationServerId: string)
Parameters :
Name Type Optional
tenantId string No
authorizationServerId string No
Returns : Promise<{ keys: Record[] }>
Async getManagedAuthorizationServerMetadata
getManagedAuthorizationServerMetadata(tenantId: string, authorizationServerId: string)
Parameters :
Name Type Optional
tenantId string No
authorizationServerId string No
Returns : Promise<Record<string, unknown>>

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 "../authorization/authorize/authorize.service";
import { AuthorizationServersService } from "../authorization/authorization-servers/authorization-servers.service";
import { ChainedAsService } from "../authorization/chained-as/chained-as.service";
import { ChainedAsVpService } from "../authorization/chained-as-vp/chained-as-vp.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 authorizationServersService: AuthorizationServersService,
        private readonly chainedAsService: ChainedAsService,
        private readonly chainedAsVpService: ChainedAsVpService,
        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,
            );
        }
    }

    /**
     * Returns the OAuth 2.0 Authorization Server metadata for the VP-backed Chained AS.
     */
    async getChainedAsVpMetadata(
        tenantId: string,
    ): Promise<Record<string, unknown>> {
        try {
            return await this.chainedAsVpService.getMetadata(tenantId);
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve VP 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 VP-backed Chained Authorization Server.
     */
    async getChainedAsVpJwks(
        tenantId: string,
    ): Promise<{ keys: Record<string, unknown>[] }> {
        try {
            return await this.chainedAsVpService.getJwks(tenantId);
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve VP Chained AS JWKS for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    async getManagedAuthorizationServerMetadata(
        tenantId: string,
        authorizationServerId: string,
    ): Promise<Record<string, unknown>> {
        try {
            return await this.authorizationServersService.getMetadata(
                tenantId,
                authorizationServerId,
            );
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve managed authorization server metadata for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }

    async getManagedAuthorizationServerJwks(
        tenantId: string,
        authorizationServerId: string,
    ): Promise<{ keys: Record<string, unknown>[] }> {
        try {
            return await this.authorizationServersService.getJwks(
                tenantId,
                authorizationServerId,
            );
        } catch (error) {
            if (error instanceof WellKnownException) {
                throw error;
            }
            throw new WellKnownException(
                `Failed to retrieve managed authorization server JWKS for tenant ${tenantId}: ${error instanceof Error ? error.message : "Unknown error"}`,
                HttpStatus.INTERNAL_SERVER_ERROR,
            );
        }
    }
}

results matching ""

    No results matching ""