Merge pull request #8371 from opf/fix/set-csp-form-action-for-oauth

Fix/set csp form action for oauth

[ci skip]
pull/8377/head
Oliver Günther 5 years ago committed by GitHub
commit d15769c7c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      app/controllers/oauth/auth_base_controller.rb
  2. 6
      config/initializers/secure_headers.rb
  3. 17
      docs/system-admin-guide/authentication/oauth-applications/README.md
  4. BIN
      docs/system-admin-guide/authentication/oauth-applications/Sys-admin-authentication-oauth-postman.png
  5. 74
      spec/features/oauth/authorization_code_flow_spec.rb

@ -34,6 +34,17 @@ module OAuth
# See config/initializers/doorkeeper.rb # See config/initializers/doorkeeper.rb
class AuthBaseController < ::ApplicationController class AuthBaseController < ::ApplicationController
skip_before_action :check_if_login_required skip_before_action :check_if_login_required
after_action :extend_content_security_policy
layout 'only_logo' layout 'only_logo'
def extend_content_security_policy
use_content_security_policy_named_append(:oauth)
end
def allowed_forms
allowed_redirect_urls = pre_auth&.client&.application&.redirect_uri
urls = allowed_redirect_urls.to_s.split
urls.map { |url| URI.join(url, '/') }.map(&:to_s)
end
end end
end end

@ -73,3 +73,9 @@ SecureHeaders::Configuration.default do |config|
connect_src: connect_src connect_src: connect_src
} }
end end
SecureHeaders::Configuration.named_append(:oauth) do |request|
hosts = request.controller_instance.try(:allowed_forms) || []
{ form_action: hosts }
end

@ -27,3 +27,20 @@ You can configure the following options to add your oauth application.
5. Press the blue **Create** button to add your oauth application. 5. Press the blue **Create** button to add your oauth application.
![Sys-admin-authentication-add-oauth-application](Sys-admin-authentication-add-oauth-application.png) ![Sys-admin-authentication-add-oauth-application](Sys-admin-authentication-add-oauth-application.png)
## Oauth endpoints
The authentication endpoints are at
* Auth URL: `https://example.com/oauth/authorize`
* Access Token URL: `https://example.com/oauth/token`
## Using Postman with oauth?
Set redirect URLs to `urn:ietf:wg:oauth:2.0:oob` in both, for your application (see step 2 above) and
within Postman.
In Postman the configuration should look like this (Replace `{{protocolHostPort}}` with your host,
i.e. `https://example.com`)
![Sys-admin-authentication-add-oauth-application](Sys-admin-authentication-oauth-postman.png)

@ -28,21 +28,37 @@
require 'spec_helper' require 'spec_helper'
describe 'OAuth authorization code flow', type: :feature, js: true do describe 'OAuth authorization code flow',
type: :feature,
js: true do
let!(:user) { FactoryBot.create(:user) } let!(:user) { FactoryBot.create(:user) }
let!(:app) { FactoryBot.create(:oauth_application, name: 'Cool API app!') } let!(:redirect_uri) { 'urn:ietf:wg:oauth:2.0:oob' }
let!(:allowed_redirect_uri) { redirect_uri }
let!(:app) { FactoryBot.create(:oauth_application, name: 'Cool API app!', redirect_uri: allowed_redirect_uri) }
let(:client_secret) { app.plaintext_secret } let(:client_secret) { app.plaintext_secret }
def oauth_path(client_id) def oauth_path(client_id, redirect_url)
"/oauth/authorize?response_type=code&client_id=#{client_id}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=api_v3" "/oauth/authorize?response_type=code&client_id=#{client_id}&redirect_uri=#{CGI.escape(redirect_url)}&scope=api_v3"
end end
before do before do
# Do not login, will do that in the oauth flow @record_requests = Billy.config.record_requests
@whitelist = Billy.config.whitelist
Billy.configure do |c|
c.record_requests = true
c.whitelist = []
end
end
after do
Billy.configure do |c|
c.record_requests = @record_requests
c.whitelist = @whitelist
end
end end
it 'can authorize and manage an OAuth application grant' do it 'can authorize and manage an OAuth application grant' do
visit oauth_path app.uid visit oauth_path app.uid, redirect_uri
# Expect we're guided to the login screen # Expect we're guided to the login screen
login_with user.login, 'adminADMIN!', visit_signin_path: false login_with user.login, 'adminADMIN!', visit_signin_path: false
@ -54,6 +70,20 @@ describe 'OAuth authorization code flow', type: :feature, js: true do
expect(page).to have_selector('li strong', text: I18n.t('oauth.scopes.api_v3')) expect(page).to have_selector('li strong', text: I18n.t('oauth.scopes.api_v3'))
expect(page).to have_selector('li', text: I18n.t('oauth.scopes.api_v3_text')) expect(page).to have_selector('li', text: I18n.t('oauth.scopes.api_v3_text'))
first = true
allow_any_instance_of(::OAuth::AuthBaseController)
.to receive(:allowed_forms).and_wrap_original do |m|
forms = m.call
# Multiple requests end up here with one not containing the request url
if first
expect(forms).to include redirect_uri
first = false
end
forms
end
# Authorize # Authorize
find('input.button[value="Authorize"]').click find('input.button[value="Authorize"]').click
@ -108,7 +138,7 @@ describe 'OAuth authorization code flow', type: :feature, js: true do
end end
it 'does not authenticate unknown applications' do it 'does not authenticate unknown applications' do
visit oauth_path 'WAT' visit oauth_path 'WAT', redirect_uri
# Expect we're guided to the login screen # Expect we're guided to the login screen
login_with user.login, 'adminADMIN!', visit_signin_path: false login_with user.login, 'adminADMIN!', visit_signin_path: false
@ -120,4 +150,34 @@ describe 'OAuth authorization code flow', type: :feature, js: true do
user.oauth_grants.reload user.oauth_grants.reload
expect(user.oauth_grants.count).to eq 0 expect(user.oauth_grants.count).to eq 0
end end
# Selenium can't return response headers
context 'in browser that can log response headers', js: false do
before do
login_as user
end
context 'with real urls as allowed redirect uris' do
let!(:redirect_uri) { "https://foo.com/foo " }
let!(:allowed_redirect_uri) { "#{redirect_uri} https://bar.com/bar" }
it 'can authorize and manage an OAuth application grant' do
visit oauth_path app.uid, redirect_uri
allow_any_instance_of(::OAuth::AuthBaseController)
.to receive(:allowed_forms).and_wrap_original do |m|
forms = m.call
expect(forms).to include 'https://foo.com/'
expect(forms).to include 'https://bar.com/'
forms
end
# Check that the hosts of allowed redirection urls are present in the content security policy
expect(page.response_headers['content-security-policy']).to(
include("form-action 'self' https://foo.com/ https://bar.com/;")
)
end
end
end
end end

Loading…
Cancel
Save