diff --git a/Gemfile b/Gemfile index b53c139bd..ebfc313f9 100644 --- a/Gemfile +++ b/Gemfile @@ -32,6 +32,7 @@ gem 'discard' gem 'dotenv-rails', require: 'dotenv/rails-now' # dotenv should always be loaded before rails gem 'dry-monads' gem 'elastic-apm' +gem 'faraday-jwt' gem 'flipper' gem 'flipper-active_record' gem 'flipper-ui' diff --git a/Gemfile.lock b/Gemfile.lock index 13cd7e0b2..8b03303a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,6 +116,7 @@ GEM axlsx_styler (1.1.0) activesupport (>= 3.1) caxlsx (>= 2.0.2) + base64 (0.2.0) bcrypt (3.1.19) benchmark-ips (2.12.0) better_html (1.0.16) @@ -126,7 +127,7 @@ GEM html_tokenizer (~> 0.0.6) parser (>= 2.4) smart_properties - bindata (2.4.10) + bindata (2.4.15) bindex (0.8.1) bootsnap (1.9.3) msgpack (~> 1.0) @@ -174,7 +175,7 @@ GEM css_parser (1.9.0) addressable daemons (1.3.1) - date (3.3.3) + date (3.3.4) deep_cloneable (3.2.0) activerecord (>= 3.1.0, < 8) delayed_cron_job (0.7.4) @@ -237,6 +238,16 @@ GEM excon (0.102.0) factory_bot (6.1.0) activesupport (>= 5.0.0) + faraday (2.7.12) + base64 + faraday-net_http (>= 2.0, < 3.1) + ruby2_keywords (>= 0.0.4) + faraday-follow_redirects (0.3.0) + faraday (>= 1, < 3) + faraday-jwt (0.1.0) + faraday (~> 2.0) + json-jwt (~> 1.16) + faraday-net_http (3.0.2) ffi (1.16.3) ffi-compiler (1.0.1) ffi (>= 1.0.0) @@ -325,7 +336,6 @@ GEM domain_name (~> 0.5) http-form_data (2.3.0) http_accept_language (2.1.1) - httpclient (2.8.3) i18n (1.14.1) concurrent-ruby (~> 1.0) i18n-tasks (1.0.9) @@ -354,10 +364,12 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (2.5.1) - json-jwt (1.13.0) + json-jwt (1.16.3) activesupport (>= 4.2) aes_key_wrap bindata + faraday (~> 2.0) + faraday-follow_redirects json_schemer (0.2.17) ecma-re-validator (~> 0.3) hana (~> 1.3) @@ -433,14 +445,14 @@ GEM multi_json (1.15.0) mustermann (3.0.0) ruby2_keywords (~> 0.0.1) - net-imap (0.3.7) + net-imap (0.4.7) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.4.0) net-protocol netrc (0.11.0) nio4r (2.5.9) @@ -448,16 +460,19 @@ GEM mini_portile2 (~> 2.8.2) racc (~> 1.4) open4 (1.3.4) - openid_connect (1.3.0) + openid_connect (2.2.0) activemodel attr_required (>= 1.0.0) - json-jwt (>= 1.5.0) - rack-oauth2 (>= 1.6.1) - swd (>= 1.0.0) + faraday (~> 2.0) + faraday-follow_redirects + json-jwt (>= 1.16) + net-smtp + rack-oauth2 (~> 2.2) + swd (~> 2.0) tzinfo validate_email validate_url - webfinger (>= 1.0.1) + webfinger (~> 2.0) orm_adapter (0.5.0) parallel (1.23.0) parsby (1.1.1) @@ -491,7 +506,7 @@ GEM pry (>= 0.13, < 0.15) pry-rails (0.3.9) pry (>= 0.10.4) - public_suffix (5.0.3) + public_suffix (5.0.4) puma (6.3.1) nio4r (~> 2.0) pundit (2.2.0) @@ -503,10 +518,11 @@ GEM rack (>= 1.0, < 3) rack-mini-profiler (3.0.0) rack (>= 1.2.0) - rack-oauth2 (1.19.0) + rack-oauth2 (2.2.0) activesupport attr_required - httpclient + faraday (~> 2.0) + faraday-follow_redirects json-jwt (>= 1.11.0) rack (>= 2.1.0) rack-protection (3.0.5) @@ -723,10 +739,11 @@ GEM stackprof (0.2.21) strong_migrations (0.8.0) activerecord (>= 5.2) - swd (1.3.0) + swd (2.0.2) activesupport (>= 3) attr_required (>= 0.0.5) - httpclient (>= 2.4) + faraday (~> 2.0) + faraday-follow_redirects sysexits (1.2.0) temple (0.8.2) terminal-table (3.0.2) @@ -735,7 +752,7 @@ GEM thread_safe (0.3.6) tilt (2.0.11) timecop (0.9.4) - timeout (0.4.0) + timeout (0.4.1) ttfunk (1.7.0) turbo-rails (1.3.2) actionpack (>= 6.0.0) @@ -754,7 +771,7 @@ GEM validate_email (0.1.6) activemodel (>= 3.0) mail (>= 2.2.5) - validate_url (1.0.13) + validate_url (1.0.15) activemodel (>= 3.0.0) public_suffix vcr (6.1.0) @@ -780,9 +797,10 @@ GEM activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webfinger (1.2.0) + webfinger (2.1.2) activesupport - httpclient (>= 2.4) + faraday (~> 2.0) + faraday-follow_redirects webmock (3.11.2) addressable (>= 2.3.6) crack (>= 0.3.2) @@ -849,6 +867,7 @@ DEPENDENCIES dry-monads elastic-apm factory_bot + faraday-jwt flipper flipper-active_record flipper-ui diff --git a/app/services/agent_connect_service.rb b/app/services/agent_connect_service.rb index bb41170d0..20812df61 100644 --- a/app/services/agent_connect_service.rb +++ b/app/services/agent_connect_service.rb @@ -13,8 +13,8 @@ class AgentConnectService uri = client.authorization_uri( scope: [:openid, :email], - state: state, - nonce: nonce, + state:, + nonce:, acr_values: 'eidas1' ) diff --git a/config/env.example b/config/env.example index a3624b572..b449c23e9 100644 --- a/config/env.example +++ b/config/env.example @@ -56,7 +56,6 @@ FC_PARTICULIER_BASE_URL="" AGENT_CONNECT_ID="" AGENT_CONNECT_SECRET="" AGENT_CONNECT_BASE_URL="" -AGENT_CONNECT_JWKS="" AGENT_CONNECT_REDIRECT="" # External service: integration with HelpScout (optional) diff --git a/config/initializers/open_id_connect.rb b/config/initializers/open_id_connect.rb index 5266653b8..0a3627a39 100644 --- a/config/initializers/open_id_connect.rb +++ b/config/initializers/open_id_connect.rb @@ -1,61 +1,3 @@ -OpenIDConnect.debug! -OpenIDConnect.logger = Rails.logger -Rack::OAuth2.logger = Rails.logger -# Webfinger.logger = Rails.logger -SWD.logger = Rails.logger - -# the openid_connect gem does not support -# jwt format in the userinfo call. -# A PR is open to improve the situation -# https://github.com/nov/openid_connect/pull/54 -module OpenIDConnect - class AccessToken < Rack::OAuth2::AccessToken::Bearer - private - - def jwk_loader - JSON.parse(URI.parse(ENV['AGENT_CONNECT_JWKS']).read).deep_symbolize_keys - end - - def decode_jwt(requested_host, jwt) - agent_connect_host = URI.parse(ENV['AGENT_CONNECT_BASE_URL']).host - - if requested_host == agent_connect_host - # rubocop:disable Lint/UselessAssignment - JWT.decode(jwt, key = nil, verify = true, { algorithms: ['ES256'], jwks: jwk_loader })[0] - # rubocop:enable Lint/UselessAssignment - else - raise "unknwon host : #{requested_host}" - end - end - - def resource_request - res = yield - case res.status - when 200 - hash = case parse_type_and_subtype(res.content_type) - when 'application/jwt' - requested_host = URI.parse(client.userinfo_endpoint).host - decode_jwt(requested_host, res.body) - when 'application/json' - JSON.parse(res.body) - end - hash&.with_indifferent_access - when 400 - raise BadRequest.new('API Access Faild', res) - when 401 - raise Unauthorized.new('Access Token Invalid or Expired', res) - when 403 - raise Forbidden.new('Insufficient Scope', res) - else - raise HttpError.new(res.status, 'Unknown HttpError', res) - end - end - - # https://datatracker.ietf.org/doc/html/rfc2045#section-5.1 - # - type and subtype are the first member - # they are case insensitive - def parse_type_and_subtype(content_type) - content_type.split(';')[0].strip.downcase - end - end +OpenIDConnect.http_config do |config| + config.response :jwt end