diff --git a/app/controllers/api/v0/base_controller.rb b/app/controllers/api/v0/base_controller.rb index 85a3913d1..15b85151e 100644 --- a/app/controllers/api/v0/base_controller.rb +++ b/app/controllers/api/v0/base_controller.rb @@ -1,6 +1,7 @@ class Api::V0::BaseController < ApplicationController include OpenidConnect::ProtectedResourceEndpoint - before_filter :require_access_token - + def user + current_token ? current_token.authorization.user : nil + end end diff --git a/app/controllers/api/v0/users_controller.rb b/app/controllers/api/v0/users_controller.rb index 70e04f372..11f68ffe9 100644 --- a/app/controllers/api/v0/users_controller.rb +++ b/app/controllers/api/v0/users_controller.rb @@ -1,11 +1,9 @@ class Api::V0::UsersController < Api::V0::BaseController + before_filter do + require_access_token OpenidConnect::Scope.find_by(name: "read") + end + def show render json: user end - - private - - def user - current_token.authorization.user - end end diff --git a/app/controllers/openid_connect/authorizations_controller.rb b/app/controllers/openid_connect/authorizations_controller.rb index 3ef6896a2..d1e3604dd 100644 --- a/app/controllers/openid_connect/authorizations_controller.rb +++ b/app/controllers/openid_connect/authorizations_controller.rb @@ -47,7 +47,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController session[:client_id] = @o_auth_application.client_id session[:response_type] = @response_type session[:redirect_uri] = @redirect_uri - session[:scopes] = @scopes.map(&:name) + session[:scopes] = @scopes.map(&:name).join(" ") session[:request_object] = @request_object session[:nonce] = params[:nonce] end @@ -84,7 +84,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController req.update_param("response_type", session[:response_type].respond_to?(:map) ? session[:response_type].map(&:to_s).join(" ") : session[:response_type]) - req.update_param("scopes", session[:scopes]) + req.update_param("scope", session[:scopes]) req.update_param("request_object", session[:request_object]) req.update_param("nonce", session[:nonce]) end diff --git a/app/models/openid_connect/authorization.rb b/app/models/openid_connect/authorization.rb index 341dbd67f..2d9b0f951 100644 --- a/app/models/openid_connect/authorization.rb +++ b/app/models/openid_connect/authorization.rb @@ -6,6 +6,7 @@ class OpenidConnect::Authorization < ActiveRecord::Base validates :o_auth_application, presence: true validates :user, uniqueness: {scope: :o_auth_application} + has_many :authorization_scopes has_many :scopes, through: :authorization_scopes has_many :o_auth_access_tokens, dependent: :destroy has_many :id_tokens, dependent: :destroy @@ -16,6 +17,12 @@ class OpenidConnect::Authorization < ActiveRecord::Base self.refresh_token = SecureRandom.hex(32) end + def accessible?(required_scopes=nil) + Array(required_scopes).all? do |required_scope| + scopes.include? required_scope + end + end + def create_access_token o_auth_access_tokens.create!.bearer_token # TODO: Add support for request object diff --git a/app/models/openid_connect/o_auth_access_token.rb b/app/models/openid_connect/o_auth_access_token.rb index cc72dc3d9..6374e968e 100644 --- a/app/models/openid_connect/o_auth_access_token.rb +++ b/app/models/openid_connect/o_auth_access_token.rb @@ -1,6 +1,5 @@ class OpenidConnect::OAuthAccessToken < ActiveRecord::Base belongs_to :authorization - has_many :scopes, through: :scope_tokens before_validation :setup, on: :create @@ -20,8 +19,4 @@ class OpenidConnect::OAuthAccessToken < ActiveRecord::Base expires_in: (expires_at - Time.now.utc).to_i ) end - - def accessible?(_scopes_or_claims_=nil) - true # TODO: For now don't support scopes - end end diff --git a/app/models/openid_connect/o_auth_application.rb b/app/models/openid_connect/o_auth_application.rb index f94e4fae2..c4c1f0aa3 100644 --- a/app/models/openid_connect/o_auth_application.rb +++ b/app/models/openid_connect/o_auth_application.rb @@ -44,7 +44,7 @@ class OpenidConnect::OAuthApplication < ActiveRecord::Base def supported_metadata %i(client_name response_types grant_types application_type - contacts logo_uri client_uri policy_uri tos_uri) + contacts logo_uri client_uri policy_uri tos_uri redirect_uris) end def registrar_attributes(registrar) diff --git a/app/models/openid_connect/scope.rb b/app/models/openid_connect/scope.rb index e5449b648..eff4da110 100644 --- a/app/models/openid_connect/scope.rb +++ b/app/models/openid_connect/scope.rb @@ -1,8 +1,7 @@ class OpenidConnect::Scope < ActiveRecord::Base - has_many :o_auth_access_token, through: :scope_tokens has_many :authorizations, through: :authorization_scopes validates :name, presence: true, uniqueness: true - # TODO: Incomplete class + # TODO: Add constants so scopes can be referenced as OpenidConnect::Scope::Read end diff --git a/app/models/openid_connect/scopes_tokens.rb b/app/models/openid_connect/scopes_tokens.rb deleted file mode 100644 index 3c336ac73..000000000 --- a/app/models/openid_connect/scopes_tokens.rb +++ /dev/null @@ -1,7 +0,0 @@ -class OpenidConnect::ScopeToken < ActiveRecord::Base - belongs_to :scope - belongs_to :o_auth_access_token - - validates :scope, presence: true - validates :token, presence: true -end diff --git a/db/migrate/20150708155202_create_authorization_scopes_join_table.rb b/db/migrate/20150708155202_create_authorization_scopes_join_table.rb new file mode 100644 index 000000000..79c275b41 --- /dev/null +++ b/db/migrate/20150708155202_create_authorization_scopes_join_table.rb @@ -0,0 +1,8 @@ +class CreateAuthorizationScopesJoinTable < ActiveRecord::Migration + def change + create_table :authorization_scopes, id: false do |t| + t.belongs_to :authorization, index: true + t.belongs_to :scope, index: true + end + end +end diff --git a/db/migrate/20150708155202_create_authorizations_scopes_join_table.rb b/db/migrate/20150708155202_create_authorizations_scopes_join_table.rb deleted file mode 100644 index c91e50d11..000000000 --- a/db/migrate/20150708155202_create_authorizations_scopes_join_table.rb +++ /dev/null @@ -1,8 +0,0 @@ -class CreateAuthorizationsScopesJoinTable < ActiveRecord::Migration - def change - create_table :authorizations_scopes, id: false do |t| - t.belongs_to :authorization, index: true - t.belongs_to :scope, index: true - end - end -end diff --git a/db/migrate/20150708155747_create_scopes_tokens_join_table.rb b/db/migrate/20150708155747_create_scopes_tokens_join_table.rb deleted file mode 100644 index a52835048..000000000 --- a/db/migrate/20150708155747_create_scopes_tokens_join_table.rb +++ /dev/null @@ -1,8 +0,0 @@ -class CreateScopesTokensJoinTable < ActiveRecord::Migration - def change - create_table :scopes_tokens, id: false do |t| - t.belongs_to :scope, index: true - t.belongs_to :o_auth_access_token, index: true - end - end -end diff --git a/db/schema.rb b/db/schema.rb index e2845d469..dfaf0ed3b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -55,6 +55,14 @@ ActiveRecord::Schema.define(version: 20150724152052) do add_index "aspects", ["user_id", "contacts_visible"], name: "index_aspects_on_user_id_and_contacts_visible", using: :btree add_index "aspects", ["user_id"], name: "index_aspects_on_user_id", using: :btree + create_table "authorization_scopes", id: false, force: :cascade do |t| + t.integer "authorization_id", limit: 4 + t.integer "scope_id", limit: 4 + end + + add_index "authorization_scopes", ["authorization_id"], name: "index_authorization_scopes_on_authorization_id", using: :btree + add_index "authorization_scopes", ["scope_id"], name: "index_authorization_scopes_on_scope_id", using: :btree + create_table "authorizations", force: :cascade do |t| t.integer "user_id", limit: 4 t.integer "o_auth_application_id", limit: 4 @@ -66,14 +74,6 @@ ActiveRecord::Schema.define(version: 20150724152052) do add_index "authorizations", ["o_auth_application_id"], name: "index_authorizations_on_o_auth_application_id", using: :btree add_index "authorizations", ["user_id"], name: "index_authorizations_on_user_id", using: :btree - create_table "authorizations_scopes", id: false, force: :cascade do |t| - t.integer "authorization_id", limit: 4 - t.integer "scope_id", limit: 4 - end - - add_index "authorizations_scopes", ["authorization_id"], name: "index_authorizations_scopes_on_authorization_id", using: :btree - add_index "authorizations_scopes", ["scope_id"], name: "index_authorizations_scopes_on_scope_id", using: :btree - create_table "blocks", force: :cascade do |t| t.integer "user_id", limit: 4 t.integer "person_id", limit: 4 @@ -523,14 +523,6 @@ ActiveRecord::Schema.define(version: 20150724152052) do t.datetime "updated_at", null: false end - create_table "scopes_tokens", id: false, force: :cascade do |t| - t.integer "scope_id", limit: 4 - t.integer "o_auth_access_token_id", limit: 4 - end - - add_index "scopes_tokens", ["o_auth_access_token_id"], name: "index_scopes_tokens_on_o_auth_access_token_id", using: :btree - add_index "scopes_tokens", ["scope_id"], name: "index_scopes_tokens_on_scope_id", using: :btree - create_table "services", force: :cascade do |t| t.string "type", limit: 127, null: false t.integer "user_id", limit: 4, null: false diff --git a/features/desktop/oauth_password_flow.feature b/features/desktop/oauth_password_flow.feature index 7fa59c4fd..4bde1b08d 100644 --- a/features/desktop/oauth_password_flow.feature +++ b/features/desktop/oauth_password_flow.feature @@ -1,6 +1,7 @@ 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 diff --git a/features/desktop/oidc_implicit_flow.feature b/features/desktop/oidc_implicit_flow.feature index 92bf59d5a..480b0a928 100644 --- a/features/desktop/oidc_implicit_flow.feature +++ b/features/desktop/oidc_implicit_flow.feature @@ -2,7 +2,7 @@ Feature: Access protected resources using implicit flow Background: Given a user with username "kent" - And the OpenID scope exists + And all scopes exist Scenario: Invalid client id to auth endpoint When I register a new client diff --git a/features/step_definitions/implicit_flow_steps.rb b/features/step_definitions/implicit_flow_steps.rb index cc0f63e34..7c50f3159 100644 --- a/features/step_definitions/implicit_flow_steps.rb +++ b/features/step_definitions/implicit_flow_steps.rb @@ -1,24 +1,18 @@ o_auth_query_params = %i( redirect_uri=http://localhost:3000 - response_type=id_token token - scope=openid + response_type=id_token%20token + scope=openid%20read nonce=hello state=hi ).join("&") -Given(/^the OpenID scope exists$/) do - OpenidConnect::Scope.create(name: "openid") -end - Given /^I send a post request from that client to the implicit flow authorization endpoint$/ do client_json = JSON.parse(last_response.body) - auth_endpoint_url = "/openid_connect/authorizations/new" - visit "#{auth_endpoint_url}?client_id=#{client_json["o_auth_application"]["client_id"]}&#{o_auth_query_params}" + visit new_openid_connect_authorization_path + "?client_id=#{client_json["o_auth_application"]["client_id"]}&#{o_auth_query_params}" end Given /^I send a post request from that client to the implicit flow authorization endpoint using a invalid client id/ do - auth_endpoint_url = "/openid_connect/authorizations/new" - visit "#{auth_endpoint_url}?client_id=randomid&#{o_auth_query_params}" + visit new_openid_connect_authorization_path + "?client_id=randomid&#{o_auth_query_params}" end When /^I give my consent and authorize the client$/ do @@ -38,8 +32,7 @@ end When /^I parse the bearer tokens and use it to access user info$/ do access_token = current_url[/(?<=access_token=)[^&]+/] - user_info_endpoint_url = "/api/v0/user/" - get user_info_endpoint_url, access_token: access_token + get api_v0_user_path, access_token: access_token end Then /^I should see an "([^\"]*)" error$/ do |error_message| diff --git a/features/step_definitions/password_flow_steps.rb b/features/step_definitions/password_flow_steps.rb index 66c35f2e1..778399a60 100644 --- a/features/step_definitions/password_flow_steps.rb +++ b/features/step_definitions/password_flow_steps.rb @@ -1,35 +1,37 @@ +Given(/^all scopes exist$/) do + OpenidConnect::Scope.find_or_create_by(name: "openid") + OpenidConnect::Scope.find_or_create_by(name: "read") +end + When /^I register a new client$/ do - client_registration_url = "/openid_connect/clients" - post client_registration_url, redirect_uris: ["http://localhost:3000"], client_name: "diaspora client" + post 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) - token_endpoint_url = "/openid_connect/access_tokens" - post token_endpoint_url, grant_type: "password", username: user.username, + post 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"] + 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) - token_endpoint_url = "/openid_connect/access_tokens" - post token_endpoint_url, grant_type: "password", username: "bob", password: "wrongpassword", + post 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"] + 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) - user_info_endpoint_url = "/api/v0/user/" - get user_info_endpoint_url, access_token: access_token_json["access_token"] + get api_v0_user_path, access_token: access_token_json["access_token"] end When /^I use invalid bearer tokens to access user info$/ do - user_info_endpoint_url = "/api/v0/user/" - get user_info_endpoint_url, access_token: SecureRandom.hex(32) + get api_v0_user_path, access_token: SecureRandom.hex(32) end Then /^I should receive "([^\"]*)"'s id, username, and email$/ do |username| diff --git a/lib/openid_connect/authorization_point/endpoint.rb b/lib/openid_connect/authorization_point/endpoint.rb index 839bed9c7..a65974273 100644 --- a/lib/openid_connect/authorization_point/endpoint.rb +++ b/lib/openid_connect/authorization_point/endpoint.rb @@ -45,8 +45,8 @@ module OpenidConnect end def build_scopes(req) - @scopes = req.scope.map {|scope| - OpenidConnect::Scope.where(name: scope).first.tap do |scope| + @scopes = req.scope.map {|scope_name| + OpenidConnect::Scope.where(name: scope_name).first.tap do |scope| req.invalid_scope! "Unknown scope: #{scope}" unless scope end } diff --git a/lib/openid_connect/authorization_point/endpoint_confirmation_point.rb b/lib/openid_connect/authorization_point/endpoint_confirmation_point.rb index cdb4d9474..7a6b83183 100644 --- a/lib/openid_connect/authorization_point/endpoint_confirmation_point.rb +++ b/lib/openid_connect/authorization_point/endpoint_confirmation_point.rb @@ -21,6 +21,7 @@ module OpenidConnect # TODO: Add support for request object and auth code def approved!(req, res) auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: @o_auth_application, user: @user) + auth.scopes << @scopes response_types = Array(req.response_type) if response_types.include?(:token) res.access_token = auth.create_access_token diff --git a/lib/openid_connect/protected_resource_endpoint.rb b/lib/openid_connect/protected_resource_endpoint.rb index 8d5a37165..abc606981 100644 --- a/lib/openid_connect/protected_resource_endpoint.rb +++ b/lib/openid_connect/protected_resource_endpoint.rb @@ -2,20 +2,14 @@ module OpenidConnect module ProtectedResourceEndpoint attr_reader :current_token - def require_access_token + def require_access_token(*required_scopes) @current_token = request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN] - unless @current_token + unless @current_token && @current_token.authorization raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new("Unauthorized user") end - # TODO: This block is useless until we actually start checking for scopes - unless @current_token.try(:accessible?, required_scopes) + unless @current_token.authorization.try(:accessible?, required_scopes) raise Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope) end end - - # TODO: Scopes should be implemented here - def required_scopes - nil - end end end diff --git a/lib/openid_connect/token_endpoint.rb b/lib/openid_connect/token_endpoint.rb index fd843110a..53347eba2 100644 --- a/lib/openid_connect/token_endpoint.rb +++ b/lib/openid_connect/token_endpoint.rb @@ -29,7 +29,13 @@ module OpenidConnect user = User.find_for_database_authentication(username: req.username) if user if user.valid_password?(req.password) + scope_list = req.scope.map { |scope_name| + OpenidConnect::Scope.find_by(name: scope_name).tap do |scope| + req.invalid_scope! "Unknown scope: #{scope}" unless scope + end + } # TODO: Check client scope permissions auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: o_auth_app, user: user) + auth.scopes << scope_list res.access_token = auth.create_access_token else req.invalid_grant! @@ -40,6 +46,8 @@ module OpenidConnect end def handle_refresh_flow(req, res) + # Handle as if scope request was omitted even if provided. + # See https://tools.ietf.org/html/rfc6749#section-6 for handling auth = OpenidConnect::Authorization.find_by_refresh_token req.client_id, req.refresh_token if auth res.access_token = auth.create_access_token diff --git a/spec/lib/openid_connect/protected_resource_endpoint_spec.rb b/spec/lib/openid_connect/protected_resource_endpoint_spec.rb index 4addeb470..7fd948a75 100644 --- a/spec/lib/openid_connect/protected_resource_endpoint_spec.rb +++ b/spec/lib/openid_connect/protected_resource_endpoint_spec.rb @@ -1,75 +1,89 @@ require "spec_helper" describe OpenidConnect::ProtectedResourceEndpoint, type: :request do - describe "getting the user info" do - let!(:client) do - OpenidConnect::OAuthApplication.create!( - client_name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) - end - let!(:auth) { OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) } - let!(:access_token) { auth.create_access_token.to_s } - let!(:invalid_token) { SecureRandom.hex(32).to_s } - # TODO: Add tests for expired access tokens + let!(:client) do + OpenidConnect::OAuthApplication.create!( + client_name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) + end + let(:auth_with_read) do + auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) + auth.scopes << [OpenidConnect::Scope.find_or_create_by(name: "read")] + auth + end + let!(:access_token_with_read) { auth_with_read.create_access_token.to_s } + let(:auth_with_read_and_write) do + auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) + auth.scopes << [OpenidConnect::Scope.find_or_create_by(name: "read"), OpenidConnect::Scope.find_or_create_by(name: "write")] + auth + end + let!(:access_token_with_read_and_write) { auth_with_read_and_write.create_access_token.to_s } + let(:invalid_token) { SecureRandom.hex(32).to_s } - context "when access token is valid" do - it "shows the user's username and email" do - get "/api/v0/user/", access_token: access_token + # TODO: Add tests for expired access tokens + + context "when read scope access token is provided for read required endpoint" do + describe "user info endpoint" do + before do + get api_v0_user_path, access_token: access_token_with_read + end + + it "shows the info" do json_body = JSON.parse(response.body) expect(json_body["username"]).to eq(bob.username) expect(json_body["email"]).to eq(bob.email) end - it "should include private in the cache-control header" do - get "/api/v0/user/", access_token: access_token + + it "includes private in the cache-control header" do expect(response.headers["Cache-Control"]).to include("private") end end + end - context "when no access token is provided" do - it "should respond with a 401 Unauthorized response" do - get "/api/v0/user/" - expect(response.status).to be(401) - end - it "should have an auth-scheme value of Bearer" do - get "/api/v0/user/" - expect(response.headers["WWW-Authenticate"]).to include("Bearer") - end + context "when no access token is provided" do + it "should respond with a 401 Unauthorized response" do + get api_v0_user_path + expect(response.status).to be(401) + end + it "should have an auth-scheme value of Bearer" do + get api_v0_user_path + expect(response.headers["WWW-Authenticate"]).to include("Bearer") + end + end + + context "when an invalid access token is provided" do + before do + get api_v0_user_path, access_token: invalid_token end - context "when an invalid access token is provided" do - before do - get "/api/v0/user/", access_token: invalid_token - end - - it "should respond with a 401 Unauthorized response" do - expect(response.status).to be(401) - end - - it "should have an auth-scheme value of Bearer" do - expect(response.headers["WWW-Authenticate"]).to include("Bearer") - end - - it "should contain an invalid_token error" do - expect(response.body).to include("invalid_token") - end + it "should respond with a 401 Unauthorized response" do + expect(response.status).to be(401) end - context "when authorization has been destroyed" do - before do - auth.destroy - get "/api/v0/user/", access_token: access_token - end + it "should have an auth-scheme value of Bearer" do + expect(response.headers["WWW-Authenticate"]).to include("Bearer") + end - it "should respond with a 401 Unauthorized response" do - expect(response.status).to be(401) - end + it "should contain an invalid_token error" do + expect(response.body).to include("invalid_token") + end + end - it "should have an auth-scheme value of Bearer" do - expect(response.headers["WWW-Authenticate"]).to include("Bearer") - end + context "when authorization has been destroyed" do + before do + auth_with_read.destroy + get api_v0_user_path, access_token: access_token_with_read + end - it "should contain an invalid_token error" do - expect(response.body).to include("invalid_token") - end + it "should respond with a 401 Unauthorized response" do + expect(response.status).to be(401) + end + + it "should have an auth-scheme value of Bearer" do + expect(response.headers["WWW-Authenticate"]).to include("Bearer") + end + + it "should contain an invalid_token error" do + expect(response.body).to include("invalid_token") end end end diff --git a/spec/lib/openid_connect/token_endpoint_spec.rb b/spec/lib/openid_connect/token_endpoint_spec.rb index 736fb715d..7af40fd21 100644 --- a/spec/lib/openid_connect/token_endpoint_spec.rb +++ b/spec/lib/openid_connect/token_endpoint_spec.rb @@ -5,53 +5,57 @@ describe OpenidConnect::TokenEndpoint, type: :request do OpenidConnect::OAuthApplication.create!( redirect_uris: ["http://localhost"], client_name: "diaspora client") end - let!(:auth) { OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) } + let(:auth) { OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) } + + before do + OpenidConnect::Scope.find_or_create_by(name: "read") + end describe "the password grant type" do context "when the username field is missing" do it "should return an invalid request error" do - post "/openid_connect/access_tokens", grant_type: "password", password: "bluepin7", - client_id: client.client_id, client_secret: client.client_secret + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "bob", - client_id: client.client_id, client_secret: client.client_secret + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "randomnoexist", - password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "bob", - password: "wrongpassword", client_id: client.client_id, client_secret: client.client_secret + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "bob", - password: "bluepin7", client_id: client.client_id, client_secret: "client.client_secret" + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "bob", - password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret + post 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) @@ -61,16 +65,16 @@ describe OpenidConnect::TokenEndpoint, type: :request do context "when there are duplicate fields" do it "should return an invalid request error" do - post "/openid_connect/access_tokens", grant_type: "password", username: "bob", password: "bluepin7", - username: "bob", password: "bluepin6", client_id: client.client_id, client_secret: client.client_secret + post 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 "/openid_connect/access_tokens", grant_type: "password", username: "bob", - password: "bluepin7", client_id: SecureRandom.hex(16).to_s, client_secret: client.client_secret + post 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 @@ -80,8 +84,8 @@ describe OpenidConnect::TokenEndpoint, type: :request do describe "an unsupported grant type" do it "should return an unsupported grant type error" do - post "/openid_connect/access_tokens", grant_type: "noexistgrant", username: "bob", - password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret + post openid_connect_access_tokens_path, grant_type: "noexistgrant", username: "bob", + password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read" expect(response.body).to include "unsupported_grant_type" end end @@ -89,7 +93,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do describe "the refresh token flow" do context "when the refresh token is valid" do it "should return an access token" do - post "/openid_connect/access_tokens", grant_type: "refresh_token", + post openid_connect_access_tokens_path, grant_type: "refresh_token", client_id: client.client_id, client_secret: client.client_secret, refresh_token: auth.refresh_token json = JSON.parse(response.body) expect(response.body).to include "expires_in" @@ -100,15 +104,15 @@ describe OpenidConnect::TokenEndpoint, type: :request do context "when the refresh token is not valid" do it "should return an invalid grant error" do - post "/openid_connect/access_tokens", grant_type: "refresh_token", - client_id: client.client_id, client_secret: client.client_secret, refresh_token: " " + post openid_connect_access_tokens_path, grant_type: "refresh_token", + client_id: client.client_id, client_secret: client.client_secret, refresh_token: "123456" expect(response.body).to include "invalid_grant" end end context "when the client is unregistered" do it "should return an error" do - post "/openid_connect/access_tokens", grant_type: "refresh_token", refresh_token: auth.refresh_token, + post openid_connect_access_tokens_path, grant_type: "refresh_token", refresh_token: auth.refresh_token, client_id: SecureRandom.hex(16).to_s, client_secret: client.client_secret expect(response.body).to include "invalid_client" end @@ -116,7 +120,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do context "when the refresh_token field is missing" do it "should return an invalid request error" do - post "/openid_connect/access_tokens", grant_type: "refresh_token", + post openid_connect_access_tokens_path, grant_type: "refresh_token", client_id: client.client_id, client_secret: client.client_secret expect(response.body).to include "'refresh_token' required" end @@ -124,7 +128,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do context "when the client_secret doesn't match" do it "should return an invalid client error" do - post "/openid_connect/access_tokens", grant_type: "refresh_token", refresh_token: auth.refresh_token, + post openid_connect_access_tokens_path, grant_type: "refresh_token", refresh_token: auth.refresh_token, client_id: client.client_id, client_secret: "client.client_secret" expect(response.body).to include "invalid_client" end