From 9c9880d8807ecc964f72ef91b2429363687f9c7e Mon Sep 17 00:00:00 2001 From: theworldbright Date: Sun, 4 Oct 2015 01:57:54 -0700 Subject: [PATCH] Move JWKs files to database --- .gitignore | 1 - .../authorizations_controller.rb | 15 +++++++------ .../token_endpoint_controller.rb | 6 ++---- .../api/openid_connect/o_auth_application.rb | 18 +++------------- ...150613202109_create_o_auth_applications.rb | 3 +-- db/schema.rb | 10 ++++----- ...t_assertion_with_nonexistent_client_id.txt | 1 + .../client_assertion_with_nonexistent_kid.txt | 1 + .../client_assertion_with_tampered_sig.txt | 1 + spec/fixtures/valid_client_assertion.txt | 1 + .../api/openid_connect/token_endpoint_spec.rb | 10 ++++----- spec/spec_helper.rb | 21 ++++++++++++++++++- 12 files changed, 49 insertions(+), 39 deletions(-) create mode 100644 spec/fixtures/client_assertion_with_nonexistent_client_id.txt create mode 100644 spec/fixtures/client_assertion_with_nonexistent_kid.txt create mode 100644 spec/fixtures/client_assertion_with_tampered_sig.txt create mode 100644 spec/fixtures/valid_client_assertion.txt diff --git a/.gitignore b/.gitignore index 3f1d9c02a..3fc25bd96 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,6 @@ config/database.yml .rvmrc_custom .rvmrc.local config/oidc_key.pem -config/jwks/ # Mailing list stuff config/email_offset diff --git a/app/controllers/api/openid_connect/authorizations_controller.rb b/app/controllers/api/openid_connect/authorizations_controller.rb index 3176b2981..e8369e372 100644 --- a/app/controllers/api/openid_connect/authorizations_controller.rb +++ b/app/controllers/api/openid_connect/authorizations_controller.rb @@ -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", diff --git a/app/controllers/api/openid_connect/token_endpoint_controller.rb b/app/controllers/api/openid_connect/token_endpoint_controller.rb index 86d3d2f79..cfa06638f 100644 --- a/app/controllers/api/openid_connect/token_endpoint_controller.rb +++ b/app/controllers/api/openid_connect/token_endpoint_controller.rb @@ -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? diff --git a/app/models/api/openid_connect/o_auth_application.rb b/app/models/api/openid_connect/o_auth_application.rb index a89fa0c05..0004a93ad 100644 --- a/app/models/api/openid_connect/o_auth_application.rb +++ b/app/models/api/openid_connect/o_auth_application.rb @@ -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 diff --git a/db/migrate/20150613202109_create_o_auth_applications.rb b/db/migrate/20150613202109_create_o_auth_applications.rb index 4b4db75f2..f0fd5ca67 100644 --- a/db/migrate/20150613202109_create_o_auth_applications.rb +++ b/db/migrate/20150613202109_create_o_auth_applications.rb @@ -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 diff --git a/db/schema.rb b/db/schema.rb index 17f436674..a7232a417 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -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 diff --git a/spec/fixtures/client_assertion_with_nonexistent_client_id.txt b/spec/fixtures/client_assertion_with_nonexistent_client_id.txt new file mode 100644 index 000000000..3bcabb078 --- /dev/null +++ b/spec/fixtures/client_assertion_with_nonexistent_client_id.txt @@ -0,0 +1 @@ +eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.ewogIGF1ZDogWwogICAgaHR0cHM6Ly9rZW50c2hpa2FtYS5jb20vYXBpL29wZW5pZF9jb25uZWN0L2FjY2Vzc190b2tlbnMKICBdLAogIGlzczogMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2QsCiAganRpOiAwbWNycmVZSCwKICBleHA6IDE0NDMxNzA4OTEuMzk3NDU2LAogIGlhdDogMTQ0MzE3MDI5MS4zOTc0NTYsCiAgc3ViOiAxNGQ2OTJjZDUzZDljMWE5ZjQ2ZmQ2OWUwZTU3NDQzZAp9Cg.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog diff --git a/spec/fixtures/client_assertion_with_nonexistent_kid.txt b/spec/fixtures/client_assertion_with_nonexistent_kid.txt new file mode 100644 index 000000000..3419d02c1 --- /dev/null +++ b/spec/fixtures/client_assertion_with_nonexistent_kid.txt @@ -0,0 +1 @@ +ewogIGFsZzogUlMyNTYsCiAga2lkOiBpbnZhbGlkX2tpZAp9Cg.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ. \ No newline at end of file diff --git a/spec/fixtures/client_assertion_with_tampered_sig.txt b/spec/fixtures/client_assertion_with_tampered_sig.txt new file mode 100644 index 000000000..ff225126e --- /dev/null +++ b/spec/fixtures/client_assertion_with_tampered_sig.txt @@ -0,0 +1 @@ +eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQoe \ No newline at end of file diff --git a/spec/fixtures/valid_client_assertion.txt b/spec/fixtures/valid_client_assertion.txt new file mode 100644 index 000000000..4a0ac9441 --- /dev/null +++ b/spec/fixtures/valid_client_assertion.txt @@ -0,0 +1 @@ +eyJhbGciOiJSUzI1NiIsImtpZCI6ImExIn0.eyJhdWQiOiBbImh0dHBzOi8va2VudHNoaWthbWEuY29tL2FwaS9vcGVuaWRfY29ubmVjdC9hY2Nlc3NfdG9rZW5zIl0sICJpc3MiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UiLCAianRpIjogIjBtY3JyZVlIIiwgImV4cCI6IDE0NDMxNzA4OTEuMzk3NDU2LCAiaWF0IjogMTQ0MzE3MDI5MS4zOTc0NTYsICJzdWIiOiAiMTRkNjkyY2Q1M2Q5YzFhOWY0NmZkNjllMGU1NzQ0M2UifQ.QJUR3SYFrEIlbfOKjO0NYInddklytbJ2LSWNpkQ1aNThgneDCVCjIYGCaL2C9Sw-GR8j7QSUsKOwBbjZMUmVPFTjsfB4wdgObbxVt1QAXwDjAXc5w1smOerRsoahZ4yKI1an6PTaFxMwnoXUQcBZTsOS6RgXOCPPPoxibxohxoehPLieM0l7LYcF5DQKg7fTxZYOpmtiP--nibJxomXdVQNLSnZuQwnyWtlp_gYmqrYMMN1LPSmNCgZMZZZIYttaaAIA96SylglqubowJRShtDO9rSvUz_sgeCo7qo5Bfb0B5n9_PtIlr1CZSVoHyYj2lVqQldx7fnGuqqQJCfDQog \ No newline at end of file diff --git a/spec/lib/api/openid_connect/token_endpoint_spec.rb b/spec/lib/api/openid_connect/token_endpoint_spec.rb index 98887932f..6d944626f 100644 --- a/spec/lib/api/openid_connect/token_endpoint_spec.rb +++ b/spec/lib/api/openid_connect/token_endpoint_spec.rb @@ -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 diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4fb30af00..b97fb6166 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -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