Class: CyberSource::Authentication::Util::JWT::JWTUtility

Inherits:
Object
  • Object
show all
Defined in:
lib/AuthenticationSDK/util/JWT/JWTUtility.rb

Overview

JWT Utility Class

Provides JWT parsing, verification, and JWK handling functionality.

Author:

  • CyberSource

Constant Summary collapse

SUPPORTED_ALGORITHMS =

Supported JWT algorithms

%w[RS256 RS384 RS512].freeze

Class Method Summary collapse

Class Method Details

.parse(jwt_token) ⇒ Hash

Parses a JWT token and extracts its header, payload, and signature components

Parameters:

  • The JWT token to parse

Returns:

  • Hash containing header, payload, signature, and raw parts

Raises:

  • If the JWT token is invalid or malformed



29
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
# File 'lib/AuthenticationSDK/util/JWT/JWTUtility.rb', line 29

def parse(jwt_token)
  raise InvalidJwtException.new('JWT token is null or undefined') if jwt_token.to_s.empty?

  token_parts = jwt_token.split('.')
  if token_parts.length != 3
    raise InvalidJwtException.new('Invalid JWT token format: expected 3 parts separated by dots')
  end

  if token_parts.any?(&:empty?)
    raise InvalidJwtException.new('Malformed JWT : JWT provided does not conform to the proper structure for JWT')
  end

  begin
    header = JSON.parse(::JWT::Base64.url_decode(token_parts[0]))
    payload = JSON.parse(::JWT::Base64.url_decode(token_parts[1]))
    signature = token_parts[2]

    {
      header: header,
      payload: payload,
      signature: signature,
      raw_header: token_parts[0],
      raw_payload: token_parts[1]
    }
  rescue StandardError => e
    raise InvalidJwtException.new("Malformed JWT cannot be parsed: #{e.message}", e)
  end
end

.verify_jwt(jwt_token, public_key) ⇒ void

This method returns an undefined value.

Verifies a JWT token using an RSA public key

Parameters:

  • The JWT token to verify

  • The RSA public key (JWK Hash or JSON string)

Raises:

  • If JWT parsing fails

  • If signature verification fails

  • If the public key is invalid

  • If JSON parsing fails



68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/AuthenticationSDK/util/JWT/JWTUtility.rb', line 68

def verify_jwt(jwt_token, public_key)
  raise JwtSignatureValidationException.new('No public key found') if public_key.to_s.empty?
  raise JwtSignatureValidationException.new('JWT token is null or undefined') if jwt_token.to_s.empty?

  parsed_token = parse(jwt_token)
  header = parsed_token[:header]

  if header['alg'].nil? || header['alg'].to_s.empty?
    raise JwtSignatureValidationException.new('JWT header missing algorithm (alg) field')
  end

  algorithm = header['alg']
  unless SUPPORTED_ALGORITHMS.include?(algorithm)
    supported_algs = SUPPORTED_ALGORITHMS.join(', ')
    raise JwtSignatureValidationException.new(
      format('Unsupported JWT algorithm: %s. Supported algorithms: %s', algorithm, supported_algs)
    )
  end

  jwk_key = validate_and_parse_jwk(public_key)
  jwk_key['alg'] ||= algorithm

  begin
    rsa_key = jwk_to_rsa_key(jwk_key)
    ::JWT.decode(jwt_token, rsa_key, true, { algorithm: algorithm })

  rescue ::JWT::VerificationError => e
    raise JwtSignatureValidationException.new('JWT signature verification failed', e)
  rescue ::JWT::ExpiredSignature => e
    raise JwtSignatureValidationException.new('JWT has expired (exp claim)', e)
  rescue ::JWT::ImmatureSignature => e
    raise JwtSignatureValidationException.new('JWT not yet valid (nbf claim)', e)
  rescue ::JWT::DecodeError => e
    raise JwtSignatureValidationException.new("JWT verification failed: #{e.message}", e)
  rescue JwtSignatureValidationException, InvalidJwtException
    raise
  rescue StandardError => e
    raise JwtSignatureValidationException.new("JWT verification failed: #{e.message}", e)
  end
end