Skip to content

Commit

Permalink
FI-2910: Host JWKS for Client Assertion (#515)
Browse files Browse the repository at this point in the history
* Added support for auth_info to fhir_client

Signed-off-by: Vanessa Fotso <[email protected]>

* Added unit test for auth info

Signed-off-by: Vanessa Fotso <[email protected]>

* WIP:perform refresh using auth_info

Signed-off-by: Vanessa Fotso <[email protected]>

* support for backend services authentication using auth_info

Signed-off-by: Vanessa Fotso <[email protected]>

* WIP auth info unit test

Signed-off-by: Vanessa Fotso <[email protected]>

* Updated auth_info_spec

Signed-off-by: Vanessa Fotso <[email protected]>

* Extracted the auth info sample data to its own fixture file"

Signed-off-by: Vanessa Fotso <[email protected]>

* Updated fhir_client_spec

Signed-off-by: Vanessa Fotso <[email protected]>

* Freeze constants

Signed-off-by: Vanessa Fotso <[email protected]>

* Updated ffhir client spec

Signed-off-by: Vanessa Fotso <[email protected]>

* Update based on PR comments

Signed-off-by: Vanessa Fotso <[email protected]>

* Update to return  for  and

Signed-off-by: Vanessa Fotso <[email protected]>

* Host jwks set for client assertion

Signed-off-by: Vanessa Fotso <[email protected]>

* Update ENV name for the inferno core jwks path

Co-authored-by: Stephen MacVicar <[email protected]>

* Added doc to JWKS class

Signed-off-by: Vanessa Fotso <[email protected]>

* proofread JWKs doc

Co-authored-by: Stephen MacVicar <[email protected]>

* extract reading content of jwks file into its own method

Signed-off-by: Vanessa Fotso <[email protected]>

* Host jwks set for client assertion

Signed-off-by: Vanessa Fotso <[email protected]>

* WIP auth info unit test

Signed-off-by: Vanessa Fotso <[email protected]>

* Updated auth_info_spec

Signed-off-by: Vanessa Fotso <[email protected]>

* Extracted the auth info sample data to its own fixture file"

Signed-off-by: Vanessa Fotso <[email protected]>

* Freeze constants

Signed-off-by: Vanessa Fotso <[email protected]>

* Update based on PR comments

Signed-off-by: Vanessa Fotso <[email protected]>

* Host jwks set for client assertion

Signed-off-by: Vanessa Fotso <[email protected]>

* Added jwks url to app property

Signed-off-by: Vanessa Fotso <[email protected]>

* use short class name

Signed-off-by: Vanessa Fotso <[email protected]>

---------

Signed-off-by: Vanessa Fotso <[email protected]>
Co-authored-by: Stephen MacVicar <[email protected]>
  • Loading branch information
vanessuniq and Jammjammjamm committed Jul 30, 2024
1 parent 3e96653 commit 55dafb6
Show file tree
Hide file tree
Showing 5 changed files with 25 additions and 1 deletion.
3 changes: 3 additions & 0 deletions lib/inferno/apps/web/router.rb
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@ module Web
# Should not need Content-Type header but GitHub Codespaces will not work without them.
# This could be investigated and likely removed if addressed properly elsewhere.
get '/', to: ->(_env) { [200, { 'Content-Type' => 'text/html' }, [client_page]] }
get '/jwks.json', to: lambda { |_env|
[200, { 'Content-Type' => 'application/json' }, [Inferno::JWKS.jwks_json]]
}, as: :jwks

Inferno.routes.each do |route|
cleaned_id = route[:suite].id.gsub(/[^a-zA-Z\d\-._~]/, '_')
Expand Down
2 changes: 2 additions & 0 deletions lib/inferno/config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class Application < Dry::System::Container
raw_js_host = ENV.fetch('JS_HOST', '')
base_path = ENV.fetch('BASE_PATH', '')
public_path = base_path.blank? ? '/public' : "/#{base_path}/public"
jwks_path = base_path.blank? ? '/jwks.json' : "/#{base_path}/jwks.json"
js_host = raw_js_host.present? ? "#{raw_js_host}/public" : public_path

Application.register('js_host', js_host)
Expand All @@ -21,6 +22,7 @@ class Application < Dry::System::Container
Application.register('async_jobs', ENV['ASYNC_JOBS'] != 'false')
Application.register('inferno_host', ENV.fetch('INFERNO_HOST', 'http://localhost:4567'))
Application.register('base_url', URI.join(Application['inferno_host'], base_path).to_s)
Application.register('jwks_url', URI.join(Application['inferno_host'], jwks_path).to_s)
Application.register('cache_bust_token', SecureRandom.uuid)

configure do |config|
Expand Down
3 changes: 2 additions & 1 deletion lib/inferno/dsl/auth_info.rb
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,8 @@ def auth_jwt_header
{
'alg' => encryption_algorithm,
'kid' => private_key['kid'],
'typ' => 'JWT'
'typ' => 'JWT',
'jku' => Inferno::Application['jwks_url']
}
end

Expand Down
3 changes: 3 additions & 0 deletions spec/inferno/dsl/auth_info_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
let(:asymmetric_auth_info) { described_class.new(asymmetric_confidential_access_default) }
let(:backend_services_auth_info) { described_class.new(backend_services_access_default) }
let(:client) { FHIR::Client.new('http://example.com') }
let(:jwks_url) { Inferno::Application['jwks_url'] }

describe '.new' do
it 'raises an error if an invalid key is provided' do
Expand Down Expand Up @@ -229,6 +230,7 @@

expect(header['alg']).to eq(asymmetric_auth_info.encryption_algorithm)
expect(header['typ']).to eq('JWT')
expect(header['jku']).to eq(jwks_url)
expect(header['kid']).to eq(asymmetric_auth_info.kid)
expect(claims['iss']).to eq(asymmetric_auth_info.client_id)
expect(claims['aud']).to eq(asymmetric_auth_info.token_url)
Expand All @@ -246,6 +248,7 @@

expect(header['alg']).to eq(asymmetric_auth_info.encryption_algorithm)
expect(header['typ']).to eq('JWT')
expect(header['jku']).to eq(jwks_url)
expect(header['kid']).to be_present
expect(claims['iss']).to eq(asymmetric_auth_info.client_id)
expect(claims['aud']).to eq(asymmetric_auth_info.token_url)
Expand Down
15 changes: 15 additions & 0 deletions spec/requests/jwks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require 'request_helper'

RSpec.describe '/jwks.json' do
let(:router) { Inferno::Web::Router }
let(:parsed_response) { JSON.parse(Inferno::JWKS.jwks_json) }

describe '/jwks.json' do
it 'renders the Inferno Core JWKS as json' do
get router.path(:jwks)

expect(last_response.status).to eq(200)
expect(parsed_body).to eq(parsed_response)
end
end
end

0 comments on commit 55dafb6

Please sign in to comment.