Class: A2A::Server::Middleware::AuthenticationMiddleware

Inherits:
Object
  • Object
show all
Defined in:
lib/a2a/server/middleware/authentication_middleware.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schemes: [], required: false, authenticators: {}) ⇒ AuthenticationMiddleware

Initialize authentication middleware

Parameters:

  • schemes (Array<String>) (defaults to: [])

    Allowed authentication schemes

  • required (Boolean) (defaults to: false)

    Whether authentication is required

  • authenticators (Hash) (defaults to: {})

    Custom authenticator implementations



37
38
39
40
41
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 37

def initialize(schemes: [], required: false, authenticators: {})
  @schemes = schemes.map(&:to_s)
  @required = required
  @authenticators = default_authenticators.merge(authenticators)
end

Instance Attribute Details

#authenticatorsObject (readonly)

Returns the value of attribute authenticators.



29
30
31
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 29

def authenticators
  @authenticators
end

#requiredObject (readonly)

Returns the value of attribute required.



29
30
31
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 29

def required
  @required
end

#schemesObject (readonly)

Returns the value of attribute schemes.



29
30
31
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 29

def schemes
  @schemes
end

Instance Method Details

#add_authenticator(scheme, authenticator) ⇒ Object

Add a custom authenticator

Parameters:

  • scheme (String)

    The authentication scheme name

  • authenticator (Proc)

    The authenticator proc



79
80
81
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 79

def add_authenticator(scheme, authenticator)
  @authenticators[scheme.to_s] = authenticator
end

#authenticate(scheme, credentials, context) ⇒ Object (private)

Authenticate using the appropriate authenticator

Parameters:

  • scheme (String)

    The authentication scheme

  • credentials (String)

    The credentials

  • context (A2A::Server::Context)

    The request context

Raises:



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 136

def authenticate(scheme, credentials, context)
  authenticator = @authenticators[scheme]

  unless authenticator
    raise A2A::Errors::AuthorizationFailed,
          "No authenticator configured for scheme '#{scheme}'"
  end

  # Call the authenticator
  result = authenticator.call(credentials, context)

  raise A2A::Errors::AuthorizationFailed, "Authentication failed for scheme '#{scheme}'" unless result

  # Set authentication in context
  context.set_authentication(scheme, result)
end

#authenticate_api_key(api_key, _context) ⇒ Hash? (private)

Authenticate API key

Parameters:

Returns:

  • (Hash, nil)

    Authentication result or nil if invalid



204
205
206
207
208
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 204

def authenticate_api_key(api_key, _context)
  # In practice, validate against API key database
  # For now, just return the key as valid
  { api_key: api_key, scheme: "api_key" }
end

#authenticate_basic(credentials, _context) ⇒ Hash? (private)

Authenticate Basic authentication

Parameters:

  • credentials (String)

    Base64 encoded username:password

  • context (A2A::Server::Context)

    The request context

Returns:

  • (Hash, nil)

    Authentication result or nil if invalid



187
188
189
190
191
192
193
194
195
196
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 187

def authenticate_basic(credentials, _context)
  decoded = Base64.decode64(credentials)
  username, = decoded.split(":", 2)

  # In practice, validate against user database
  # For now, just return the username
  { username: username, scheme: "basic" }
rescue StandardError
  nil
end

#authenticate_bearer(token, _context) ⇒ Hash? (private)

Authenticate Bearer token (JWT or API key)

Parameters:

Returns:

  • (Hash, nil)

    Authentication result or nil if invalid



171
172
173
174
175
176
177
178
179
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 171

def authenticate_bearer(token, _context)
  # This is a basic implementation - in practice you would:
  # 1. Validate JWT signature and expiration
  # 2. Check API key against database
  # 3. Extract user/session information

  # For now, just return the token as valid
  { token: token, scheme: "bearer" }
end

#call(request, context) { ... } ⇒ Object

Process authentication for a request

Parameters:

Yields:

  • Block to continue the middleware chain

Returns:

  • (Object)

    The result from the next middleware or handler



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 50

def call(request, context)
  # Extract authentication information
  auth_info = extract_authentication(request, context)

  if auth_info
    scheme, credentials = auth_info

    # Check if scheme is allowed
    unless @schemes.empty? || @schemes.include?(scheme)
      raise A2A::Errors::AuthorizationFailed, "Authentication scheme '#{scheme}' not supported"
    end

    # Authenticate using the appropriate authenticator
    authenticate(scheme, credentials, context)

  elsif @required
    raise A2A::Errors::AuthenticationRequired,
          "Authentication required. Supported schemes: #{@schemes.join(', ')}"
  end

  # Continue to next middleware
  yield
end

#default_authenticatorsHash (private)

Default authenticator implementations

Returns:

  • (Hash)

    Hash of scheme name to authenticator proc



157
158
159
160
161
162
163
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 157

def default_authenticators
  {
    "bearer" => method(:authenticate_bearer),
    "basic" => method(:authenticate_basic),
    "api_key" => method(:authenticate_api_key)
  }
end

#extract_authentication(request, context) ⇒ Array<String>? (private)

Extract authentication information from request/context

Parameters:

Returns:

  • (Array<String>, nil)

    [scheme, credentials] or nil if no auth



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 91

def extract_authentication(request, context)
  # Try to extract from various sources

  # 1. Check context metadata for HTTP headers
  auth_header = context.(:authorization) ||
                context.("Authorization")

  return parse_authorization_header(auth_header) if auth_header

  # 2. Check request parameters for API key
  if request.params.is_a?(Hash)
    api_key = request.params["api_key"] || request.params[:api_key]
    return ["api_key", api_key] if api_key
  end

  # 3. Check context for pre-set authentication
  if context.authenticated?
    # Return the first available authentication scheme
    context.instance_variable_get(:@auth_schemes)&.first
  end

  nil
end

#parse_authorization_header(header) ⇒ Array<String> (private)

Parse Authorization header

Parameters:

  • header (String)

    The Authorization header value

Returns:

  • (Array<String>)

    [scheme, credentials]



120
121
122
123
124
125
126
127
128
# File 'lib/a2a/server/middleware/authentication_middleware.rb', line 120

def parse_authorization_header(header)
  parts = header.strip.split(" ", 2)
  return nil if parts.length != 2

  scheme = parts[0].downcase
  credentials = parts[1]

  [scheme, credentials]
end