Skip to content

Commit

Permalink
JWT::EncodedToken#verify! to bundle signature and claim validation
Browse files Browse the repository at this point in the history
  • Loading branch information
anakinj committed Dec 28, 2024
1 parent b3701ef commit 1f5d745
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

**Features:**

- JWT::EncodedToken#verify! method that bundles signature and claim validation [#647](https://github.com/jwt/ruby-jwt/pull/647) ([@anakinj](https://github.com/anakinj))
- Your contribution here

**Fixes and enhancements:**
Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,16 @@ encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
```

A simple way to handle token verification using the `JWT::EncodedToken#verify!` method. It verifies the signature and the `exp` claim by default.

```ruby
encoded_token = JWT::EncodedToken.new(token.jwt)
encoded_token.verify!(signature: {algorithm: 'HS256', key: "secret"})

encoded_token.payload # => { 'exp'=>1234, 'jti'=>'1234", 'sub'=>'my-subject' }
encoded_token.header # {'kid'=>'hmac', 'alg'=>'HS256'}
```

### Detached payload

The `::JWT::Token#detach_payload!` method can be use to detach the payload from the JWT.
Expand Down
11 changes: 10 additions & 1 deletion lib/jwt/claims/verification_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,25 @@

module JWT
module Claims
# @api private
# Provides methods to verify the claims of a token.
module VerificationMethods
# Verifies the claims of the token.
# @param options [Array<Symbol>, Hash] the claims to verify.
# @raise [JWT::DecodeError] if the claims are invalid.
def verify_claims!(*options)
Verifier.verify!(self, *options)
end

# Returns the errors of the claims of the token.
# @param options [Array<Symbol>, Hash] the claims to verify.
# @return [Array<Symbol>] the errors of the claims.
def claim_errors(*options)
Verifier.errors(self, *options)
end

# Returns whether the claims of the token are valid.
# @param options [Array<Symbol>, Hash] the claims to verify.
# @return [Boolean] whether the claims are valid.
def valid_claims?(*options)
claim_errors(*options).empty?
end
Expand Down
16 changes: 16 additions & 0 deletions lib/jwt/encoded_token.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,22 @@ def signing_input
[encoded_header, encoded_payload].join('.')
end

# Verifies the token signature and claims.
# By default it verifies the 'exp' claim.
#
# @example
# encoded_token.verify!(signature: { algorithm: 'HS256', key: 'secret' }, claims: [:exp])
#
# @param signature [Hash] the parameters for signature verification (see {#verify_signature!}).
# @param claims [Array<Symbol>, Hash] the claims to verify (see {#verify_claims!}).
# @return [nil]
# @raise [JWT::DecodeError] if the signature or claim verification fails.
def verify!(signature:, claims: [:exp])
verify_signature!(**signature)
claims.is_a?(Array) ? verify_claims!(*claims) : verify_claims!(claims)
nil
end

# Verifies the signature of the JWT token.
#
# @param algorithm [String, Array<String>, Object, Array<Object>] the algorithm(s) to use for verification.
Expand Down
25 changes: 25 additions & 0 deletions spec/jwt/encoded_token_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,31 @@
end
end

context '#verify!' do
context 'when key is valid' do
it 'does not raise' do
expect(token.verify!(signature: { algorithm: 'HS256', key: 'secret' })).to eq(nil)
end
end

context 'when key is invalid' do
it 'raises an error' do
expect { token.verify!(signature: { algorithm: 'HS256', key: 'wrong' }) }.to raise_error(JWT::VerificationError, 'Signature verification failed')
end
end

context 'when claims are invalid' do
let(:payload) { { 'pay' => 'load', exp: Time.now.to_i - 1000 } }

it 'raises an error' do
expect do
token.verify!(signature: { algorithm: 'HS256', key: 'secret' },
claims: { exp: { leeway: 900 } })
end.to raise_error(JWT::ExpiredSignature, 'Signature has expired')
end
end
end

describe '#valid_claims?' do
context 'exp claim' do
let(:payload) { { 'exp' => Time.now.to_i - 10, 'pay' => 'load' } }
Expand Down

0 comments on commit 1f5d745

Please sign in to comment.