{"version":3,"file":"OpenId4VcIssuerModule.mjs","names":[],"sources":["../../src/openid4vc-issuer/OpenId4VcIssuerModule.ts"],"sourcesContent":["import { type AgentContext, type DependencyManager, joinUriParts, type Module } from '@credo-ts/core'\nimport type { NextFunction, Response } from 'express'\nimport { getAgentContextForActorId, getRequestContext, importExpress } from '../shared/router'\nimport { OpenId4VcIssuerApi } from './OpenId4VcIssuerApi'\nimport type { InternalOpenId4VcIssuerModuleConfigOptions } from './OpenId4VcIssuerModuleConfig'\nimport { OpenId4VcIssuerModuleConfig } from './OpenId4VcIssuerModuleConfig'\nimport { OpenId4VcIssuerService } from './OpenId4VcIssuerService'\nimport { OpenId4VcIssuanceSessionRepository } from './repository'\nimport { OpenId4VcIssuerRepository } from './repository/OpenId4VcIssuerRepository'\nimport type { OpenId4VcIssuanceRequest } from './router'\nimport {\n  configureAccessTokenEndpoint,\n  configureAuthorizationChallengeEndpoint,\n  configureAuthorizationEndpoint,\n  configureCredentialEndpoint,\n  configureCredentialOfferEndpoint,\n  configureDeferredCredentialEndpoint,\n  configureIssuerMetadataEndpoint,\n  configureJwksEndpoint,\n  configureNonceEndpoint,\n  configureOAuthAuthorizationServerMetadataEndpoint,\n  configurePushedAuthorizationRequestEndpoint,\n  configureRedirectEndpoint,\n} from './router'\n\n/**\n * @public\n */\nexport class OpenId4VcIssuerModule implements Module {\n  public readonly config: OpenId4VcIssuerModuleConfig\n\n  public constructor(options: InternalOpenId4VcIssuerModuleConfigOptions | OpenId4VcIssuerModuleConfig) {\n    this.config = options instanceof OpenId4VcIssuerModuleConfig ? options : new OpenId4VcIssuerModuleConfig(options)\n  }\n\n  /**\n   * Registers the dependencies of the openid4vc issuer module on the dependency manager.\n   */\n  public register(dependencyManager: DependencyManager) {\n    // Since the OpenID4VC module is a nested module (a module consisting of three modules) we register the API\n    // manually. In the future we may disallow resolving the sub-api, but for now it allows for a cleaner migration path\n    dependencyManager.registerContextScoped(OpenId4VcIssuerApi)\n\n    // Register config\n    dependencyManager.registerInstance(OpenId4VcIssuerModuleConfig, this.config)\n\n    // Services\n    dependencyManager.registerSingleton(OpenId4VcIssuerService)\n\n    // Repository\n    dependencyManager.registerSingleton(OpenId4VcIssuerRepository)\n    dependencyManager.registerSingleton(OpenId4VcIssuanceSessionRepository)\n  }\n\n  public async initialize(rootAgentContext: AgentContext): Promise<void> {\n    this.configureRouter(rootAgentContext)\n  }\n\n  /**\n   * Registers the endpoints on the router passed to this module.\n   */\n  private configureRouter(rootAgentContext: AgentContext) {\n    // TODO: it is currently not possible to initialize an agent\n    // shut it down, and then start it again, as the\n    // express router is configured with a specific `AgentContext` instance\n    // and dependency manager. One option is to always create a new router\n    // but then users cannot pass their own router implementation.\n    // We need to find a proper way to fix this.\n    this.registerWellKnownRoutes(rootAgentContext)\n    this.registerIssuerRoutes(rootAgentContext)\n  }\n\n  private getIssuerIdParamHandler =\n    (rootAgentContext: AgentContext) =>\n    async (req: OpenId4VcIssuanceRequest, res: Response, next: NextFunction, issuerId: string) => {\n      if (!issuerId) {\n        rootAgentContext.config.logger.debug('No issuerId provided for incoming oid4vci request, returning 404')\n        return res.status(404).send('Not found')\n      }\n\n      let agentContext: AgentContext | undefined\n\n      try {\n        // FIXME: should we create combined openId actor record?\n        agentContext = await getAgentContextForActorId(rootAgentContext, issuerId)\n        const issuerApi = agentContext.dependencyManager.resolve(OpenId4VcIssuerApi)\n        const issuer = await issuerApi.getIssuerByIssuerId(issuerId)\n\n        req.requestContext = {\n          agentContext,\n          issuer,\n        }\n      } catch (error) {\n        agentContext?.config.logger.error(\n          'Failed to correlate incoming oid4vci request to existing tenant and issuer',\n          {\n            error,\n          }\n        )\n        // If the opening failed\n        await agentContext?.endSession()\n\n        return res.status(404).send('Not found')\n      }\n\n      next()\n    }\n\n  private registerWellKnownRoutes(rootAgentContext: AgentContext) {\n    const issuerIdParamHandler = this.getIssuerIdParamHandler(rootAgentContext)\n    const { Router } = importExpress()\n    const wellKnownEndpointsRouter = Router()\n\n    const basePath = new URL(this.config.baseUrl).pathname\n    const issuerPath = joinUriParts(basePath, [':issuerId'])\n\n    // The files need to be hosted at the root .well-known directory\n    const openidCredentialIssuerPath = joinUriParts('/.well-known/openid-credential-issuer', [issuerPath])\n    const oauthAuthorizationServerPath = joinUriParts('/.well-known/oauth-authorization-server', [issuerPath])\n\n    wellKnownEndpointsRouter.param('issuerId', issuerIdParamHandler)\n\n    configureIssuerMetadataEndpoint(wellKnownEndpointsRouter, openidCredentialIssuerPath)\n    configureOAuthAuthorizationServerMetadataEndpoint(wellKnownEndpointsRouter, oauthAuthorizationServerPath)\n\n    wellKnownEndpointsRouter.use(async (req: OpenId4VcIssuanceRequest, _res: unknown, next) => {\n      const { agentContext } = getRequestContext(req)\n      await agentContext.endSession()\n\n      next()\n    })\n\n    // This one will be called for all errors that are thrown\n    wellKnownEndpointsRouter.use(\n      async (_error: unknown, req: OpenId4VcIssuanceRequest, res: Response, next: NextFunction) => {\n        const { agentContext } = getRequestContext(req)\n\n        if (!res.headersSent) {\n          agentContext.config.logger.warn(\n            'Error was thrown but openid4vci endpoint did not send a response. Sending generic server_error.'\n          )\n\n          res.status(500).json({\n            error: 'server_error',\n            error_description: 'An unexpected error occurred on the server.',\n          })\n        }\n\n        await agentContext.endSession()\n        next()\n      }\n    )\n\n    // We only want these routes to be handle by the router, so we register each path separately\n    // here, so it's also possible for the app to register other `.well-known` endpoints.\n    this.config.app.get(openidCredentialIssuerPath, wellKnownEndpointsRouter)\n    this.config.app.get(oauthAuthorizationServerPath, wellKnownEndpointsRouter)\n  }\n\n  private registerIssuerRoutes(rootAgentContext: AgentContext) {\n    const { Router, json, urlencoded } = importExpress()\n\n    const issuerContextRouter = Router()\n    const issuerEndpointsRouter = Router()\n    const issuerIdParamHandler = this.getIssuerIdParamHandler(rootAgentContext)\n\n    const basePath = new URL(this.config.baseUrl).pathname\n\n    // parse application/x-www-form-urlencoded\n    issuerContextRouter.use(urlencoded({ extended: false }))\n    // parse application/json\n    issuerContextRouter.use(json())\n\n    // Register the issuer endpoints under /:issuerId\n    issuerContextRouter.param('issuerId', issuerIdParamHandler)\n    issuerContextRouter.use('/:issuerId', issuerEndpointsRouter)\n\n    // NOTE: these are here for backwards compat, at some point we should remove them for the root well-known counterpart\n    configureIssuerMetadataEndpoint(issuerEndpointsRouter, '/.well-known/openid-credential-issuer')\n    configureOAuthAuthorizationServerMetadataEndpoint(issuerEndpointsRouter, '/.well-known/oauth-authorization-server')\n\n    configureJwksEndpoint(issuerEndpointsRouter, this.config)\n    configureNonceEndpoint(issuerEndpointsRouter, this.config)\n    configureCredentialOfferEndpoint(issuerEndpointsRouter, this.config)\n    configureAccessTokenEndpoint(issuerEndpointsRouter, this.config)\n    configureAuthorizationChallengeEndpoint(issuerEndpointsRouter, this.config)\n    configureCredentialEndpoint(issuerEndpointsRouter, this.config)\n    configureDeferredCredentialEndpoint(issuerEndpointsRouter, this.config)\n    configurePushedAuthorizationRequestEndpoint(issuerEndpointsRouter, this.config)\n    configureAuthorizationEndpoint(issuerEndpointsRouter, this.config)\n    configureRedirectEndpoint(issuerEndpointsRouter, this.config)\n\n    // First one will be called for all requests (when next is called)\n    issuerContextRouter.use(async (req: OpenId4VcIssuanceRequest, _res: unknown, next) => {\n      const { agentContext } = getRequestContext(req)\n      await agentContext.endSession()\n\n      next()\n    })\n\n    // This one will be called for all errors that are thrown\n    issuerContextRouter.use(\n      async (_error: unknown, req: OpenId4VcIssuanceRequest, res: Response, next: NextFunction) => {\n        const { agentContext } = getRequestContext(req)\n\n        if (!res.headersSent) {\n          agentContext.config.logger.warn(\n            'Error was thrown but openid4vci endpoint did not send a response. Sending generic server_error.'\n          )\n\n          res.status(500).json({\n            error: 'server_error',\n            error_description: 'An unexpected error occurred on the server.',\n          })\n        }\n\n        await agentContext.endSession()\n        next()\n      }\n    )\n\n    // Register the issuer context router under /<basePath>\n    this.config.app.use(basePath, issuerContextRouter)\n  }\n}\n"],"mappings":"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"}