Delete password flow
This commit is contained in:
parent
e5932968fd
commit
2be932ceff
5 changed files with 30 additions and 158 deletions
|
|
@ -1,21 +0,0 @@
|
||||||
Feature: Access protected resources using password flow
|
|
||||||
Background:
|
|
||||||
Given a user with username "kent"
|
|
||||||
And all scopes exist
|
|
||||||
|
|
||||||
Scenario: Invalid credentials to token endpoint
|
|
||||||
When I register a new client
|
|
||||||
And I send a post request from that client to the password flow token endpoint using invalid credentials
|
|
||||||
Then I should receive an "invalid_grant" error
|
|
||||||
|
|
||||||
Scenario: Invalid bearer tokens sent
|
|
||||||
When I register a new client
|
|
||||||
And I send a post request from that client to the password flow token endpoint using "kent"'s credentials
|
|
||||||
And I use invalid bearer tokens to access user info
|
|
||||||
Then I should receive an "invalid_token" error
|
|
||||||
|
|
||||||
Scenario: Valid password flow
|
|
||||||
When I register a new client
|
|
||||||
And I send a post request from that client to the password flow token endpoint using "kent"'s credentials
|
|
||||||
And I use received valid bearer tokens to access user info
|
|
||||||
Then I should receive "kent"'s id, username, and email
|
|
||||||
30
features/step_definitions/oidc_common_steps.rb
Normal file
30
features/step_definitions/oidc_common_steps.rb
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
Given(/^all scopes exist$/) do
|
||||||
|
Api::OpenidConnect::Scope.find_or_create_by(name: "openid")
|
||||||
|
Api::OpenidConnect::Scope.find_or_create_by(name: "read")
|
||||||
|
end
|
||||||
|
|
||||||
|
When /^I register a new client$/ do
|
||||||
|
post api_openid_connect_clients_path, redirect_uris: ["http://localhost:3000"], client_name: "diaspora client"
|
||||||
|
end
|
||||||
|
|
||||||
|
When /^I use received valid bearer tokens to access user info$/ do
|
||||||
|
access_token_json = JSON.parse(last_response.body)
|
||||||
|
get api_v0_user_path, access_token: access_token_json["access_token"]
|
||||||
|
end
|
||||||
|
|
||||||
|
When /^I use invalid bearer tokens to access user info$/ do
|
||||||
|
get api_v0_user_path, access_token: SecureRandom.hex(32)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^I should receive "([^\"]*)"'s id, username, and email$/ do |username|
|
||||||
|
user_info_json = JSON.parse(last_response.body)
|
||||||
|
user = User.find_by_username(username)
|
||||||
|
expect(user_info_json["username"]).to have_content(user.username)
|
||||||
|
expect(user_info_json["language"]).to have_content(user.language)
|
||||||
|
expect(user_info_json["email"]).to have_content(user.email)
|
||||||
|
end
|
||||||
|
|
||||||
|
Then /^I should receive an "([^\"]*)" error$/ do |error_message|
|
||||||
|
user_info_json = JSON.parse(last_response.body)
|
||||||
|
expect(user_info_json["error"]).to have_content(error_message)
|
||||||
|
end
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
Given(/^all scopes exist$/) do
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "openid")
|
|
||||||
Api::OpenidConnect::Scope.find_or_create_by(name: "read")
|
|
||||||
end
|
|
||||||
|
|
||||||
When /^I register a new client$/ do
|
|
||||||
post api_openid_connect_clients_path, redirect_uris: ["http://localhost:3000"], client_name: "diaspora client"
|
|
||||||
end
|
|
||||||
|
|
||||||
Given /^I send a post request from that client to the password flow token endpoint using "([^\"]*)"'s credentials$/ do |username|
|
|
||||||
client_json = JSON.parse(last_response.body)
|
|
||||||
user = User.find_by(username: username)
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: user.username,
|
|
||||||
password: "password", # Password has been hard coded as all test accounts seem to have a password of "password"
|
|
||||||
client_id: client_json["o_auth_application"]["client_id"],
|
|
||||||
client_secret: client_json["o_auth_application"]["client_secret"],
|
|
||||||
scope: "read"
|
|
||||||
end
|
|
||||||
|
|
||||||
Given /^I send a post request from that client to the password flow token endpoint using invalid credentials$/ do
|
|
||||||
client_json = JSON.parse(last_response.body)
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob", password: "wrongpassword",
|
|
||||||
client_id: client_json["o_auth_application"]["client_id"],
|
|
||||||
client_secret: client_json["o_auth_application"]["client_secret"],
|
|
||||||
scope: "read"
|
|
||||||
end
|
|
||||||
|
|
||||||
When /^I use received valid bearer tokens to access user info$/ do
|
|
||||||
access_token_json = JSON.parse(last_response.body)
|
|
||||||
get api_v0_user_path, access_token: access_token_json["access_token"]
|
|
||||||
end
|
|
||||||
|
|
||||||
When /^I use invalid bearer tokens to access user info$/ do
|
|
||||||
get api_v0_user_path, access_token: SecureRandom.hex(32)
|
|
||||||
end
|
|
||||||
|
|
||||||
Then /^I should receive "([^\"]*)"'s id, username, and email$/ do |username|
|
|
||||||
user_info_json = JSON.parse(last_response.body)
|
|
||||||
user = User.find_by_username(username)
|
|
||||||
expect(user_info_json["username"]).to have_content(user.username)
|
|
||||||
expect(user_info_json["language"]).to have_content(user.language)
|
|
||||||
expect(user_info_json["email"]).to have_content(user.email)
|
|
||||||
end
|
|
||||||
|
|
||||||
Then /^I should receive an "([^\"]*)" error$/ do |error_message|
|
|
||||||
user_info_json = JSON.parse(last_response.body)
|
|
||||||
expect(user_info_json["error"]).to have_content(error_message)
|
|
||||||
end
|
|
||||||
|
|
@ -17,8 +17,6 @@ module Api
|
||||||
|
|
||||||
def handle_flows(o_auth_app, req, res)
|
def handle_flows(o_auth_app, req, res)
|
||||||
case req.grant_type
|
case req.grant_type
|
||||||
when :password
|
|
||||||
handle_password_flow(o_auth_app, req, res)
|
|
||||||
when :refresh_token
|
when :refresh_token
|
||||||
handle_refresh_flow(req, res)
|
handle_refresh_flow(req, res)
|
||||||
when :authorization_code
|
when :authorization_code
|
||||||
|
|
@ -34,20 +32,6 @@ module Api
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle_password_flow(o_auth_app, req, res)
|
|
||||||
user = User.find_for_database_authentication(username: req.username)
|
|
||||||
if user
|
|
||||||
if user.valid_password?(req.password)
|
|
||||||
auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: o_auth_app, user: user)
|
|
||||||
build_auth_and_access_token(auth, req, res)
|
|
||||||
else
|
|
||||||
req.invalid_grant!
|
|
||||||
end
|
|
||||||
else
|
|
||||||
req.invalid_grant! # TODO: Change to user login: Perhaps redirect_to login_path?
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_auth_and_access_token(auth, req, res)
|
def build_auth_and_access_token(auth, req, res)
|
||||||
scope_list = req.scope.map { |scope_name|
|
scope_list = req.scope.map { |scope_name|
|
||||||
OpenidConnect::Scope.find_by(name: scope_name).tap do |scope|
|
OpenidConnect::Scope.find_by(name: scope_name).tap do |scope|
|
||||||
|
|
|
||||||
|
|
@ -75,79 +75,6 @@ describe Api::OpenidConnect::TokenEndpoint, type: :request do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "the password grant type" do
|
|
||||||
context "when the username field is missing" do
|
|
||||||
it "should return an invalid request error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", password: "bluepin7",
|
|
||||||
client_id: client.client_id, client_secret: client.client_secret, scope: "read"
|
|
||||||
expect(response.body).to include "'username' required"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the password field is missing" do
|
|
||||||
it "should return an invalid request error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob",
|
|
||||||
client_id: client.client_id, client_secret: client.client_secret, scope: "read"
|
|
||||||
expect(response.body).to include "'password' required"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the username does not match an existing user" do
|
|
||||||
it "should return an invalid request error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "randomnoexist",
|
|
||||||
password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
|
|
||||||
expect(response.body).to include "invalid_grant"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the password is invalid" do
|
|
||||||
it "should return an invalid request error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob",
|
|
||||||
password: "wrongpassword", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
|
|
||||||
expect(response.body).to include "invalid_grant"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the client_secret doesn't match" do
|
|
||||||
it "should return an invalid client error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob",
|
|
||||||
password: "bluepin7", client_id: client.client_id, client_secret: "client.client_secret", scope: "read"
|
|
||||||
expect(response.body).to include "invalid_client"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the request is valid" do
|
|
||||||
it "should return an access token" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob",
|
|
||||||
password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
|
|
||||||
json = JSON.parse(response.body)
|
|
||||||
expect(json.keys).to include "expires_in"
|
|
||||||
expect(json["access_token"].length).to eq(64)
|
|
||||||
expect(json["token_type"]).to eq("bearer")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when there are duplicate fields" do
|
|
||||||
it "should return an invalid request error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob", password: "bluepin7",
|
|
||||||
username: "bob", password: "bluepin6", client_id: client.client_id, client_secret: client.client_secret,
|
|
||||||
scope: "read"
|
|
||||||
expect(response.body).to include "invalid_grant"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context "when the client is unregistered" do
|
|
||||||
it "should return an error" do
|
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "password", username: "bob",
|
|
||||||
password: "bluepin7", client_id: SecureRandom.hex(16).to_s, client_secret: client.client_secret,
|
|
||||||
scope: "read"
|
|
||||||
expect(response.body).to include "invalid_client"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
# TODO: Support a way to prevent brute force attacks using rate-limitation
|
|
||||||
# as specified by RFC 6749 4.3.2 Access Token Request
|
|
||||||
end
|
|
||||||
|
|
||||||
describe "an unsupported grant type" do
|
describe "an unsupported grant type" do
|
||||||
it "should return an unsupported grant type error" do
|
it "should return an unsupported grant type error" do
|
||||||
post api_openid_connect_access_tokens_path, grant_type: "noexistgrant", username: "bob",
|
post api_openid_connect_access_tokens_path, grant_type: "noexistgrant", username: "bob",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue