Move JWKs files to database

This commit is contained in:
theworldbright 2015-10-04 01:57:54 -07:00
parent 2c7d102019
commit 9c9880d880
12 changed files with 49 additions and 39 deletions

1
.gitignore vendored
View file

@ -21,7 +21,6 @@ config/database.yml
.rvmrc_custom
.rvmrc.local
config/oidc_key.pem
config/jwks/
# Mailing list stuff
config/email_offset

View file

@ -10,12 +10,7 @@ module Api
def new
auth = Api::OpenidConnect::Authorization.find_by_client_id_and_user(params[:client_id], current_user)
if auth
auth.o_auth_access_tokens.destroy_all
auth.id_tokens.destroy_all
auth.code_used = false
auth.save
end
reset_auth(auth)
if logged_in_before?(params[:max_age])
reauthenticate
elsif params[:prompt]
@ -43,6 +38,14 @@ module Api
private
def reset_auth(auth)
return unless auth
auth.o_auth_access_tokens.destroy_all
auth.id_tokens.destroy_all
auth.code_used = false
auth.save
end
def handle_prompt(prompt, auth)
if prompt.include? "select_account"
handle_prompt_params_error("account_selection_required",

View file

@ -6,7 +6,7 @@ module Api
if req["client_assertion_type"] == "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
handle_jwt_bearer(req)
end
self.status, self.response.headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
self.status, response.headers, self.response_body = Api::OpenidConnect::TokenEndpoint.new.call(request.env)
nil
end
@ -24,12 +24,10 @@ module Api
end
def fetch_public_key(o_auth_app, jwt)
jwks_file_path = File.join(Rails.root, "config", "jwks", o_auth_app.jwks_file)
public_key = fetch_public_key_from_json(File.read(jwks_file_path), jwt)
public_key = fetch_public_key_from_json(o_auth_app.jwks, jwt)
if public_key.empty? && o_auth_app.jwks_uri
uri = URI.parse(o_auth_app.jwks_uri)
response = Net::HTTP.get_response(uri)
File.write jwks_file_path, response.body
public_key = fetch_public_key_from_json(response.body, jwt)
end
raise Rack::OAuth2::Server::Authorize::BadRequest(:unauthorized_client) if public_key.empty?

View file

@ -10,7 +10,7 @@ module Api
validates :client_secret, presence: true
validates :client_name, uniqueness: {scope: :redirect_uris}
%i(redirect_uris response_types grant_types contacts).each do |serializable|
%i(redirect_uris response_types grant_types contacts jwks).each do |serializable|
serialize serializable, JSON
end
@ -82,27 +82,15 @@ module Api
elsif key == :jwks_uri
uri = URI.parse(value)
response = Net::HTTP.get_response(uri)
file_name = create_file_path(response.body)
attr[:jwks_file] = file_name + ".json"
attr[:jwks] = response.body
attr[:jwks_uri] = value
elsif key == :jwks
file_name = create_file_path(value.to_json)
attr[:jwks_file] = file_name + ".json"
attr[:jwks] = value.to_json
else
attr[key] = value
end
end
end
def create_file_path(content)
file_name = Base64.urlsafe_encode64(Digest::SHA256.base64digest(content))
directory_name = File.join(Rails.root, "config", "jwks")
Dir.mkdir(directory_name) unless File.exist?(directory_name)
jwk_file_path = File.join(Rails.root, "config", "jwks", file_name + ".json")
File.write jwk_file_path, content
File.chmod(0600, jwk_file_path)
file_name
end
end
end
end

View file

@ -17,9 +17,8 @@ class CreateOAuthApplications < ActiveRecord::Migration
t.string :tos_uri
t.string :sector_identifier_uri
t.string :token_endpoint_auth_method
t.text :jwks
t.string :jwks_uri
t.string :jwks_file
t.boolean :ppid, default: false
t.timestamps null: false

View file

@ -281,7 +281,7 @@ ActiveRecord::Schema.define(version: 20150828132451) do
t.string "redirect_uris", limit: 255
t.string "response_types", limit: 255
t.string "grant_types", limit: 255
t.string "application_type", limit: 255, default: "web"
t.string "application_type", limit: 255, default: "web"
t.string "contacts", limit: 255
t.string "logo_uri", limit: 255
t.string "client_uri", limit: 255
@ -289,11 +289,11 @@ ActiveRecord::Schema.define(version: 20150828132451) do
t.string "tos_uri", limit: 255
t.string "sector_identifier_uri", limit: 255
t.string "token_endpoint_auth_method", limit: 255
t.text "jwks", limit: 65535
t.string "jwks_uri", limit: 255
t.string "jwks_file", limit: 255
t.boolean "ppid", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "ppid", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "o_auth_applications", ["client_id"], name: "index_o_auth_applications_on_client_id", unique: true, length: {"client_id"=>191}, using: :btree

View file

@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.ewogIGF1ZDogWwogICAgaHR0cHM6Ly9rZW50c2hpa2FtYS5jb20vYXBpL29wZW5pZF9jb25uZWN0L2FjY2Vzc190b2tlbnMKICBdLAogIGlzczogMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2QsCiAganRpOiAwbWNycmVZSCwKICBleHA6IDE0NDMxNzA4OTEuMzk3NDU2LAogIGlhdDogMTQ0MzE3MDI5MS4zOTc0NTYsCiAgc3ViOiAxNGQ2OTJjZDUzZDljMWE5ZjQ2ZmQ2OWUwZTU3NDQzZAp9Cg.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog

View file

@ -0,0 +1 @@
ewogIGFsZzogUlMyNTYsCiAga2lkOiBpbnZhbGlkX2tpZAp9Cg.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.

View file

@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQoe

View file

@ -0,0 +1 @@
eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog

View file

@ -10,7 +10,7 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
let!(:client_with_specific_id) { FactoryGirl.create(:o_auth_application_with_ppid_with_specific_id) }
let!(:auth_with_specific_id) do
client_with_specific_id.client_id = "14d692cd53d9c1a9f46fd69e0e57443e"
client_with_specific_id.jwks_file = jwks_file_path
client_with_specific_id.jwks = File.read(jwks_file_path)
client_with_specific_id.save!
Api::OpenidConnect::Authorization.find_or_create_by(
o_auth_application: client_with_specific_id,
@ -67,7 +67,7 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog"
client_assertion: File.read(valid_client_assertion_path)
end
it "should return a valid id token" do
@ -125,7 +125,7 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQoe"
client_assertion: File.read(client_assertion_with_tampered_sig_path)
end
it "should return an error" do
@ -138,7 +138,7 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "ewogIGFsZzogUlMyNTYsCiAga2lkOiBpbnZhbGlkX2tpZAp9Cg.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ."
client_assertion: File.read(client_assertion_with_nonexistent_kid_path)
end
it "should return an error" do
@ -159,7 +159,7 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
post api_openid_connect_access_tokens_path, grant_type: "authorization_code",
redirect_uri: "http://localhost:3000/", code: code_with_specific_id,
client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
client_assertion: "eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.ewogIGF1ZDogWwogICAgaHR0cHM6Ly9rZW50c2hpa2FtYS5jb20vYXBpL29wZW5pZF9jb25uZWN0L2FjY2Vzc190b2tlbnMKICBdLAogIGlzczogMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2QsCiAganRpOiAwbWNycmVZSCwKICBleHA6IDE0NDMxNzA4OTEuMzk3NDU2LAogIGlhdDogMTQ0MzE3MDI5MS4zOTc0NTYsCiAgc3ViOiAxNGQ2OTJjZDUzZDljMWE5ZjQ2ZmQ2OWUwZTU3NDQzZAp9Cg.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog"
client_assertion: File.read(client_assertion_with_nonexistent_client_id_path)
end
it "should return an error" do

View file

@ -60,7 +60,26 @@ def photo_fixture_name
end
def jwks_file_path
@jwks_file = "../../spec/fixtures/jwks.json"
@jwks_file = File.join(File.dirname(__FILE__), "fixtures", "jwks.json")
end
def valid_client_assertion_path
@valid_client_assertion = File.join(File.dirname(__FILE__), "fixtures", "valid_client_assertion.txt")
end
def client_assertion_with_tampered_sig_path
@client_assertion_with_tampered_sig = File.join(File.dirname(__FILE__), "fixtures",
"client_assertion_with_tampered_sig.txt")
end
def client_assertion_with_nonexistent_kid_path
@client_assertion_with_nonexistent_kid = File.join(File.dirname(__FILE__), "fixtures",
"client_assertion_with_nonexistent_kid.txt")
end
def client_assertion_with_nonexistent_client_id_path
@client_assertion_with_nonexistent_client_id = File.join(File.dirname(__FILE__), "fixtures",
"client_assertion_with_nonexistent_client_id.txt")
end
# Force fixture rebuild