barrel_mcp_auth behaviour (barrel_mcp v3.0.0)

View Source

Authentication behaviour and utilities for barrel_mcp.

This module defines the barrel_mcp_auth behaviour for implementing pluggable authentication providers. It also provides utility functions for extracting credentials from HTTP headers.

Built-in Providers

Implementing a Custom Provider

To create a custom authentication provider, implement the barrel_mcp_auth behaviour:

   -module(my_auth_provider).
   -behaviour(barrel_mcp_auth).
  
   -export([init/1, authenticate/2, challenge/2]).
  
   init(Opts) ->
       {ok, Opts}.
  
   authenticate(Request, State) ->
       Headers = maps:get(headers, Request, #{}),
       case barrel_mcp_auth:extract_bearer_token(Headers) of
           {ok, Token} -> verify_token(Token);
           {error, no_token} -> {error, unauthorized}
       end.
  
   challenge(Reason, _State) ->
       {401, #{<<"www-authenticate">> => <<"Bearer">>}, <<>>}.

Configuring Authentication

Pass authentication configuration when starting the HTTP server:

   barrel_mcp:start_http(#{
       port => 9090,
       auth => #{
           provider => barrel_mcp_auth_bearer,
           provider_opts => #{secret => <<"my-secret">>,
                              audience => <<"https://mcp.example.com">>},
           required_scopes => [<<"read">>]
       }
   }).

Summary

Types

Possible authentication error reasons.

Module implementing the barrel_mcp_auth behaviour.

Functions

Return the list of HTTP request headers (lower-case) the configured provider expects to read credentials from. Used by the HTTP transport to build CORS Access-Control-Allow-Headers and to extract inputs in the request handler. Falls back to a sensible default per built-in provider when the provider does not export auth_headers/1.

Authenticate a request using the configured provider.

Generate a challenge response for failed authentication.

Extract API key from headers.

Extract Basic authentication credentials.

Extract Bearer token from Authorization header.

Get authentication info from a context map.

The identity a task, a sealed request state or an elicitation belongs to.

Types

auth_config/0

-type auth_config() ::
          #{provider := module(),
            provider_opts => map(),
            provider_state => term(),
            realm => binary(),
            namespace => binary(),
            required_scopes => [binary()]}.

auth_error/0

-type auth_error() ::
          unauthorized | invalid_token | expired_token | insufficient_scope | invalid_credentials |
          {error, term()}.

Possible authentication error reasons.

auth_info/0

-type auth_info() ::
          #{subject => binary(),
            principal => term(),
            issuer => binary(),
            audience => binary() | [binary()],
            scopes => [binary()],
            expires_at => integer(),
            claims => map(),
            metadata => map()}.

auth_provider/0

-type auth_provider() :: module().

Module implementing the barrel_mcp_auth behaviour.

Callbacks

auth_headers/1

(optional)
-callback auth_headers(State :: term()) -> [binary()].

authenticate/2

-callback authenticate(Request :: map(), State :: term()) -> {ok, auth_info()} | {error, auth_error()}.

challenge/2

-callback challenge(Reason :: auth_error(), State :: term()) ->
                       {StatusCode :: integer(), Headers :: map(), Body :: binary()}.

init/1

(optional)
-callback init(Opts :: map()) -> {ok, State :: term()} | {error, term()}.

principal/2

(optional)
-callback principal(AuthInfo :: auth_info(), State :: term()) -> {ok, term()} | {error, no_subject}.

Functions

auth_headers(Config)

-spec auth_headers(auth_config()) -> [binary()].

Return the list of HTTP request headers (lower-case) the configured provider expects to read credentials from. Used by the HTTP transport to build CORS Access-Control-Allow-Headers and to extract inputs in the request handler. Falls back to a sensible default per built-in provider when the provider does not export auth_headers/1.

authenticate(Config, Request, State)

-spec authenticate(auth_config(), map(), term()) -> {ok, auth_info()} | {error, auth_error()}.

Authenticate a request using the configured provider.

Delegates authentication to the configured provider and optionally checks for required scopes after successful authentication.

challenge_response(Config, Reason)

-spec challenge_response(auth_config(), auth_error()) -> {integer(), map(), binary()}.

Generate a challenge response for failed authentication.

Creates an HTTP response with appropriate status code, headers (including WWW-Authenticate), and error body.

extract_api_key(Headers, Opts)

-spec extract_api_key(Headers :: map(), Opts :: map()) -> {ok, binary()} | {error, no_key}.

Extract API key from headers.

Looks for API key in the following locations (in order):

  1. Custom header specified by header_name option
  2. X-API-Key header
  3. Authorization header with ApiKey scheme

Example

  Headers = #{<<"x-api-key">> => <<"my-key">>},
  {ok, <<"my-key">>} = barrel_mcp_auth:extract_api_key(Headers, #{}).

extract_basic_auth(Headers)

-spec extract_basic_auth(Headers :: map()) ->
                            {ok, Username :: binary(), Password :: binary()} | {error, no_credentials}.

Extract Basic authentication credentials.

Parses the Authorization header for Basic authentication scheme and decodes the base64-encoded credentials.

Example

  Encoded = base64:encode(<<"user:pass">>),
  Headers = #{<<"authorization">> => <<"Basic ", Encoded/binary>>},
  {ok, <<"user">>, <<"pass">>} = barrel_mcp_auth:extract_basic_auth(Headers).

extract_bearer_token(Headers)

-spec extract_bearer_token(Headers :: map()) -> {ok, binary()} | {error, no_token}.

Extract Bearer token from Authorization header.

Parses the Authorization header and extracts the token value from a Bearer authentication scheme.

Example

  Headers = #{<<"authorization">> => <<"Bearer abc123">>},
  {ok, <<"abc123">>} = barrel_mcp_auth:extract_bearer_token(Headers).

get_auth_info(_)

-spec get_auth_info(map()) -> auth_info() | undefined.

Get authentication info from a context map.

Extracts authentication information that was added to the context after successful authentication.

principal(Config, AuthInfo)

-spec principal(auth_config(), auth_info()) -> {ok, term()} | {error, no_subject}.

The identity a task, a sealed request state or an elicitation belongs to.

Not the auth_info() map: that carries the whole token, so a refreshed credential with a new exp or jti would look like a different caller and orphan the work started under the old one.

Not the subject alone either. Two providers, two issuers or two tenants can each mint the same subject, and the stores these key are node-wide, so the provider and a namespace are part of the identity.