Skip to content
This repository has been archived by the owner on Mar 22, 2021. It is now read-only.

Commit

Permalink
Add ability to decode tokens via JWKs
Browse files Browse the repository at this point in the history
  • Loading branch information
fightingtheboss committed Nov 25, 2016
1 parent b32e6c0 commit a1c20fe
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 2 deletions.
31 changes: 29 additions & 2 deletions app/model/knock/auth_token.rb
@@ -1,4 +1,7 @@
require 'uri'
require 'jwt'
require 'json/jwt'
require 'net/http'

module Knock
class AuthToken
Expand All @@ -7,7 +10,20 @@ class AuthToken

def initialize payload: {}, token: nil, verify_options: {}
if token.present?
@payload, _ = JWT.decode token, decode_key, true, options.merge(verify_options)
token_decode_key = decode_key

if token_decode_key.is_a?(JSON::JWK::Set)
@payload = JSON::JWT.decode(token, token_decode_key)

options.merge(verify_options).each do |key, val|
next unless key.to_s =~ /verify/

JWT::Verify.send(key, payload, @options) if val
end
else
@payload, _ = JWT.decode token, token_decode_key, true, options.merge(verify_options)
end

@token = token
else
@payload = claims.merge(payload)
Expand Down Expand Up @@ -35,7 +51,18 @@ def secret_key
end

def decode_key
Knock.token_public_key || secret_key
if Knock.token_public_key
if Knock.token_public_key =~ /^#{URI::Parser.new.make_regexp(['http', 'https'])}$/
# This means there's a JWK or JWKs public key that we need to fetch
JSON::JWK::Set.new(
JSON.parse( Net::HTTP.get( URI(Knock.token_public_key) ) )
)
else
Knock.token_public_key
end
else
secret_key
end
end

def options
Expand Down
1 change: 1 addition & 0 deletions knock.gemspec
Expand Up @@ -24,4 +24,5 @@ Gem::Specification.new do |s|

s.add_development_dependency "sqlite3", "~> 1.3"
s.add_development_dependency "timecop", "~> 0.8.0"
s.add_development_dependency "webmock", "~> 2.1"
end
19 changes: 19 additions & 0 deletions test/model/knock/auth_token_test.rb
@@ -1,6 +1,10 @@
require 'test_helper'
require 'jwt'
require 'timecop'
require 'webmock/minitest'

# Disable all remote connections
WebMock.disable_net_connect!

module Knock
class AuthTokenTest < ActiveSupport::TestCase
Expand All @@ -27,6 +31,21 @@ class AuthTokenTest < ActiveSupport::TestCase
assert_nothing_raised { AuthToken.new(token: token) }
end

test "decode RSA encoded tokens with JWKs from URL" do
rsa_private = OpenSSL::PKey::RSA.generate 2048
rsa_public = rsa_private.public_key

Knock.token_public_key = 'https://example.com/.well-known/jwks.json'
Knock.token_signature_algorithm = 'RS256'

stub_request(:get, Knock.token_public_key)
.to_return(body: JSON::JWK::Set.new(JSON::JWK.new(rsa_public)).to_json)

token = JSON::JWT.new({sub: "1"}).sign(JSON::JWK.new(rsa_private)).to_s

assert_nothing_raised { AuthToken.new(token: token) }
end

test "encode tokens with RSA" do
rsa_private = OpenSSL::PKey::RSA.generate 2048
Knock.token_secret_signature_key = -> { rsa_private }
Expand Down

0 comments on commit a1c20fe

Please sign in to comment.