Add support for scopes

Remove scopes from tokens

Squashed commits:

[83db38f] Add redirect uris to supported metadata
This commit is contained in:
theworldbright 2015-07-27 18:26:41 +09:00
parent de4f68c289
commit 3cbe75469b
22 changed files with 165 additions and 171 deletions

View file

@ -1,6 +1,7 @@
class Api::V0::BaseController < ApplicationController class Api::V0::BaseController < ApplicationController
include OpenidConnect::ProtectedResourceEndpoint include OpenidConnect::ProtectedResourceEndpoint
before_filter :require_access_token def user
current_token ? current_token.authorization.user : nil
end
end end

View file

@ -1,11 +1,9 @@
class Api::V0::UsersController < Api::V0::BaseController class Api::V0::UsersController < Api::V0::BaseController
before_filter do
require_access_token OpenidConnect::Scope.find_by(name: "read")
end
def show def show
render json: user render json: user
end end
private
def user
current_token.authorization.user
end
end end

View file

@ -47,7 +47,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
session[:client_id] = @o_auth_application.client_id session[:client_id] = @o_auth_application.client_id
session[:response_type] = @response_type session[:response_type] = @response_type
session[:redirect_uri] = @redirect_uri session[:redirect_uri] = @redirect_uri
session[:scopes] = @scopes.map(&:name) session[:scopes] = @scopes.map(&:name).join(" ")
session[:request_object] = @request_object session[:request_object] = @request_object
session[:nonce] = params[:nonce] session[:nonce] = params[:nonce]
end end
@ -84,7 +84,7 @@ class OpenidConnect::AuthorizationsController < ApplicationController
req.update_param("response_type", session[:response_type].respond_to?(:map) ? req.update_param("response_type", session[:response_type].respond_to?(:map) ?
session[:response_type].map(&:to_s).join(" ") : session[:response_type].map(&:to_s).join(" ") :
session[:response_type]) 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("request_object", session[:request_object])
req.update_param("nonce", session[:nonce]) req.update_param("nonce", session[:nonce])
end end

View file

@ -6,6 +6,7 @@ class OpenidConnect::Authorization < ActiveRecord::Base
validates :o_auth_application, presence: true validates :o_auth_application, presence: true
validates :user, uniqueness: {scope: :o_auth_application} validates :user, uniqueness: {scope: :o_auth_application}
has_many :authorization_scopes
has_many :scopes, through: :authorization_scopes has_many :scopes, through: :authorization_scopes
has_many :o_auth_access_tokens, dependent: :destroy has_many :o_auth_access_tokens, dependent: :destroy
has_many :id_tokens, dependent: :destroy has_many :id_tokens, dependent: :destroy
@ -16,6 +17,12 @@ class OpenidConnect::Authorization < ActiveRecord::Base
self.refresh_token = SecureRandom.hex(32) self.refresh_token = SecureRandom.hex(32)
end end
def accessible?(required_scopes=nil)
Array(required_scopes).all? do |required_scope|
scopes.include? required_scope
end
end
def create_access_token def create_access_token
o_auth_access_tokens.create!.bearer_token o_auth_access_tokens.create!.bearer_token
# TODO: Add support for request object # TODO: Add support for request object

View file

@ -1,6 +1,5 @@
class OpenidConnect::OAuthAccessToken < ActiveRecord::Base class OpenidConnect::OAuthAccessToken < ActiveRecord::Base
belongs_to :authorization belongs_to :authorization
has_many :scopes, through: :scope_tokens
before_validation :setup, on: :create before_validation :setup, on: :create
@ -20,8 +19,4 @@ class OpenidConnect::OAuthAccessToken < ActiveRecord::Base
expires_in: (expires_at - Time.now.utc).to_i expires_in: (expires_at - Time.now.utc).to_i
) )
end end
def accessible?(_scopes_or_claims_=nil)
true # TODO: For now don't support scopes
end
end end

View file

@ -44,7 +44,7 @@ class OpenidConnect::OAuthApplication < ActiveRecord::Base
def supported_metadata def supported_metadata
%i(client_name response_types grant_types application_type %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 end
def registrar_attributes(registrar) def registrar_attributes(registrar)

View file

@ -1,8 +1,7 @@
class OpenidConnect::Scope < ActiveRecord::Base class OpenidConnect::Scope < ActiveRecord::Base
has_many :o_auth_access_token, through: :scope_tokens
has_many :authorizations, through: :authorization_scopes has_many :authorizations, through: :authorization_scopes
validates :name, presence: true, uniqueness: true validates :name, presence: true, uniqueness: true
# TODO: Incomplete class # TODO: Add constants so scopes can be referenced as OpenidConnect::Scope::Read
end end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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", "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 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| create_table "authorizations", force: :cascade do |t|
t.integer "user_id", limit: 4 t.integer "user_id", limit: 4
t.integer "o_auth_application_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", ["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 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| create_table "blocks", force: :cascade do |t|
t.integer "user_id", limit: 4 t.integer "user_id", limit: 4
t.integer "person_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 t.datetime "updated_at", null: false
end 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| create_table "services", force: :cascade do |t|
t.string "type", limit: 127, null: false t.string "type", limit: 127, null: false
t.integer "user_id", limit: 4, null: false t.integer "user_id", limit: 4, null: false

View file

@ -1,6 +1,7 @@
Feature: Access protected resources using password flow Feature: Access protected resources using password flow
Background: Background:
Given a user with username "kent" Given a user with username "kent"
And all scopes exist
Scenario: Invalid credentials to token endpoint Scenario: Invalid credentials to token endpoint
When I register a new client When I register a new client

View file

@ -2,7 +2,7 @@
Feature: Access protected resources using implicit flow Feature: Access protected resources using implicit flow
Background: Background:
Given a user with username "kent" Given a user with username "kent"
And the OpenID scope exists And all scopes exist
Scenario: Invalid client id to auth endpoint Scenario: Invalid client id to auth endpoint
When I register a new client When I register a new client

View file

@ -1,24 +1,18 @@
o_auth_query_params = %i( o_auth_query_params = %i(
redirect_uri=http://localhost:3000 redirect_uri=http://localhost:3000
response_type=id_token token response_type=id_token%20token
scope=openid scope=openid%20read
nonce=hello nonce=hello
state=hi state=hi
).join("&") ).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 Given /^I send a post request from that client to the implicit flow authorization endpoint$/ do
client_json = JSON.parse(last_response.body) client_json = JSON.parse(last_response.body)
auth_endpoint_url = "/openid_connect/authorizations/new" visit new_openid_connect_authorization_path + "?client_id=#{client_json["o_auth_application"]["client_id"]}&#{o_auth_query_params}"
visit "#{auth_endpoint_url}?client_id=#{client_json["o_auth_application"]["client_id"]}&#{o_auth_query_params}"
end end
Given /^I send a post request from that client to the implicit flow authorization endpoint using a invalid client id/ do 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 new_openid_connect_authorization_path + "?client_id=randomid&#{o_auth_query_params}"
visit "#{auth_endpoint_url}?client_id=randomid&#{o_auth_query_params}"
end end
When /^I give my consent and authorize the client$/ do 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 When /^I parse the bearer tokens and use it to access user info$/ do
access_token = current_url[/(?<=access_token=)[^&]+/] access_token = current_url[/(?<=access_token=)[^&]+/]
user_info_endpoint_url = "/api/v0/user/" get api_v0_user_path, access_token: access_token
get user_info_endpoint_url, access_token: access_token
end end
Then /^I should see an "([^\"]*)" error$/ do |error_message| Then /^I should see an "([^\"]*)" error$/ do |error_message|

View file

@ -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 When /^I register a new client$/ do
client_registration_url = "/openid_connect/clients" post openid_connect_clients_path, redirect_uris: ["http://localhost:3000"], client_name: "diaspora client"
post client_registration_url, redirect_uris: ["http://localhost:3000"], client_name: "diaspora client"
end end
Given /^I send a post request from that client to the password flow token endpoint using "([^\"]*)"'s credentials$/ do |username| 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) client_json = JSON.parse(last_response.body)
user = User.find_by(username: username) user = User.find_by(username: username)
token_endpoint_url = "/openid_connect/access_tokens" post openid_connect_access_tokens_path, grant_type: "password", username: user.username,
post token_endpoint_url, grant_type: "password", username: user.username,
password: "password", # Password has been hard coded as all test accounts seem to have a password of "password" 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_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 end
Given /^I send a post request from that client to the password flow token endpoint using invalid credentials$/ do 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) client_json = JSON.parse(last_response.body)
token_endpoint_url = "/openid_connect/access_tokens" post openid_connect_access_tokens_path, grant_type: "password", username: "bob", password: "wrongpassword",
post token_endpoint_url, grant_type: "password", username: "bob", password: "wrongpassword",
client_id: client_json["o_auth_application"]["client_id"], 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 end
When /^I use received valid bearer tokens to access user info$/ do When /^I use received valid bearer tokens to access user info$/ do
access_token_json = JSON.parse(last_response.body) access_token_json = JSON.parse(last_response.body)
user_info_endpoint_url = "/api/v0/user/" get api_v0_user_path, access_token: access_token_json["access_token"]
get user_info_endpoint_url, access_token: access_token_json["access_token"]
end end
When /^I use invalid bearer tokens to access user info$/ do When /^I use invalid bearer tokens to access user info$/ do
user_info_endpoint_url = "/api/v0/user/" get api_v0_user_path, access_token: SecureRandom.hex(32)
get user_info_endpoint_url, access_token: SecureRandom.hex(32)
end end
Then /^I should receive "([^\"]*)"'s id, username, and email$/ do |username| Then /^I should receive "([^\"]*)"'s id, username, and email$/ do |username|

View file

@ -45,8 +45,8 @@ module OpenidConnect
end end
def build_scopes(req) def build_scopes(req)
@scopes = req.scope.map {|scope| @scopes = req.scope.map {|scope_name|
OpenidConnect::Scope.where(name: scope).first.tap do |scope| OpenidConnect::Scope.where(name: scope_name).first.tap do |scope|
req.invalid_scope! "Unknown scope: #{scope}" unless scope req.invalid_scope! "Unknown scope: #{scope}" unless scope
end end
} }

View file

@ -21,6 +21,7 @@ module OpenidConnect
# TODO: Add support for request object and auth code # TODO: Add support for request object and auth code
def approved!(req, res) def approved!(req, res)
auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: @o_auth_application, user: @user) auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: @o_auth_application, user: @user)
auth.scopes << @scopes
response_types = Array(req.response_type) response_types = Array(req.response_type)
if response_types.include?(:token) if response_types.include?(:token)
res.access_token = auth.create_access_token res.access_token = auth.create_access_token

View file

@ -2,20 +2,14 @@ module OpenidConnect
module ProtectedResourceEndpoint module ProtectedResourceEndpoint
attr_reader :current_token attr_reader :current_token
def require_access_token def require_access_token(*required_scopes)
@current_token = request.env[Rack::OAuth2::Server::Resource::ACCESS_TOKEN] @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") raise Rack::OAuth2::Server::Resource::Bearer::Unauthorized.new("Unauthorized user")
end end
# TODO: This block is useless until we actually start checking for scopes unless @current_token.authorization.try(:accessible?, required_scopes)
unless @current_token.try(:accessible?, required_scopes)
raise Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope) raise Rack::OAuth2::Server::Resource::Bearer::Forbidden.new(:insufficient_scope)
end end
end end
# TODO: Scopes should be implemented here
def required_scopes
nil
end
end end
end end

View file

@ -29,7 +29,13 @@ module OpenidConnect
user = User.find_for_database_authentication(username: req.username) user = User.find_for_database_authentication(username: req.username)
if user if user
if user.valid_password?(req.password) 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 = 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 res.access_token = auth.create_access_token
else else
req.invalid_grant! req.invalid_grant!
@ -40,6 +46,8 @@ module OpenidConnect
end end
def handle_refresh_flow(req, res) 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 auth = OpenidConnect::Authorization.find_by_refresh_token req.client_id, req.refresh_token
if auth if auth
res.access_token = auth.create_access_token res.access_token = auth.create_access_token

View file

@ -1,75 +1,89 @@
require "spec_helper" require "spec_helper"
describe OpenidConnect::ProtectedResourceEndpoint, type: :request do describe OpenidConnect::ProtectedResourceEndpoint, type: :request do
describe "getting the user info" do let!(:client) do
let!(:client) do OpenidConnect::OAuthApplication.create!(
OpenidConnect::OAuthApplication.create!( client_name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"])
client_name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) end
end let(:auth_with_read) do
let!(:auth) { OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob) } auth = OpenidConnect::Authorization.find_or_create_by(o_auth_application: client, user: bob)
let!(:access_token) { auth.create_access_token.to_s } auth.scopes << [OpenidConnect::Scope.find_or_create_by(name: "read")]
let!(:invalid_token) { SecureRandom.hex(32).to_s } auth
# TODO: Add tests for expired access tokens 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 # TODO: Add tests for expired access tokens
it "shows the user's username and email" do
get "/api/v0/user/", access_token: access_token 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) json_body = JSON.parse(response.body)
expect(json_body["username"]).to eq(bob.username) expect(json_body["username"]).to eq(bob.username)
expect(json_body["email"]).to eq(bob.email) expect(json_body["email"]).to eq(bob.email)
end 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") expect(response.headers["Cache-Control"]).to include("private")
end end
end end
end
context "when no access token is provided" do context "when no access token is provided" do
it "should respond with a 401 Unauthorized response" do it "should respond with a 401 Unauthorized response" do
get "/api/v0/user/" get api_v0_user_path
expect(response.status).to be(401) expect(response.status).to be(401)
end end
it "should have an auth-scheme value of Bearer" do it "should have an auth-scheme value of Bearer" do
get "/api/v0/user/" get api_v0_user_path
expect(response.headers["WWW-Authenticate"]).to include("Bearer") expect(response.headers["WWW-Authenticate"]).to include("Bearer")
end end
end
context "when an invalid access token is provided" do
before do
get api_v0_user_path, access_token: invalid_token
end end
context "when an invalid access token is provided" do it "should respond with a 401 Unauthorized response" do
before do expect(response.status).to be(401)
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
end end
context "when authorization has been destroyed" do it "should have an auth-scheme value of Bearer" do
before do expect(response.headers["WWW-Authenticate"]).to include("Bearer")
auth.destroy end
get "/api/v0/user/", access_token: access_token
end
it "should respond with a 401 Unauthorized response" do it "should contain an invalid_token error" do
expect(response.status).to be(401) expect(response.body).to include("invalid_token")
end end
end
it "should have an auth-scheme value of Bearer" do context "when authorization has been destroyed" do
expect(response.headers["WWW-Authenticate"]).to include("Bearer") before do
end auth_with_read.destroy
get api_v0_user_path, access_token: access_token_with_read
end
it "should contain an invalid_token error" do it "should respond with a 401 Unauthorized response" do
expect(response.body).to include("invalid_token") expect(response.status).to be(401)
end 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 end
end end

View file

@ -5,53 +5,57 @@ describe OpenidConnect::TokenEndpoint, type: :request do
OpenidConnect::OAuthApplication.create!( OpenidConnect::OAuthApplication.create!(
redirect_uris: ["http://localhost"], client_name: "diaspora client") redirect_uris: ["http://localhost"], client_name: "diaspora client")
end 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 describe "the password grant type" do
context "when the username field is missing" do context "when the username field is missing" do
it "should return an invalid request error" do it "should return an invalid request error" do
post "/openid_connect/access_tokens", grant_type: "password", password: "bluepin7", post openid_connect_access_tokens_path, grant_type: "password", password: "bluepin7",
client_id: client.client_id, client_secret: client.client_secret client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "'username' required" expect(response.body).to include "'username' required"
end end
end end
context "when the password field is missing" do context "when the password field is missing" do
it "should return an invalid request error" do it "should return an invalid request error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", post openid_connect_access_tokens_path, grant_type: "password", username: "bob",
client_id: client.client_id, client_secret: client.client_secret client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "'password' required" expect(response.body).to include "'password' required"
end end
end end
context "when the username does not match an existing user" do context "when the username does not match an existing user" do
it "should return an invalid request error" do it "should return an invalid request error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "randomnoexist", post openid_connect_access_tokens_path, grant_type: "password", username: "randomnoexist",
password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "invalid_grant" expect(response.body).to include "invalid_grant"
end end
end end
context "when the password is invalid" do context "when the password is invalid" do
it "should return an invalid request error" do it "should return an invalid request error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", post openid_connect_access_tokens_path, grant_type: "password", username: "bob",
password: "wrongpassword", client_id: client.client_id, client_secret: client.client_secret password: "wrongpassword", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "invalid_grant" expect(response.body).to include "invalid_grant"
end end
end end
context "when the client_secret doesn't match" do context "when the client_secret doesn't match" do
it "should return an invalid client error" do it "should return an invalid client error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", post openid_connect_access_tokens_path, grant_type: "password", username: "bob",
password: "bluepin7", client_id: client.client_id, client_secret: "client.client_secret" password: "bluepin7", client_id: client.client_id, client_secret: "client.client_secret", scope: "read"
expect(response.body).to include "invalid_client" expect(response.body).to include "invalid_client"
end end
end end
context "when the request is valid" do context "when the request is valid" do
it "should return an access token" do it "should return an access token" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", post openid_connect_access_tokens_path, grant_type: "password", username: "bob",
password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
json = JSON.parse(response.body) json = JSON.parse(response.body)
expect(json.keys).to include "expires_in" expect(json.keys).to include "expires_in"
expect(json["access_token"].length).to eq(64) 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 context "when there are duplicate fields" do
it "should return an invalid request error" do it "should return an invalid request error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", password: "bluepin7", 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 username: "bob", password: "bluepin6", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "invalid_grant" expect(response.body).to include "invalid_grant"
end end
end end
context "when the client is unregistered" do context "when the client is unregistered" do
it "should return an error" do it "should return an error" do
post "/openid_connect/access_tokens", grant_type: "password", username: "bob", 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 password: "bluepin7", client_id: SecureRandom.hex(16).to_s, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "invalid_client" expect(response.body).to include "invalid_client"
end end
end end
@ -80,8 +84,8 @@ describe OpenidConnect::TokenEndpoint, type: :request do
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 "/openid_connect/access_tokens", grant_type: "noexistgrant", username: "bob", post openid_connect_access_tokens_path, grant_type: "noexistgrant", username: "bob",
password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret password: "bluepin7", client_id: client.client_id, client_secret: client.client_secret, scope: "read"
expect(response.body).to include "unsupported_grant_type" expect(response.body).to include "unsupported_grant_type"
end end
end end
@ -89,7 +93,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do
describe "the refresh token flow" do describe "the refresh token flow" do
context "when the refresh token is valid" do context "when the refresh token is valid" do
it "should return an access token" 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 client_id: client.client_id, client_secret: client.client_secret, refresh_token: auth.refresh_token
json = JSON.parse(response.body) json = JSON.parse(response.body)
expect(response.body).to include "expires_in" 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 context "when the refresh token is not valid" do
it "should return an invalid grant error" do it "should return an invalid grant 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, refresh_token: " " client_id: client.client_id, client_secret: client.client_secret, refresh_token: "123456"
expect(response.body).to include "invalid_grant" expect(response.body).to include "invalid_grant"
end end
end end
context "when the client is unregistered" do context "when the client is unregistered" do
it "should return an error" 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 client_id: SecureRandom.hex(16).to_s, client_secret: client.client_secret
expect(response.body).to include "invalid_client" expect(response.body).to include "invalid_client"
end end
@ -116,7 +120,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do
context "when the refresh_token field is missing" do context "when the refresh_token field is missing" do
it "should return an invalid request error" 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 client_id: client.client_id, client_secret: client.client_secret
expect(response.body).to include "'refresh_token' required" expect(response.body).to include "'refresh_token' required"
end end
@ -124,7 +128,7 @@ describe OpenidConnect::TokenEndpoint, type: :request do
context "when the client_secret doesn't match" do context "when the client_secret doesn't match" do
it "should return an invalid client error" 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" client_id: client.client_id, client_secret: "client.client_secret"
expect(response.body).to include "invalid_client" expect(response.body).to include "invalid_client"
end end