Verifying Signed JWTs (JWS) with Ruby

Featured image for sharing metadata for article

When you're working with JSON Web Tokens (JWTs), you'll almost certainly be validating that the contents of the token is sent by the correct service by verifying the token's signature.

However if these are, for instance, access tokens, you should not be putting them into an online tool like [JWT.io] as you pose a risk of information leakage, as well as potentially compromising accounts!

Even though the website says it should be done client-side, it's still a bad practice to randomly copy-paste potentially dangerous data around.

So how do you easily validate the signature, without it touching an easy-to-use service? Below you can find a simple code snippet to do the work for you using Ruby's ruby-jwt library.

For this example, the expectation is that you can run them as the following, where the process will return successfully (with exit status 0) or unsuccessfully (with exit status 1) based on whether the JWT was successfully verified:

ruby verify.rb payload.jwt 'hmac-key-here'
ruby verify.rb payload.jwt 'hmac-key-here' 'HS256'
ruby verify.rb payload.jwt '/path/to/key.pem' 'RS256'
ruby verify.rb payload.jwt '/path/to/key.pem' 'ES256'
ruby verify.rb payload.jwt 'https://url/of/jwks_uri' 'RS256'

With ruby-jwt

Using ruby-jwt, we have the following code:

#!/usr/bin/env ruby
require 'jwt'
require 'net/http'

def parse_jwks(jwks_uri)
  res = Net::HTTP.get_response(URI(jwks_uri))
  JSON.parse(res.body, symbolize_names: true) # jwt expects symbols for all Hash keys
end

jwt = File.read ARGV[0]
maybe_secret_or_jwks = ARGV[1]
algorithm = ARGV[2] || 'HS256'

opts = {
  algorithm: algorithm,
}

if maybe_secret_or_jwks.start_with? 'http'
  jwk_loader = ->(options) do
    @cached_keys = nil if options[:invalidate] # need to reload the keys
    @cached_keys ||= { keys: parse_jwks(maybe_secret_or_jwks)[:keys] }
  end

  opts[:jwks] = jwk_loader
else
  maybe_secret = maybe_secret_or_jwks

  if File.exists? maybe_secret
    maybe_secret = OpenSSL::PKey.read(File.read maybe_secret)
  end
end

begin
  JWT.decode jwt, maybe_secret, true, opts
rescue JWT::VerificationError
  exit 1
end

Written by Jamie Tanna's profile image Jamie Tanna on , and last updated on .

Content for this article is shared under the terms of the Creative Commons Attribution Non Commercial Share Alike 4.0 International, and code is shared under the Apache License 2.0.

#blogumentation #ruby #command-line #jwt #json.

This post was filed under articles.

Interactions with this post

Interactions with this post

Below you can find the interactions that this page has had using WebMention.

Have you written a response to this post? Let me know the URL:

Do you not have a website set up with WebMention capabilities? You can use Comment Parade.