AttestoMCP.Test.Factory (AttestoMCP v2.2.3)

Copy Markdown View Source

Test fixtures for exercising a host MCP server's Attesto pipeline.

This factory mints access tokens and DPoP proofs for host application test suites, including the shipped AttestoMCP.Test.DPoPAssertions helpers. It is built entirely on Attesto's published API (Attesto.Test.DPoP, Attesto.Token.mint/3, Attesto.PrincipalKind.new/3, Attesto.Config.new/1, Attesto.Keystore.Static) plus JOSE, so it has no dependency on any Attesto-internal test scaffolding.

The module is shipped for downstream ExUnit test suites. config/0 registers an ExUnit.Callbacks.on_exit/1 cleanup and must be called from an ExUnit test process configured with async: false, because Attesto.Keystore.Static uses process-global application configuration.

defmodule MyApp.MCPAuthTest do
  use ExUnit.Case
  import AttestoMCP.Test.DPoPAssertions

  setup do
    %{config: AttestoMCP.Test.Factory.config()}
  end
end

Summary

Functions

Mint a signed access token for config.

Run the AttestoMCP authentication plug with the factory request URI.

Build an Attesto.Config backed by an in-memory Attesto.Keystore.Static.

Return the RFC 7638 thumbprint for a DPoP proof key.

Generate a fresh P-256 JWK for use as a DPoP proof key.

Build a DPoP proof JWT bound to access_token and return {proof, jkt}.

The audience the factory mints tokens for, usable as the request htu.

Build a self-signed certificate (DER) for exercising mTLS sender constraints.

Functions

access_token(config, opts \\ [])

@spec access_token(
  Attesto.Config.t(),
  keyword()
) :: String.t()

Mint a signed access token for config.

Options:

  • :scopes - scopes granted to the token (default [AttestoMCP.Scopes.tools_call()]).
  • :audience - scalar or array-valued aud claim override. Attesto deduplicates the array and collapses a single remaining member to a scalar when minting.
  • :dpop_jkt - JWK thumbprint to bind the token to (RFC 9449 cnf.jkt).
  • :mtls_cert_thumbprint - certificate thumbprint to bind the token to (RFC 8705).

authenticate(conn, config, opts \\ [])

@spec authenticate(Plug.Conn.t(), Attesto.Config.t(), keyword()) :: Plug.Conn.t()

Run the AttestoMCP authentication plug with the factory request URI.

config()

@spec config() :: Attesto.Config.t()

Build an Attesto.Config backed by an in-memory Attesto.Keystore.Static.

The signing key is registered into the :attesto application environment and the previous value is restored via ExUnit.Callbacks.on_exit/1, so each test gets an isolated keystore. Call this only from a test using async: false.

dpop_jkt(jwk)

@spec dpop_jkt(JOSE.JWK.t()) :: String.t()

Return the RFC 7638 thumbprint for a DPoP proof key.

dpop_jwk()

@spec dpop_jwk() :: JOSE.JWK.t()

Generate a fresh P-256 JWK for use as a DPoP proof key.

dpop_proof(access_token, opts \\ [])

@spec dpop_proof(
  String.t(),
  keyword()
) :: {String.t(), String.t()}

Build a DPoP proof JWT bound to access_token and return {proof, jkt}.

jkt is the RFC 7638 thumbprint of the proof key, suitable for passing as :dpop_jkt to access_token/2. Options:

  • :jwk - reuse a specific key (default a fresh P-256 key).
  • :htm - the proof htm claim (default "POST").
  • :htu - the proof htu claim (default the factory audience).

htu()

@spec htu() :: String.t()

The audience the factory mints tokens for, usable as the request htu.

self_signed_cert_der()

@spec self_signed_cert_der() :: binary()

Build a self-signed certificate (DER) for exercising mTLS sender constraints.