Skip to content

Commit 902b76d

Browse files
authored
Merge pull request #2181 from govuk-forms/add-jwks-endpoint-for-one-login
Add JWKS endpoint for one login
2 parents 1e44c2c + 07a9d12 commit 902b76d

6 files changed

Lines changed: 108 additions & 1 deletion

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class OneLoginJwksController < ApplicationController
2+
def show
3+
jwk = Rails.application.config.x.one_login.public_key_jwk
4+
render json: JWT::JWK::Set.new(jwk).export
5+
end
6+
end

config/application.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,7 @@ class Application < Rails::Application
8989

9090
I18n.available_locales = %i[en cy]
9191
I18n.default_locale = :en
92+
93+
JWT.configuration.jwk.kid_generator_type = :rfc7638_thumbprint
9294
end
9395
end

config/initializers/omniauth.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
private_key_pem = private_key_pem.gsub('\n', "\n")
88

99
private_key = OpenSSL::PKey::RSA.new(private_key_pem)
10+
11+
public_key_jwk = JWT::JWK.new(private_key.public_key, use: "sig")
12+
Rails.application.config.x.one_login.public_key_jwk = public_key_jwk
1013
end
1114

1215
Rails.application.config.middleware.use OmniAuth::Builder do
@@ -16,7 +19,7 @@
1619
idp_base_url: Settings.govuk_one_login.base_url,
1720
private_key: private_key,
1821
redirect_uri: "/auth/govuk_one_login/callback",
19-
private_key_kid: "", # TODO: we'll need to set this when we switch to using a JWKS endpoint
22+
private_key_kid: public_key_jwk&.kid,
2023
signing_algorithm: "ES256",
2124
scope: "openid email",
2225
ui_locales: "en cy",

config/routes.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
get "/submission" => "submission_status#status", as: :status
2020
get "/.well-known/security.txt" => redirect("https://vulnerability-reporting.service.security.gov.uk/.well-known/security.txt")
2121

22+
get "/govuk-one-login-jwks", to: "one_login_jwks#show", as: :one_login_jwks
23+
2224
form_id_constraints = { form_id: UrlPatterns::FORM_ID_REGEX }
2325
form_constraints = {
2426
**form_id_constraints,

spec/initializers/omniauth_spec.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require "rails_helper"
2+
3+
RSpec.describe "omniauth initializer" do
4+
let(:initializer_path) { Rails.root.join("config/initializers/omniauth.rb") }
5+
6+
let(:one_login_settings) do
7+
double(
8+
private_key: private_key_setting,
9+
client_id: "test-client-id",
10+
base_url: "https://oidc.integration.account.gov.uk",
11+
)
12+
end
13+
14+
before do
15+
allow(Settings).to receive(:govuk_one_login).and_return(one_login_settings)
16+
allow(Rails.application.config.middleware).to receive(:use)
17+
allow(OmniAuth::GovukOneLogin::IdpConfiguration).to receive(:new).and_return(instance_double(OmniAuth::GovukOneLogin::IdpConfiguration))
18+
end
19+
20+
around do |example|
21+
original_public_key_jwk = Rails.application.config.x.one_login.public_key_jwk
22+
original_idp_configuration = Rails.application.config.x.one_login.idp_configuration
23+
example.run
24+
ensure
25+
Rails.application.config.x.one_login.public_key_jwk = original_public_key_jwk
26+
Rails.application.config.x.one_login.idp_configuration = original_idp_configuration
27+
end
28+
29+
context "when the private key is present" do
30+
let(:rsa_private_key) { OpenSSL::PKey::RSA.generate(2048) }
31+
let(:private_key_setting) { Base64.encode64(rsa_private_key.to_pem) }
32+
33+
before { load initializer_path }
34+
35+
it "sets Rails.application.config.x.one_login.public_key_jwk" do
36+
expect(Rails.application.config.x.one_login.public_key_jwk).to be_a(JWT::JWK::RSA)
37+
end
38+
39+
it "sets the JWK use to 'sig'" do
40+
expect(Rails.application.config.x.one_login.public_key_jwk[:use]).to eq("sig")
41+
end
42+
43+
it "sets the JWK kid" do
44+
expect(Rails.application.config.x.one_login.public_key_jwk.kid).to be_a(String)
45+
end
46+
end
47+
48+
context "when the private key is absent" do
49+
let(:private_key_setting) { nil }
50+
51+
before do
52+
Rails.application.config.x.one_login.public_key_jwk = nil
53+
load initializer_path
54+
end
55+
56+
it "does not set Rails.application.config.x.one_login.public_key_jwk" do
57+
expect(Rails.application.config.x.one_login.public_key_jwk).to be_nil
58+
end
59+
end
60+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require "rails_helper"
2+
require "base64"
3+
4+
RSpec.describe OneLoginJwksController, type: :request do
5+
describe "GET #show" do
6+
before do
7+
public_key = OpenSSL::PKey::RSA.generate(2048).public_key
8+
public_key_jwk = JWT::JWK.new(public_key, use: "sig")
9+
10+
one_login_config = ActiveSupport::OrderedOptions.new
11+
one_login_config.public_key_jwk = public_key_jwk
12+
13+
allow(Rails.application.config.x).to receive(:one_login).and_return(one_login_config)
14+
end
15+
16+
it "returns the JWKS JSON" do
17+
get one_login_jwks_path
18+
19+
expect(response).to have_http_status(:ok)
20+
expect(response.content_type).to eq("application/json; charset=utf-8")
21+
expect(response.parsed_body).to match({
22+
"keys" => [
23+
{
24+
"e" => "AQAB",
25+
"kty" => "RSA",
26+
"use" => "sig",
27+
"kid" => a_kind_of(String),
28+
"n" => a_kind_of(String),
29+
},
30+
],
31+
})
32+
end
33+
end
34+
end

0 commit comments

Comments
 (0)