Class: A2A::Client::Auth::JWT

Inherits:
Object
  • Object
show all
Defined in:
lib/a2a/client/auth/jwt.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(token: nil, secret: nil, algorithm: "HS256", payload: nil, headers: nil, expires_in: nil) ⇒ JWT

Initialize JWT authentication

Parameters:

  • token (String, nil) (defaults to: nil)

    Pre-generated JWT token

  • secret (String, nil) (defaults to: nil)

    Secret key for JWT signing (required for dynamic tokens)

  • algorithm (String) (defaults to: "HS256")

    JWT signing algorithm (default: 'HS256')

  • payload (Hash, nil) (defaults to: nil)

    JWT payload for dynamic token generation

  • headers (Hash, nil) (defaults to: nil)

    Additional JWT headers

  • expires_in (Integer, nil) (defaults to: nil)

    Token expiration time in seconds



27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/a2a/client/auth/jwt.rb', line 27

def initialize(token: nil, secret: nil, algorithm: "HS256", payload: nil,
               headers: nil, expires_in: nil)
  @token = token
  @secret = secret
  @algorithm = algorithm
  @payload = payload || {}
  @headers = headers || {}
  @expires_in = expires_in
  @generated_token = nil
  @token_expires_at = nil
  @token_mutex = Mutex.new

  validate_configuration!
end

Instance Attribute Details

#algorithmObject (readonly)

Returns the value of attribute algorithm.



16
17
18
# File 'lib/a2a/client/auth/jwt.rb', line 16

def algorithm
  @algorithm
end

#headersObject (readonly)

Returns the value of attribute headers.



16
17
18
# File 'lib/a2a/client/auth/jwt.rb', line 16

def headers
  @headers
end

#payloadObject (readonly)

Returns the value of attribute payload.



16
17
18
# File 'lib/a2a/client/auth/jwt.rb', line 16

def payload
  @payload
end

#secretObject (readonly)

Returns the value of attribute secret.



16
17
18
# File 'lib/a2a/client/auth/jwt.rb', line 16

def secret
  @secret
end

#tokenObject (readonly)

Returns the value of attribute token.



16
17
18
# File 'lib/a2a/client/auth/jwt.rb', line 16

def token
  @token
end

Instance Method Details

#apply_to_request(request) ⇒ Object

Apply authentication to a Faraday request

Parameters:

  • request (Faraday::Request)

    The request to authenticate



71
72
73
# File 'lib/a2a/client/auth/jwt.rb', line 71

def apply_to_request(request)
  request.headers["Authorization"] = authorization_header
end

#authorization_headerString

Get authorization header value

Returns:

  • (String)

    The authorization header value



63
64
65
# File 'lib/a2a/client/auth/jwt.rb', line 63

def authorization_header
  "Bearer #{jwt_token}"
end

#expires_atTime?

Get token expiration time

Returns:

  • (Time, nil)

    Token expiration time



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/a2a/client/auth/jwt.rb', line 136

def expires_at
  if @token_expires_at
    @token_expires_at
  elsif @token
    begin
      payload = validate_token(@token, verify_signature: false).first
      exp = payload["exp"]
      Time.zone.at(exp) if exp
    rescue A2A::Errors::AuthenticationError
      nil
    end
  end
end

#generate_tokenString (private)

Generate a new JWT token

Returns:

  • (String)

    The generated JWT token



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/a2a/client/auth/jwt.rb', line 168

def generate_token
  raise ArgumentError, "Cannot generate token without secret" unless @secret

  # Build payload with expiration
  token_payload = @payload.dup

  if @expires_in
    now = Time.now.to_i
    token_payload["iat"] = now
    token_payload["exp"] = now + @expires_in
    @token_expires_at = Time.now + @expires_in
  end

  # Generate token
  @generated_token = ::JWT.encode(token_payload, @secret, @algorithm, @headers)
rescue ::JWT::EncodeError => e
  raise A2A::Errors::AuthenticationError, "JWT generation failed: #{e.message}"
end

#jwt_tokenString

Get a valid JWT token

Returns:

  • (String)

    The JWT token



46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/a2a/client/auth/jwt.rb', line 46

def jwt_token
  if @token
    # Static token
    @token
  else
    # Dynamic token generation
    @token_mutex.synchronize do
      generate_token if token_expired?
      @generated_token
    end
  end
end

#regenerate_token!String

Force regenerate the token (for dynamic tokens)

Returns:

  • (String)

    The new JWT token



124
125
126
127
128
129
130
# File 'lib/a2a/client/auth/jwt.rb', line 124

def regenerate_token!
  return @token if @token # Can't regenerate static tokens

  @token_mutex.synchronize do
    generate_token
  end
end

#token_expired?(token = nil) ⇒ Boolean

Check if the token is expired

Parameters:

  • token (String, nil) (defaults to: nil)

    Token to check (uses current token if nil)

Returns:

  • (Boolean)

    True if token is expired



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/a2a/client/auth/jwt.rb', line 98

def token_expired?(token = nil)
  return false unless @expires_in || token

  if @generated_token && @token_expires_at
    # Check generated token expiration
    Time.now >= (@token_expires_at - 30) # 30 second buffer
  elsif token || @token
    # Check token payload expiration
    begin
      payload = validate_token(token, verify_signature: false).first
      exp = payload["exp"]
      return false unless exp

      Time.now.to_i >= (exp - 30) # 30 second buffer
    rescue A2A::Errors::AuthenticationError
      true # Consider invalid tokens as expired
    end
  else
    false
  end
end

#validate_configuration!Object (private)

Validate the authentication configuration

Raises:

  • (ArgumentError)


154
155
156
157
158
159
160
161
162
# File 'lib/a2a/client/auth/jwt.rb', line 154

def validate_configuration!
  raise ArgumentError, "Either token or secret must be provided" if @token.nil? && @secret.nil?

  raise ArgumentError, "Payload is required for dynamic token generation" if @token.nil? && @payload.empty?

  return if %w[HS256 HS384 HS512 RS256 RS384 RS512 ES256 ES384 ES512].include?(@algorithm)

  raise ArgumentError, "Unsupported JWT algorithm: #{@algorithm}"
end

#validate_token(token = nil, verify_signature: true) ⇒ Hash

Validate the JWT token

Parameters:

  • token (String, nil) (defaults to: nil)

    Token to validate (uses current token if nil)

  • verify_signature (Boolean) (defaults to: true)

    Whether to verify the signature

Returns:

  • (Hash)

    Decoded JWT payload



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/a2a/client/auth/jwt.rb', line 81

def validate_token(token = nil, verify_signature: true)
  token_to_validate = token || jwt_token

  if verify_signature && @secret
    ::JWT.decode(token_to_validate, @secret, true, { algorithm: @algorithm })
  else
    ::JWT.decode(token_to_validate, nil, false)
  end
rescue ::JWT::DecodeError => e
  raise A2A::Errors::AuthenticationError, "JWT validation failed: #{e.message}"
end