Skip to content

Commit

Permalink
Various documentation additions
Browse files Browse the repository at this point in the history
  • Loading branch information
anakinj committed Oct 5, 2024
1 parent c9bec7b commit fa9195d
Show file tree
Hide file tree
Showing 20 changed files with 248 additions and 81 deletions.
3 changes: 0 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,6 @@ AllCops:
- 'gemfiles/*.gemfile'
- 'vendor/**/*'

Style/Documentation:
Enabled: false

Style/IfUnlessModifier:
Enabled: false

Expand Down
22 changes: 0 additions & 22 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,5 @@
# Changelog

## Upcoming breaking changes

Notable changes in the upcoming **version 3.0**:

- The indirect dependency to [rbnacl](https://github.com/RubyCrypto/rbnacl) will be removed:
- Support for the nonstandard SHA512256 algorithm will be removed.
- Support for Ed25519 will be moved to a [separate gem](https://github.com/anakinj/jwt-eddsa) for better dependency handling.

- Base64 decoding will no longer fallback on the looser RFC 2045.

- Claim verification has been [split into separate classes](https://github.com/jwt/ruby-jwt/pull/605) and has [a new api](https://github.com/jwt/ruby-jwt/pull/626) and lead to the following deprecations:
- The `::JWT::ClaimsValidator` class will be removed in favor of the functionality provided by `::JWT::Claims`.
- The `::JWT::Claims::verify!` method will be removed in favor of `::JWT::Claims::verify_payload!`.
- The `::JWT::JWA.create` method will be removed. No recommended alternatives.
- The `::JWT::Verify` class will be removed in favor of the functionality provided by `::JWT::Claims`.
- Calling `::JWT::Claims::Numeric.new` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.
- Calling `::JWT::Claims::Numeric.verify!` with a payload will be removed in favor of `::JWT::Claims::verify_payload!(payload, :numeric)`.

- The internal algorithms were [restructured](https://github.com/jwt/ruby-jwt/pull/607) to support extensions from separate libraries. The changes lead to a few deprecations and new requirements:
- The `sign` and `verify` static methods on all the algorithms (`::JWT::JWA`) will be removed.
- Custom algorithms are expected to include the `JWT::JWA::SigningAlgorithm` module.

## [v2.9.4](https://github.com/jwt/ruby-jwt/tree/v2.9.4) (NEXT)

[Full Changelog](https://github.com/jwt/ruby-jwt/compare/v2.9.3...main)
Expand Down
133 changes: 79 additions & 54 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions lib/jwt/claims.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ module Claims
class << self
# @deprecated Use {verify_payload!} instead. Will be removed in the next major version of ruby-jwt.
def verify!(payload, options)
Deprecations.warning('The ::JWT::Claims.verify! method is deprecated will be removed in the next major version of ruby-jwt')
DecodeVerifier.verify!(payload, options)
end

Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/audience.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The Audience class is responsible for validating the audience claim ('aud') in a JWT token.
class Audience
# Initializes a new Audience instance.
#
# @param expected_audience [String, Array<String>] the expected audience(s) for the JWT token.
def initialize(expected_audience:)
@expected_audience = expected_audience
end

# Verifies the audience claim ('aud') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidAudError] if the audience claim is invalid.
# @return [nil]
def verify!(context:, **_args)
aud = context.payload['aud']
raise JWT::InvalidAudError, "Invalid audience. Expected #{expected_audience}, received #{aud || '<none>'}" if ([*aud] & [*expected_audience]).empty?
Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/expiration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The Expiration class is responsible for validating the expiration claim ('exp') in a JWT token.
class Expiration
# Initializes a new Expiration instance.
#
# @param leeway [Integer] the amount of leeway (in seconds) to allow when validating the expiration time. Default: 0.
def initialize(leeway:)
@leeway = leeway || 0
end

# Verifies the expiration claim ('exp') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::ExpiredSignature] if the token has expired.
# @return [nil]
def verify!(context:, **_args)
return unless context.payload.is_a?(Hash)
return unless context.payload.key?('exp')
Expand Down
7 changes: 7 additions & 0 deletions lib/jwt/claims/issued_at.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

module JWT
module Claims
# The IssuedAt class is responsible for validating the issued at claim ('iat') in a JWT token.
class IssuedAt
# Verifies the issued at claim ('iat') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidIatError] if the issued at claim is invalid.
# @return [nil]
def verify!(context:, **_args)
return unless context.payload.is_a?(Hash)
return unless context.payload.key?('iat')
Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/issuer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The Issuer class is responsible for validating the issuer claim ('iss') in a JWT token.
class Issuer
# Initializes a new Issuer instance.
#
# @param issuers [String, Symbol, Array<String, Symbol>] the expected issuer(s) for the JWT token.
def initialize(issuers:)
@issuers = Array(issuers).map { |item| item.is_a?(Symbol) ? item.to_s : item }
end

# Verifies the issuer claim ('iss') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidIssuerError] if the issuer claim is invalid.
# @return [nil]
def verify!(context:, **_args)
case (iss = context.payload['iss'])
when *issuers
Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/jwt_id.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The JwtId class is responsible for validating the JWT ID claim ('jti') in a JWT token.
class JwtId
# Initializes a new JwtId instance.
#
# @param validator [#call] an object responding to `call` to validate the JWT ID.
def initialize(validator:)
@validator = validator
end

# Verifies the JWT ID claim ('jti') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidJtiError] if the JWT ID claim is invalid or missing.
# @return [nil]
def verify!(context:, **_args)
jti = context.payload['jti']
if validator.respond_to?(:call)
Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/not_before.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The NotBefore class is responsible for validating the 'nbf' (Not Before) claim in a JWT token.
class NotBefore
# Initializes a new NotBefore instance.
#
# @param leeway [Integer] the amount of leeway (in seconds) to allow when validating the 'nbf' claim. Defaults to 0.
def initialize(leeway:)
@leeway = leeway || 0
end

# Verifies the 'nbf' (Not Before) claim in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::ImmatureSignature] if the 'nbf' claim has not been reached.
# @return [nil]
def verify!(context:, **_args)
return unless context.payload.is_a?(Hash)
return unless context.payload.key?('nbf')
Expand Down
22 changes: 22 additions & 0 deletions lib/jwt/claims/numeric.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

module JWT
module Claims
# The Numeric class is responsible for validating numeric claims in a JWT token.
# The numeric claims are: exp, iat and nbf
class Numeric
# The Compat class provides backward compatibility for numeric claim validation.
# @api private
class Compat
def initialize(payload)
@payload = payload
Expand All @@ -13,23 +17,41 @@ def verify!
end
end

# List of numeric claims that can be validated.
NUMERIC_CLAIMS = %i[
exp
iat
nbf
].freeze

private_constant(:NUMERIC_CLAIMS)

# @api private
def self.new(*args)
return super if args.empty?

Deprecations.warning('Calling ::JWT::Claims::Numeric.new with the payload will be removed in the next major version of ruby-jwt')
Compat.new(*args)
end

# Verifies the numeric claims in the JWT context.
#
# @param context [Object] the context containing the JWT payload.
# @raise [JWT::InvalidClaimError] if any numeric claim is invalid.
# @return [nil]
def verify!(context:)
validate_numeric_claims(context.payload)
end

# Verifies the numeric claims in the JWT payload.
#
# @param payload [Hash] the JWT payload containing the claims.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidClaimError] if any numeric claim is invalid.
# @return [nil]
# @deprecated The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt
def self.verify!(payload:, **_args)
Deprecations.warning('The ::JWT::Claims::Numeric.verify! method will be removed in the next major version of ruby-jwt.')
JWT::Claims.verify_payload!(payload, :numeric)
end

Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/required.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The Required class is responsible for validating that all required claims are present in a JWT token.
class Required
# Initializes a new Required instance.
#
# @param required_claims [Array<String>] the list of required claims.
def initialize(required_claims:)
@required_claims = required_claims
end

# Verifies that all required claims are present in the JWT payload.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::MissingRequiredClaim] if any required claim is missing.
# @return [nil]
def verify!(context:, **_args)
required_claims.each do |required_claim|
next if context.payload.is_a?(Hash) && context.payload.key?(required_claim)
Expand Down
10 changes: 10 additions & 0 deletions lib/jwt/claims/subject.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

module JWT
module Claims
# The Subject class is responsible for validating the subject claim ('sub') in a JWT token.
class Subject
# Initializes a new Subject instance.
#
# @param expected_subject [String] the expected subject for the JWT token.
def initialize(expected_subject:)
@expected_subject = expected_subject.to_s
end

# Verifies the subject claim ('sub') in the JWT token.
#
# @param context [Object] the context containing the JWT payload.
# @param _args [Hash] additional arguments (not used).
# @raise [JWT::InvalidSubError] if the subject claim is invalid.
# @return [nil]
def verify!(context:, **_args)
sub = context.payload['sub']
raise(JWT::InvalidSubError, "Invalid subject. Expected #{expected_subject}, received #{sub || '<none>'}") unless sub.to_s == expected_subject
Expand Down
6 changes: 4 additions & 2 deletions lib/jwt/claims_validator.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
# frozen_string_literal: true

require_relative 'error'

module JWT
# @deprecated Use `Claims.verify_payload!` directly instead.
class ClaimsValidator
# @deprecated Use `Claims.verify_payload!` directly instead.
def initialize(payload)
Deprecations.warning('The ::JWT::ClaimsValidator class is deprecated and will be removed in the next major version of ruby-jwt')
@payload = payload
end

# @deprecated Use `Claims.verify_payload!` directly instead.
def validate!
Claims.verify_payload!(@payload, :numeric)
true
Expand Down
8 changes: 8 additions & 0 deletions lib/jwt/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,19 @@
require_relative 'configuration/container'

module JWT
# The Configuration module provides methods to configure JWT settings.
module Configuration
# Configures the JWT settings.
#
# @yield [config] Gives the current configuration to the block.
# @yieldparam config [JWT::Configuration::Container] the configuration container.
def configure
yield(configuration)
end

# Returns the JWT configuration container.
#
# @return [JWT::Configuration::Container] the configuration container.
def configuration
@configuration ||= ::JWT::Configuration::Container.new
end
Expand Down
20 changes: 20 additions & 0 deletions lib/jwt/configuration/container.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,28 @@

module JWT
module Configuration
# The Container class holds the configuration settings for JWT.
class Container
# @!attribute [rw] decode
# @return [DecodeConfiguration] the decode configuration.
# @!attribute [rw] jwk
# @return [JwkConfiguration] the JWK configuration.
# @!attribute [rw] strict_base64_decoding
# @return [Boolean] whether strict Base64 decoding is enabled.
attr_accessor :decode, :jwk, :strict_base64_decoding

# @!attribute [r] deprecation_warnings
# @return [Symbol] the deprecation warnings setting.
attr_reader :deprecation_warnings

# Initializes a new Container instance and resets the configuration.
def initialize
reset!
end

# Resets the configuration to default values.
#
# @return [void]
def reset!
@decode = DecodeConfiguration.new
@jwk = JwkConfiguration.new
Expand All @@ -22,6 +36,12 @@ def reset!
end

DEPRECATION_WARNINGS_VALUES = %i[once warn silent].freeze
private_constant(:DEPRECATION_WARNINGS_VALUES)
# Sets the deprecation warnings setting.
#
# @param value [Symbol] the deprecation warnings setting. Must be one of `:once`, `:warn`, or `:silent`.
# @raise [ArgumentError] if the value is not one of the supported values.
# @return [void]
def deprecation_warnings=(value)
raise ArgumentError, "Invalid deprecation_warnings value #{value}. Supported values: #{DEPRECATION_WARNINGS_VALUES}" unless DEPRECATION_WARNINGS_VALUES.include?(value)

Expand Down
24 changes: 24 additions & 0 deletions lib/jwt/configuration/decode_configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,29 @@

module JWT
module Configuration
# The DecodeConfiguration class holds the configuration settings for decoding JWT tokens.
class DecodeConfiguration
# @!attribute [rw] verify_expiration
# @return [Boolean] whether to verify the expiration claim.
# @!attribute [rw] verify_not_before
# @return [Boolean] whether to verify the not before claim.
# @!attribute [rw] verify_iss
# @return [Boolean] whether to verify the issuer claim.
# @!attribute [rw] verify_iat
# @return [Boolean] whether to verify the issued at claim.
# @!attribute [rw] verify_jti
# @return [Boolean] whether to verify the JWT ID claim.
# @!attribute [rw] verify_aud
# @return [Boolean] whether to verify the audience claim.
# @!attribute [rw] verify_sub
# @return [Boolean] whether to verify the subject claim.
# @!attribute [rw] leeway
# @return [Integer] the leeway in seconds for time-based claims.
# @!attribute [rw] algorithms
# @return [Array<String>] the list of acceptable algorithms.
# @!attribute [rw] required_claims
# @return [Array<String>] the list of required claims.

attr_accessor :verify_expiration,
:verify_not_before,
:verify_iss,
Expand All @@ -14,6 +36,7 @@ class DecodeConfiguration
:algorithms,
:required_claims

# Initializes a new DecodeConfiguration instance with default settings.
def initialize
@verify_expiration = true
@verify_not_before = true
Expand All @@ -27,6 +50,7 @@ def initialize
@required_claims = []
end

# @api private
def to_h
{
verify_expiration: verify_expiration,
Expand Down
Loading

0 comments on commit fa9195d

Please sign in to comment.