jwt / ruby-jwt

A ruby implementation of the RFC 7519 OAuth JSON Web Token (JWT) standard.
https://jwt.github.io/ruby-jwt/
MIT License
3.59k stars 373 forks source link

Regression: 2.4.0 results in an ArgumentError with an invalid Base64 string (expecting JWT::DecodeError) #483

Closed freakyfelt closed 2 years ago

freakyfelt commented 2 years ago

Calling JWT.decode(token) used to result in a JWT::DecodeError, however this is now raising an ArgumentError that must be caught separately

➜  ~ gem install jwt '= 2.4.0'
Successfully installed jwt-2.4.0
Parsing documentation for jwt-2.4.0
Installing ri documentation for jwt-2.4.0
Done installing documentation for jwt after 0 seconds
➜  ~ irb
3.1.1 :003 > JWT.decode('hello.there.world')
3.1.1 :001 > require 'jwt'
3.1.1 :002 > JWT.decode('hello')
~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt/decode.rb:108:in `validate_segment_count!': Not enough or too many segments (JWT::DecodeError)
    from ~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt/decode.rb:25:in `decode_segments'
    from ~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt.rb:28:in `decode'
    from (irb):2:in `<main>'
    from ~/.rvm/rubies/ruby-3.1.1/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
    from ~/.rvm/rubies/ruby-3.1.1/bin/irb:25:in `load'
    from ~/.rvm/rubies/ruby-3.1.1/bin/irb:25:in `<main>'
3.1.1 :003 > JWT.decode('hello.there.world')
<internal:pack>:309:in `unpack1': invalid base64 (ArgumentError)
    from ~/.rvm/rubies/ruby-3.1.1/lib/ruby/3.1.0/base64.rb:74:in `strict_decode64'
    from ~/.rvm/rubies/ruby-3.1.1/lib/ruby/3.1.0/base64.rb:108:in `urlsafe_decode64'
    from ~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt/decode.rb:120:in `decode_crypto'
    from ~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt/decode.rb:27:in `decode_segments'
    from ~/.rvm/gems/ruby-3.1.1/gems/jwt-2.4.0/lib/jwt.rb:28:in `decode'
    from (irb):3:in `<main>'
    from ~/.rvm/rubies/ruby-3.1.1/lib/ruby/gems/3.1.0/gems/irb-1.4.1/exe/irb:11:in `<top (required)>'
    from ~/.rvm/rubies/ruby-3.1.1/bin/irb:25:in `load'
    from ~/.rvm/rubies/ruby-3.1.1/bin/irb:25:in `<main>'
excpt commented 2 years ago

Thanks for the detailed report of that bug. That's not the intended behavior.

freakyfelt commented 2 years ago

Creating a fix PR, will get it posted momentarily

anakinj commented 2 years ago

Unfortunate side-effect of using the built-in Base64.urlsafe_decode64 that internally uses the Base64.strict_decode64 (previously Base64.decode64)

Now I'm a little unsure of what type of Base64 decoding you are supposed to use for a JWT token. The RFC only refers to Base64url.

The Base64.strict_decode64 seems to be according to RFC 4648 whereas the Base64.decode64 is according to RFC 2045. Based on the String#unpack documentation:

m            | String  | base64 encoded string (RFC 2045) (default)
             |         | base64 encoded string (RFC 4648) if followed by 0

I guess using the strict decoding is fine, just that when something invalid is given the behaviour is different as noticed here.