Class: OmniauthOpenidFederation::EndpointResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/omniauth_openid_federation/endpoint_resolver.rb

Overview

Endpoint resolver for OpenID Federation

Resolves OAuth 2.0 endpoints from entity statement metadata or configuration.

Examples:

Resolve endpoints from entity statement

endpoints = EndpointResolver.resolve(
  entity_statement_path: "config/provider-entity-statement.jwt",
  config: {}
)

Class Method Summary collapse

Class Method Details

.build_endpoint_url(issuer_uri, endpoint_path) ⇒ String

Build full endpoint URL from issuer and endpoint path



98
99
100
# File 'lib/omniauth_openid_federation/endpoint_resolver.rb', line 98

def build_endpoint_url(issuer_uri, endpoint_path)
  Utils.build_endpoint_url(issuer_uri, endpoint_path)
end

.build_entity_statement_url(issuer_uri, entity_statement_endpoint: nil) ⇒ String

Build full entity statement URL from issuer and endpoint path



89
90
91
# File 'lib/omniauth_openid_federation/endpoint_resolver.rb', line 89

def build_entity_statement_url(issuer_uri, entity_statement_endpoint: nil)
  Utils.build_entity_statement_url(issuer_uri, entity_statement_endpoint: entity_statement_endpoint)
end

.resolve(entity_statement_path: nil, config: {}) ⇒ Hash

Resolve endpoints from entity statement or configuration

Priority: config -> entity statement -> nil



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/omniauth_openid_federation/endpoint_resolver.rb', line 30

def resolve(entity_statement_path: nil, config: {})
  # Try to get endpoints from entity statement if available
   = (entity_statement_path)

  # Extract endpoints with priority: config -> entity statement -> nil
  # For entity statement, prefer full URLs if available, otherwise extract path
  # Entity statement metadata may contain full URLs (preferred) or paths
   = &.dig(:metadata, :openid_provider) || {}

  # Get endpoint from entity statement (may be full URL or path)
  entity_auth_endpoint = ["authorization_endpoint"] || [:authorization_endpoint]
  entity_token_endpoint = ["token_endpoint"] || [:token_endpoint]
  entity_userinfo_endpoint = ["userinfo_endpoint"] || [:userinfo_endpoint]
  entity_jwks_uri = ["jwks_uri"] || [:jwks_uri]

  # Use config if provided, otherwise use entity statement value (full URL or path)
  authorization_endpoint = config[:authorization_endpoint] || entity_auth_endpoint
  token_endpoint = config[:token_endpoint] || entity_token_endpoint
  userinfo_endpoint = config[:userinfo_endpoint] || entity_userinfo_endpoint
  jwks_uri = config[:jwks_uri] || entity_jwks_uri

  # Entity statement endpoint (defaults to /.well-known/openid-federation if not specified)
  entity_statement_endpoint = config[:entity_statement_endpoint] ||
    extract_path_from_url(&.dig(:metadata, :openid_federation, "federation_entity_endpoint")) ||
    extract_path_from_url(&.dig(:metadata, :openid_federation, :federation_entity_endpoint)) ||
    "/.well-known/openid-federation"

  # Determine audience
  # For OpenID Federation, audience should be the provider issuer (not token endpoint)
  audience = config[:audience]
  unless audience
    # Try provider issuer from entity statement first (preferred for OpenID Federation)
    provider_issuer = &.dig(:metadata, :openid_provider, "issuer") ||
      &.dig(:metadata, :openid_provider, :issuer)
    if StringHelpers.present?(provider_issuer)
      audience = provider_issuer
    else
      # Fallback to token endpoint URL if provider issuer not available
      token_endpoint_url = &.dig(:metadata, :openid_provider, "token_endpoint") ||
        &.dig(:metadata, :openid_provider, :token_endpoint)
      audience = token_endpoint_url if StringHelpers.present?(token_endpoint_url)
    end
  end

  {
    authorization_endpoint: authorization_endpoint,
    token_endpoint: token_endpoint,
    userinfo_endpoint: userinfo_endpoint,
    jwks_uri: jwks_uri,
    entity_statement_endpoint: entity_statement_endpoint,
    audience: audience
  }
end

.validate_and_build_audience(endpoints, issuer_uri: nil) ⇒ Hash

Validate that required endpoints are present

Raises:



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/omniauth_openid_federation/endpoint_resolver.rb', line 107

def validate_and_build_audience(endpoints, issuer_uri: nil)
  if StringHelpers.blank?(endpoints[:authorization_endpoint])
    raise ConfigurationError, "Authorization endpoint not configured. Provide authorization_endpoint in config or entity statement"
  end
  if StringHelpers.blank?(endpoints[:token_endpoint])
    raise ConfigurationError, "Token endpoint not configured. Provide token_endpoint in config or entity statement"
  end
  if StringHelpers.blank?(endpoints[:jwks_uri])
    raise ConfigurationError, "JWKS URI not configured. Provide jwks_uri in config or entity statement"
  end

  # Build audience from issuer + token_endpoint if not provided
  # Note: For OpenID Federation, audience should ideally be provider issuer,
  # but if not available, we build from issuer + token_endpoint path
  unless StringHelpers.present?(endpoints[:audience])
    if issuer_uri
      # For some providers, audience may be issuer + path prefix
      # But we'll use token_endpoint as fallback since we don't have provider issuer here
      audience_uri = issuer_uri.dup
      audience_uri.path = endpoints[:token_endpoint]
      endpoints[:audience] = audience_uri.to_s
    end
  end

  endpoints
end