Hugo
Hugo

Reputation: 2484

How to sign JWT?

I'm trying to secure a Sinatra API.

I'm using ruby-jwt to create the JWT, but I don't know exactly what to sign it with.

I'm trying to use the user's BCrypt password_digest, but every time password_digest is called it changes, making the signature invalid when I go to verify it.

Upvotes: 6

Views: 2830

Answers (3)

Marcelo Fonseca
Marcelo Fonseca

Reputation: 1844

For RS256 public and private key strategy you can use Ruby OpenSSL lib:

Generating keys:

key = OpenSSL::PKey::RSA.new 2048

open 'private_key.pem', 'w' do |io| io.write key.to_pem end
open 'public_key.pem', 'w' do |io| io.write key.public_key.to_pem end

Load key from .pem file to sign token:

priv_key = OpenSSL::PKey::RSA.new File.read 'private_key.pem'
token = JWT.encode payload, priv_key, 'RS256'

Load key from .pem file to Verify token(Create a middleware for this):

      begin
        # env.fetch gets http header
        bearer = env.fetch('HTTP_AUTHORIZATION').slice(7..-1)
        pub_key = OpenSSL::PKey::RSA.new File.read 'public_key.pem'
        payload = JWT.decode bearer, pub_key, true, { algorithm: 'RS256'}
        
        # access your payload here
  
        @app.call env

      rescue JWT::ExpiredSignature
        [403, { 'Content-Type' => 'text/plain' }, ['The token has expired.']]
      rescue JWT::DecodeError
        [401, { 'Content-Type' => 'text/plain' }, ['A token must be passed.']]
      rescue JWT::InvalidIssuerError
        [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid issuer.']]
      rescue JWT::InvalidIatError
        [403, { 'Content-Type' => 'text/plain' }, ['The token does not have a valid "issued at" time.']]
      end

To use RSA key in your .env instead of loading a file, you will need to use gem 'dotenv' and import the key as a single line variable with the use of newline '\n'. check this question on how to do it. example:

PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nmineminemineminemine\nmineminemineminemine\nmineminemine...\n-----END PUBLIC KEY-----\n"

as an .env PUBLIC_KEY variable, loading the key will change to this:

key = OpenSSL::PKey::RSA.new ENV['PUBLIC_KEY']

Upvotes: 5

Control Complex
Control Complex

Reputation: 163

According to wikipedia, a secret key used in cryptography is basically just that, a key to open the lock. The key should be consistent and reliable, but not easy to duplicate, just like a key you would use on your home.

As stated in this answer, secret keys should be randomly-generated. However, you still want the key to be retained for use across the application. By using the password digest from bcrypt, you are actually using a hashed key that was derived from a base secret key (the password). Because the hash is random, this is not a reliable secret key to use, as you stated.

The previous answer using SecureRandom.hex(64) is a great way to create an initial base application key. However, in a production system, you should be taking this in as a configuration variable and storing it for consistent use across multiple runs of your application (for example following a server reboot, you should not invalidate all of your user's JWTs) or across multiple distributed servers. This article gives an example of pulling in the secret key from an environment variable for rails.

Upvotes: 1

joelparkerhenderson
joelparkerhenderson

Reputation: 35483

Use any kind of application secret key, not a user's bcrypt password digest.

For example, use the dot env gem and a .env file, with an entry such as:

JWT_KEY=YOURSIGNINGKEYGOESHERE

I personally generate a key by using a simple random hex string:

SecureRandom.hex(64)

The hex string contains just 0-9 and a-f, so the string is URL safe.

Upvotes: 4

Related Questions