From b10b46d2339ae7449ea1ac06e7dcdf69c6ed37c9 Mon Sep 17 00:00:00 2001 From: Warren Parad Date: Wed, 1 May 2024 23:36:14 +0200 Subject: [PATCH] Enable service client keys to be validated. --- Gemfile.lock | 2 ++ authress-sdk.gemspec | 1 + .../service_client_token_provider.rb | 17 +++++++++----- spec/service_client_token_provider_spec.rb | 16 +++++--------- spec/token_validator_spec.rb | 22 +++++++++---------- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index ca9cce2..faed790 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,6 +6,7 @@ PATH jwt (>= 2.8) oauth2 omniauth-oauth2 + openssl rbnacl typhoeus (>= 1.4) @@ -44,6 +45,7 @@ GEM omniauth-oauth2 (1.8.0) oauth2 (>= 1.4, < 3) omniauth (~> 2.0) + openssl (3.2.0) parallel (1.22.1) parser (3.2.1.1) ast (~> 2.4.1) diff --git a/authress-sdk.gemspec b/authress-sdk.gemspec index 47448b8..9d3f4b5 100644 --- a/authress-sdk.gemspec +++ b/authress-sdk.gemspec @@ -47,6 +47,7 @@ Gem::Specification.new do |s| s.add_runtime_dependency 'jwt', '>= 2.8' s.add_runtime_dependency 'oauth2' s.add_runtime_dependency 'rbnacl' + s.add_runtime_dependency 'openssl' s.add_development_dependency 'rspec' diff --git a/lib/authress-sdk/service_client_token_provider.rb b/lib/authress-sdk/service_client_token_provider.rb index 923ff13..5970066 100644 --- a/lib/authress-sdk/service_client_token_provider.rb +++ b/lib/authress-sdk/service_client_token_provider.rb @@ -62,14 +62,19 @@ def get_token() raise Exception("Invalid Service Client Access Key") end - return decodedAccessKey.privateKey + priv_pem = <<~EOF + -----BEGIN PRIVATE KEY----- + #{decodedAccessKey.privateKey} + -----END PRIVATE KEY----- + EOF - # The Ed25519 module is broken right now and doesn't accept valid private keys. - # private_key = RbNaCl::Signatures::Ed25519::SigningKey.new(Base64.decode64(decodedAccessKey.privateKey)[0, 32]) + privateKey = OpenSSL::PKey.read(priv_pem) + result = Base64.encode64(privateKey.raw_private_key).tr('+/', '-_').delete('=') + private_key = RbNaCl::Signatures::Ed25519::SigningKey.new(Base64.decode64(result)) - # token = JWT.encode(jwt, private_key, 'ED25519', { typ: 'at+jwt', alg: 'EdDSA', kid: decodedAccessKey.keyId }) - # @cachedKeyData = { token: token, expires: jwt['exp'] } - # return token + token = JWT.encode(jwt, private_key, 'ED25519', { typ: 'at+jwt', alg: 'EdDSA', kid: decodedAccessKey.keyId }) + @cachedKeyData = { token: token, expires: jwt['exp'] } + return token end end end diff --git a/spec/service_client_token_provider_spec.rb b/spec/service_client_token_provider_spec.rb index 0e530d1..f10e8c6 100644 --- a/spec/service_client_token_provider_spec.rb +++ b/spec/service_client_token_provider_spec.rb @@ -1,7 +1,3 @@ -=begin - -=end - require 'spec_helper' customDomain = 'authress.token-validation.test' @@ -15,14 +11,14 @@ tokenProvider = AuthressSdk::ServiceClientTokenProvider.new(access_key, customDomain) result = tokenProvider.get_token() - # user_identity = JSON.parse(Base64.decode64(result.split(".")[1].tr('-_','+/'))) + user_identity = JSON.parse(Base64.decode64(result.split(".")[1].tr('-_','+/'))) - # expect(user_identity["client_id"]).to eq("CLIENT"); - # expect(user_identity["sub"]).to eq("CLIENT"); - # expect(user_identity["iss"]).to eq("https://authress.token-validation.test/v1/clients/CLIENT"); + expect(user_identity["client_id"]).to eq("CLIENT"); + expect(user_identity["sub"]).to eq("CLIENT"); + expect(user_identity["iss"]).to eq("https://authress.token-validation.test/v1/clients/CLIENT"); - # headers = JSON.parse(Base64.decode64(result.split(".")[0].tr('-_','+/'))) - # expect(headers).to eq({"alg"=>"EdDSA", "kid"=>"KEY", "typ"=>"at+jwt"}) + headers = JSON.parse(Base64.decode64(result.split(".")[0].tr('-_','+/'))) + expect(headers).to eq({"alg"=>"EdDSA", "kid"=>"KEY", "typ"=>"at+jwt"}) end end end diff --git a/spec/token_validator_spec.rb b/spec/token_validator_spec.rb index 4a49ea3..76478f1 100644 --- a/spec/token_validator_spec.rb +++ b/spec/token_validator_spec.rb @@ -5,21 +5,21 @@ describe AuthressSdk::TokenVerifier do describe "verify_token()" do - # it "Verifies a service client access key used token" do - # access_key = "CLIENT.KEY.ACCOUNT.MC4CAQAwBQYDK2VwBCIEIDVjjrIVCH3dVRq4ixRzBwjVHSoB2QzZ2iJuHq1Wshwp" - # publicKey = { "alg": "EdDSA", "kty": "OKP", "crv": "Ed25519", "x": "JxtSC5tZZJuaW7Aeu5Kh_3tgCpPZRkHaaFyTj5sQ3KU" } + it "Verifies a service client access key used token" do + access_key = "CLIENT.KEY.ACCOUNT.MC4CAQAwBQYDK2VwBCIEIDVjjrIVCH3dVRq4ixRzBwjVHSoB2QzZ2iJuHq1Wshwp" + publicKey = { "alg": "EdDSA", "kty": "OKP", "crv": "Ed25519", "x": "JxtSC5tZZJuaW7Aeu5Kh_3tgCpPZRkHaaFyTj5sQ3KU" } - # token_verifier_instance = AuthressSdk::TokenVerifier.new() + token_verifier_instance = AuthressSdk::TokenVerifier.new() - # allow(token_verifier_instance).to receive(:get_key_uncached) { jwks = JWT::JWK.new(publicKey) } + allow(token_verifier_instance).to receive(:get_key_uncached) { jwks = JWT::JWK.new(publicKey) } - # identity = token_verifier_instance.verify_token("https://#{customDomain}", access_key) + identity = token_verifier_instance.verify_token("https://#{customDomain}", access_key) - # expect(token_verifier_instance).to have_received(:get_key_uncached).with("https://#{customDomain}/v1/clients/CLIENT/.well-known/openid-configuration/jwks", "KEY") - # expect(identity["iss"]).to eq("https://#{customDomain}/v1/clients/CLIENT") - # expect(identity["sub"]).to eq("CLIENT") - # expect(identity["client_id"]).to eq("CLIENT") - # end + expect(token_verifier_instance).to have_received(:get_key_uncached).with("https://#{customDomain}/v1/clients/CLIENT/.well-known/openid-configuration/jwks", "KEY") + expect(identity["iss"]).to eq("https://#{customDomain}/v1/clients/CLIENT") + expect(identity["sub"]).to eq("CLIENT") + expect(identity["client_id"]).to eq("CLIENT") + end it "Verifies a valid token" do access_key = "eyJhbGciOiJFZERTQSIsImtpZCI6IktFWSIsInR5cCI6ImF0K2p3dCJ9.eyJhdWQiOiJBQ0NPVU5ULmFjY291bnRzLmF1dGhyZXNzLmlvIiwiaXNzIjoiaHR0cHM6Ly9hdXRocmVzcy50b2tlbi12YWxpZGF0aW9uLnRlc3QvdjEvY2xpZW50cy9DTElFTlQiLCJzdWIiOiJDTElFTlQiLCJjbGllbnRfaWQiOiJDTElFTlQiLCJpYXQiOjE3MTQ1ODA4NDQsImV4cCI6MTcxNDY2NzI0NCwic2NvcGUiOiJvcGVuaWQifQ.Rm8VvEO9dKn9RTEVkF_qH7NernVKnKwYu9GAnxUBjiweXubWchIAW8HymD-RAdXjzPYU9Pvq5p0f_1Pi4n2bBw"