Class: OmniAuth::Authify::JwtValidator

Inherits:
Object
  • Object
show all
Defined in:
lib/omniauth/authify/jwt_validator.rb

Overview

Validates OpenID Connect ID tokens issued by Authify.

Verifies the RS256 signature against the signing keys published at the organization's JWKS endpoint, and the standard ID token claims: iss, sub, aud, exp, iat, auth_time (when max_age applies), and nonce.

Constant Summary collapse

SUPPORTED_ALGORITHMS =

ID token signature algorithms this validator accepts

%w[RS256].freeze
REQUIRED_CLAIMS =

Claims that must be present in every ID token

%w[iss sub aud exp iat].freeze
NETWORK_ERRORS =

Network failures that map to a TokenValidationError during JWKS fetches

[SocketError, Errno::ECONNREFUSED, Timeout::Error, Net::OpenTimeout,
OpenSSL::SSL::SSLError].freeze

Instance Method Summary collapse

Constructor Details

#initialize(client_id:, issuer:, jwks_uri:) ⇒ JwtValidator

Creates a validator

Parameters:

  • client_id (String)

    the OAuth2 client ID (expected aud value)

  • issuer (String)

    the expected value of the ID token iss claim

  • jwks_uri (String)

    the organization's JWKS endpoint URL



31
32
33
34
35
36
# File 'lib/omniauth/authify/jwt_validator.rb', line 31

def initialize(client_id:, issuer:, jwks_uri:)
  @client_id = client_id
  @issuer = issuer
  @jwks_uri = URI(jwks_uri)
  @jwks = nil
end

Instance Method Details

#decode(jwt) ⇒ Array(Hash, Hash)

Decodes an ID token, verifying its signature but skipping claim checks.

Parameters:

  • jwt (String)

    the ID token

Returns:

  • (Array(Hash, Hash))

    the claims and the JOSE header

Raises:



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/omniauth/authify/jwt_validator.rb', line 66

def decode(jwt)
  header = token_head(jwt)
  algorithm = header["alg"]
  unless SUPPORTED_ALGORITHMS.include?(algorithm)
    raise TokenValidationError,
          "Signature algorithm of #{algorithm.inspect} is not supported. " \
          "Expected the ID token to be signed with RS256"
  end

  claims, = JWT.decode(
    jwt,
    nil,
    true,
    jwks: jwks_loader,
    algorithms: [algorithm],
    required_claims: REQUIRED_CLAIMS,
    verify_iss: false,
    verify_aud: false,
    verify_expiration: false,
    verify_iat: false
  )
  [claims, header]
rescue JWT::DecodeError, JWT::ExpiredSignature, JWT::JWKError => e
  raise TokenValidationError, "ID token could not be verified: #{e.message}"
end

#verify(jwt, authorize_params = {}) ⇒ Hash

Decodes an ID token and verifies its signature and claims.

Parameters:

  • jwt (String)

    the ID token

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

    per-login parameters stored at the start of the flow; may contain nonce, leeway, max_age and issuer (string or symbol keys)

Returns:

  • (Hash)

    the verified claims

Raises:



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/omniauth/authify/jwt_validator.rb', line 46

def verify(jwt, authorize_params = {})
  raise TokenValidationError, "ID token is required but missing" if jwt.to_s.empty?

  params = authorize_params || {}
  claims, = decode(jwt)
  verify_iss(claims, params)
  verify_sub(claims)
  verify_aud(claims)
  verify_expiration(claims, params)
  verify_iat(claims, params)
  verify_auth_time(claims, params)
  verify_nonce(claims, params)
  claims
end