CI / Test (pull_request) Successful in 41s
Implements the optional OAuth 2.0 Authorization Code grant with PKCE (RFC 7636) for remoteStorage, as described in the remoteStorage spec (section 10.1). - Advertise the authorization and token endpoints plus the supported code_challenge method (S256) in WebFinger, and bump the advertised spec version to draft-dejong-remotestorage-27 - Add RsAuthorizationCode: a short-lived (5 min), single-use authorization code stored only as a SHA256 digest - Add RsOauthManager services for issuing and exchanging codes, including S256 verification - Add a cross-origin POST /rs/oauth/token endpoint that exchanges a code (and code_verifier) for an access token, with CORS and no CSRF token - Consent dialog now carries response_type / code_challenge through the form and redirects with the code in the query string for the code flow - Remove the existing-token reuse shortcut so every authorization results in a distinct authorization (and token) Part of #254
104 lines
3.4 KiB
Ruby
104 lines
3.4 KiB
Ruby
require 'rails_helper'
|
|
|
|
RSpec.describe 'remoteStorage OAuth Dialog', type: :feature do
|
|
context "when signed in" do
|
|
let(:user) { create :user }
|
|
|
|
before do
|
|
login_as user, :scope => :user
|
|
end
|
|
|
|
context "with normal permissions" do
|
|
before do
|
|
visit new_rs_oauth_path(username: user.cn,
|
|
redirect_uri: "http://example.com",
|
|
client_id: "http://example.com",
|
|
scope: "documents,[photos], contacts:r")
|
|
end
|
|
|
|
it "shows the permissions in a list" do
|
|
within ".permissions" do
|
|
expect(page).to have_content("documents")
|
|
expect(page).to have_content("photos")
|
|
expect(page).to have_content("contacts")
|
|
end
|
|
|
|
within ".scope:first-of-type" do
|
|
expect(page).not_to have_content("read only")
|
|
end
|
|
|
|
within ".scope:last-of-type" do
|
|
expect(page).to have_content("read only")
|
|
end
|
|
end
|
|
end
|
|
|
|
context "root access" do
|
|
context "full" do
|
|
before do
|
|
visit new_rs_oauth_path(username: user.cn,
|
|
redirect_uri: "http://example.com",
|
|
client_id: "http://example.com",
|
|
scope: ":rw")
|
|
end
|
|
|
|
it "shows a special permission for all files and dirs" do
|
|
within ".scope" do
|
|
expect(page).to have_content("All files and directories")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with PKCE parameters" do
|
|
before do
|
|
visit new_rs_oauth_path(username: user.cn,
|
|
redirect_uri: "http://example.com",
|
|
client_id: "http://example.com",
|
|
scope: "documents:rw",
|
|
response_type: "code",
|
|
code_challenge: "challenge123",
|
|
code_challenge_method: "S256")
|
|
end
|
|
|
|
it "includes the PKCE parameters in the consent form" do
|
|
expect(page).to have_selector("input[name='response_type'][value='code']", visible: false)
|
|
expect(page).to have_selector("input[name='code_challenge'][value='challenge123']", visible: false)
|
|
expect(page).to have_selector("input[name='code_challenge_method'][value='S256']", visible: false)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "when signed out" do
|
|
let(:user) { create :user }
|
|
|
|
before do
|
|
allow_any_instance_of(User).to receive(:valid_ldap_authentication?)
|
|
.with(user.password).and_return(true)
|
|
end
|
|
|
|
it "prefills the username field in the signin form" do
|
|
visit new_rs_oauth_path(username: user.cn,
|
|
redirect_uri: "http://example.com",
|
|
client_id: "http://example.com",
|
|
scope: "documents,[photos], contacts:r")
|
|
|
|
expect(find("#user_cn").value).to eq(user.cn)
|
|
end
|
|
|
|
it "redirects to the OAuth dialog after sign-in" do
|
|
auth_url = new_rs_oauth_url(username: user.cn,
|
|
redirect_uri: "http://example.com",
|
|
client_id: "http://example.com",
|
|
scope: "documents,[photos], contacts:r")
|
|
visit auth_url
|
|
|
|
fill_in "User", with: user.cn
|
|
fill_in "Password", with: user.password
|
|
click_button "Log in"
|
|
|
|
expect(current_url).to eq(auth_url)
|
|
end
|
|
end
|
|
end
|