Packages
fnord
0.9.39
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/mcp/oauth2/registration.ex
defmodule MCP.OAuth2.Registration do
@moduledoc """
RFC 7591 Dynamic Client Registration for OAuth2.
Allows automatic registration of native clients with OAuth providers.
"""
@doc """
Register a new OAuth client with the authorization server.
Uses RFC 7591 Dynamic Client Registration to automatically obtain a client_id
without requiring manual pre-registration.
## Parameters
- registration_endpoint: The registration endpoint URL from discovery
- opts: Optional overrides
- :client_name - Application name (default: "fnord")
- :redirect_uris - Callback URIs (default: ["http://localhost:8080/callback"])
## Returns
- `{:ok, %{client_id: String.t(), client_secret: String.t() | nil}}`
- `{:error, reason}`
"""
@spec register(String.t(), keyword()) ::
{:ok, %{client_id: String.t(), client_secret: String.t() | nil}} | {:error, term()}
def register(registration_endpoint, opts \\ []) do
# RFC 8252 section 7.3: Native apps should use loopback with ephemeral port
# Since many servers require exact URI matches, we use a fixed port (8080)
default_redirects = [
"http://localhost:8080/callback"
]
request_body = %{
"client_name" => opts[:client_name] || "fnord",
"redirect_uris" => opts[:redirect_uris] || default_redirects,
"grant_types" => ["authorization_code", "refresh_token"],
"response_types" => ["code"],
"token_endpoint_auth_method" => "none",
"application_type" => "native"
}
headers = [{"Content-Type", "application/json"}]
case Http.Client.impl().post(
registration_endpoint,
SafeJson.encode!(request_body),
headers,
recv_timeout: 15_000,
timeout: 15_000
) do
{:ok, %{status_code: 201, body: body}} ->
parse_registration_response(body)
{:ok, %{status_code: 200, body: body}} ->
# Some servers return 200 instead of 201
parse_registration_response(body)
{:ok, %{status_code: code, body: _body}} ->
{:error, {:registration_failed, code}}
end
rescue
e in HTTPoison.Error ->
{:error, {:network_error, e.reason}}
catch
_kind, reason ->
{:error, {:network_error, reason}}
end
defp parse_registration_response(body) do
case SafeJson.decode(body) do
{:ok, %{"client_id" => client_id} = response} ->
result = %{
client_id: client_id,
client_secret: Map.get(response, "client_secret")
}
MCP.Util.debug("MCP OAuth", "Successfully registered client: #{client_id}")
{:ok, result}
{:ok, _} ->
{:error, :missing_client_id}
{:error, {:invalid_json, reason}} ->
{:error, {:invalid_json, reason}}
end
end
end