Skip to content

Commit

Permalink
started work on decoding globus token. datacite/datacite#936
Browse files Browse the repository at this point in the history
  • Loading branch information
Martin Fenner committed Jan 23, 2020
1 parent 4db2935 commit 983f26f
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 4 deletions.
48 changes: 45 additions & 3 deletions app/models/concerns/authenticable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,40 @@ def encode_alb_token(payload)
nil
end

# decode JWT token using SHA-256 hash algorithm
# use only for testing, as we don't have private key for JWT encoded by Globus
def encode_globus_token(payload)
return nil if payload.blank? || !Rails.env.test?

# replace newline characters with actual newlines
private_key = OpenSSL::PKey.read(File.read(Rails.root.join("spec", "fixtures", "certs", "ec512-private.pem").to_s))
JWT.encode(payload, private_key, 'RS512')
rescue OpenSSL::PKey::ECError => e
Rails.logger.error e.inspect + " for " + payload.inspect

nil
end

# decode JWT token. Check whether it is a DataCite or Globus JWT via the JWT header
# DataCite uses RS256, Globus uses RS512
def decode_token(token)
public_key = OpenSSL::PKey::RSA.new(ENV['JWT_PUBLIC_KEY'].to_s.gsub('\n', "\n"))
payload = (JWT.decode token, public_key, true, { :algorithm => 'RS256' }).first
# check that JWT has header, payload and secret, separated by dot
token_parts = token.to_s.split(".")
raise JWT::DecodeError if token_parts.length != 3

# decode token
header = JSON.parse(Base64.urlsafe_decode64(token_parts.first))
case header["alg"]
when "RS256"
# DataCite JWT
public_key = OpenSSL::PKey::RSA.new(ENV['JWT_PUBLIC_KEY'].to_s.gsub('\n', "\n"))
payload = (JWT.decode token, public_key, true, { :algorithm => 'RS256' }).first
when "RS512"
# Globus JWT
public_key = OpenSSL::PKey::RSA.new(cached_globus_public_key.fetch("n", nil).to_s.gsub('\n', "\n"))
payload = (JWT.decode token, public_key, true, { :algorithm => 'RS512' }).first
else
raise JWT::DecodeError, "Algorithm #{header["alg"]} is not supported."
end

# check whether token has expired
fail JWT::ExpiredSignature, "The token has expired." unless Time.now.to_i < payload["exp"].to_i
Expand Down Expand Up @@ -191,6 +221,18 @@ def encode_alb_token(payload)
nil
end

# encode token using RSA and SHA-512
# use this only for testing as private key is publicly available from ruby-jwt gem
def encode_globus_token(payload)
return nil if payload.blank? || !Rails.env.test?
private_key = OpenSSL::PKey.read(File.read(Rails.root.join("spec", "fixtures", "certs", "ec512-private.pem").to_s))
JWT.encode(payload, private_key, 'RS512')
rescue OpenSSL::PKey::ECError => e
Rails.logger.error e.inspect + " for " + payload.inspect

nil
end

# basic auth
def encode_auth_param(username: nil, password: nil)
return nil unless username.present? && password.present?
Expand Down
8 changes: 8 additions & 0 deletions app/models/concerns/cacheable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,14 @@ def cached_alb_public_key(kid)
response.body.fetch("data", nil)
end
end

def cached_globus_public_key
Rails.cache.fetch("globus_public_key", expires_in: 1.month) do
url = "https://auth.globus.org/jwk.json"
response = Maremma.get(url)
response.body.dig("data", "keys", 0)
end
end
end

module ClassMethods
Expand Down
38 changes: 37 additions & 1 deletion spec/concerns/authenticable_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
let(:token) { User.generate_token }
subject { User.new(token) }

describe 'decode_token' do
describe "decode_token DataCite" do
it "has name" do
payload = subject.decode_token(token)
expect(payload["name"]).to eq("Josiah Carberry")
Expand All @@ -28,6 +28,30 @@
end
end

# describe "decode_token Globus", vcr: true do
# it "has name" do
# payload = subject.decode_token(token)
# expect(payload["name"]).to eq("Josiah Carberry")
# end

# it "empty token" do
# payload = subject.decode_token("")
# expect(payload).to eq(errors: "The token could not be decoded.")
# end

# it "invalid token" do
# payload = subject.decode_token("abc")
# expect(payload).to eq(errors: "The token could not be decoded.")
# end

# it "expired token" do
# token = User.generate_token(exp: 0)
# subject = User.new(token)
# payload = subject.decode_token(token)
# expect(payload).to eq(errors: "The token has expired.")
# end
# end

describe 'decode_alb_token' do
let(:token) { User.generate_alb_token }

Expand Down Expand Up @@ -197,6 +221,18 @@
expect(token).to be_nil
end
end

describe 'encode_globus_token' do
it "with name" do
token = subject.encode_globus_token("name" => "Josiah Carberry")
expect(token).to start_with("eyJhbG")
end

it "empty string" do
token = subject.encode_globus_token("")
expect(token).to be_nil
end
end
end

describe Provider, type: :model do
Expand Down
10 changes: 10 additions & 0 deletions spec/fixtures/certs/ec512-private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN EC PARAMETERS-----
BgUrgQQAIw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIB0/+ffxEj7j62xvGaB5pvzk888e412ESO/EK/K0QlS9dSF8+Rj1rG
zqpRB8fvDnoe8xdmkW/W5GKzojMyv7YQYumgBwYFK4EEACOhgYkDgYYABAEw74Yw
aTbPY6TtWmxx6LJDzCX2nKWCPnKdZcEH9Ncu8g5RjRBRq2yacja3OoS6nA2YeDng
reBJxZr376P6Ns6XcQFWDA6K/MCTrEBCsPxXZNxd8KR9vMGWhgNtWRrcKzwJfQkr
suyehZkbbYyFnAWyARKHZuV7VUXmeEmRS/f93MPqVA==
-----END EC PRIVATE KEY-----
6 changes: 6 additions & 0 deletions spec/fixtures/certs/ec512-public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBMO+GMGk2z2Ok7VpsceiyQ8wl9pyl
gj5ynWXBB/TXLvIOUY0QUatsmnI2tzqEupwNmHg54K3gScWa9++j+jbOl3EBVgwO
ivzAk6xAQrD8V2TcXfCkfbzBloYDbVka3Cs8CX0JK7LsnoWZG22MhZwFsgESh2bl
e1VF5nhJkUv3/dzD6lQ=
-----END PUBLIC KEY-----
10 changes: 10 additions & 0 deletions spec/fixtures/certs/ec512-wrong-private.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-----BEGIN EC PARAMETERS-----
BgUrgQQAIw==
-----END EC PARAMETERS-----
-----BEGIN EC PRIVATE KEY-----
MIHbAgEBBEG/KbA2oCbiCT6L3V8XSz2WKBy0XhGvIFbl/ZkXIXnkYt+1B7wViSVo
KCHuMFsi6xU/5nE1EuDG2UsQJmKeAMkIOKAHBgUrgQQAI6GBiQOBhgAEAG0TFWe5
cZ5DZIyfuysrCoQySTNxd+aT8sPIxsx7mW6YBTsuO6rEgxyegd2Auy4xtikxpzKv
soMXR02999Aaus2jAAt/wxrhhr41BDP4MV0b6Zngb72hna0pcGqit5OyU8AbOJUZ
+rdyowRGsOY+aPbOyVhdNcsEdxYC8GdIyCQLBC1H
-----END EC PRIVATE KEY-----
6 changes: 6 additions & 0 deletions spec/fixtures/certs/ec512-wrong-public.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAbRMVZ7lxnkNkjJ+7KysKhDJJM3F3
5pPyw8jGzHuZbpgFOy47qsSDHJ6B3YC7LjG2KTGnMq+ygxdHTb330Bq6zaMAC3/D
GuGGvjUEM/gxXRvpmeBvvaGdrSlwaqK3k7JTwBs4lRn6t3KjBEaw5j5o9s7JWF01
ywR3FgLwZ0jIJAsELUc=
-----END PUBLIC KEY-----

0 comments on commit 983f26f

Please sign in to comment.