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

Raises:



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

Raises:



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