Allow POST requests at authentication endpoint

This commit is contained in:
theworldbright 2015-07-10 15:12:21 +09:00 committed by theworldbright
parent 3cfbcbce8f
commit 3d26cbf657
4 changed files with 142 additions and 24 deletions

View file

@ -1,8 +1,7 @@
class OpenidConnect::AuthorizationsController < ApplicationController class OpenidConnect::AuthorizationsController < ApplicationController
rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e| rescue_from Rack::OAuth2::Server::Authorize::BadRequest do |e|
@error = e logger.info e.backtrace[0,10].join("\n")
print e.backtrace[0,10].join("\n") render json: {error: e.message || :error, status: e.status}
render json: {error: :error, status: e.status} #error_message: e.message
end end
before_action :authenticate_user! before_action :authenticate_user!
@ -19,21 +18,34 @@ private
def request_authorization_consent_form def request_authorization_consent_form
endpoint = OpenidConnect::Authorization::EndpointStartPoint.new(current_user) endpoint = OpenidConnect::Authorization::EndpointStartPoint.new(current_user)
endpoint.call(request.env) handleStartPointResponse(endpoint)
end
def handleStartPointResponse(endpoint)
status, header, response = *endpoint.call(request.env)
if response.redirect?
redirect_to header['Location']
else
@client, @response_type, @redirect_uri, @scopes, @request_object = *[ @client, @response_type, @redirect_uri, @scopes, @request_object = *[
endpoint.client, endpoint.response_type, endpoint.redirect_uri, endpoint.scopes, endpoint.request_object endpoint.client, endpoint.response_type, endpoint.redirect_uri, endpoint.scopes, endpoint.request_object
] ]
saveRequestParameters saveRequestParameters
render :new render :new
end end
end
def process_authorization_consent(approvedString) def process_authorization_consent(approvedString)
endpoint = OpenidConnect::Authorization::EndpointConfirmationPoint.new(current_user, to_boolean(approvedString)) endpoint = OpenidConnect::Authorization::EndpointConfirmationPoint.new(current_user, to_boolean(approvedString))
restoreRequestParameters(endpoint) restoreRequestParameters(endpoint)
handleConfirmationPointResponse(endpoint)
end
def handleConfirmationPointResponse(endpoint)
status, header, response = *endpoint.call(request.env) status, header, response = *endpoint.call(request.env)
redirect_to header['Location'] redirect_to header['Location']
end end
def saveRequestParameters def saveRequestParameters
session[:client_id], session[:response_type], session[:redirect_uri], session[:scopes], session[:request_object] = session[:client_id], session[:response_type], session[:redirect_uri], session[:scopes], session[:request_object] =
@client.client_id, @response_type, @redirect_uri, @scopes.collect { |scope| scope.name }, @request_object @client.client_id, @response_type, @redirect_uri, @scopes.collect { |scope| scope.name }, @request_object

View file

@ -236,8 +236,11 @@ Diaspora::Application.routes.draw do
#OpenID Connect & OAuth #OpenID Connect & OAuth
namespace :openid_connect do namespace :openid_connect do
resources :clients, only: :create resources :clients, only: :create
resources :authorizations, only: [:new, :create]
post 'access_tokens', to: proc { |env| OpenidConnect::TokenEndpoint.new.call(env) } post 'access_tokens', to: proc { |env| OpenidConnect::TokenEndpoint.new.call(env) }
# Authorization Servers MUST support the use of the HTTP GET and POST methods at the Authorization Endpoint (see http://openid.net/specs/openid-connect-core-1_0.html#AuthResponseValidation).
resources :authorizations, only: [:new, :create]
post 'authorizations/new', to: 'authorizations#new'
end end
api_version(module: "Api::V0", path: {value: "api/v0"}, default: true) do api_version(module: "Api::V0", path: {value: "api/v0"}, default: true) do

View file

@ -20,7 +20,7 @@ module OpenidConnect
end end
def buildScopes(req) def buildScopes(req)
@scopes = req.scope.inject([]) do |_scopes_, scope| @scopes = req.scope.inject([]) do |_scopes_, scope|
_scopes_ << Scope.find_by_name(scope) or req.invalid_scope! "Unknown scope: #{scope}" _scopes_ << (Scope.find_by_name(scope) or req.invalid_scope! "Unknown scope: #{scope}")
end end
end end
end end

View file

@ -1,7 +1,8 @@
require 'spec_helper' require 'spec_helper'
describe OpenidConnect::AuthorizationsController, type: :controller do describe OpenidConnect::AuthorizationsController, type: :controller do
let!(:client) { OAuthApplication.create!(redirect_uris: ["http://localhost:3000/"]) } let!(:client) { OAuthApplication.create!(name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/"]) }
let!(:client_with_multiple_redirects) { OAuthApplication.create!(name: "Diaspora Test Client", redirect_uris: ["http://localhost:3000/","http://localhost/"]) }
before do before do
sign_in :user, alice sign_in :user, alice
@ -10,8 +11,9 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
end end
describe "#new" do describe "#new" do
render_views
context "when valid parameters are passed" do context "when valid parameters are passed" do
render_views
context "as GET request" do
it "should return a form page" do it "should return a form page" do
get :new, get :new,
{ {
@ -22,11 +24,107 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
nonce: SecureRandom.hex(16), nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16) state: SecureRandom.hex(16)
} }
expect(response.body).to match("Approve") expect(response.body).to match("Diaspora Test Client")
expect(response.body).to match("Deny") end
end
context "as POST request" do
it "should return a form page" do
post :new,
{
client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token",
scope: "openid",
nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16)
}
expect(response.body).to match("Diaspora Test Client")
end
end
end
context "when client id is missing" do
it "should return an bad request error" do
post :new,
{
redirect_uri: "http://localhost:3000/",
response_type: "id_token",
scope: "openid",
nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16)
}
expect(response.body).to match("bad_request")
end
end
context "when redirect uri is missing" do
context "when only one redirect URL is pre-registered" do
it "should return a form pager" do
# Note this intentionally behavior diverts from OIDC spec http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
# See https://github.com/nov/rack-oauth2/blob/master/lib/rack/oauth2/server/authorize.rb#L63
post :new,
{
client_id: client.client_id,
response_type: "id_token",
scope: "openid",
nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16)
}
expect(response.body).to match("Diaspora Test Client")
end
end
end
context "when multiple redirect URLs are pre-registered" do
it "should return an invalid request error" do
post :new,
{
client_id: client_with_multiple_redirects.client_id,
response_type: "id_token",
scope: "openid",
nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16)
}
expect(response.body).to match("bad_request")
end
end
context "when redirect URI does not match pre-registered URIs" do
it "should return an invalid request error" do
post :new,
{
client_id: client.client_id,
redirect_uri: "http://localhost:2000/",
response_type: "id_token",
scope: "openid",
nonce: SecureRandom.hex(16)
}
expect(response.body).to match("bad_request")
end
end
context "when an unsupported scope is passed in" do
it "should return an invalid scope error" do
post :new,
{
client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token",
scope: "random",
nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16)
}
expect(response.body).to match("error=invalid_scope")
end
end
context "when nonce is missing" do
it "should return an invalid request error" do
post :new,
{
client_id: client.client_id,
redirect_uri: "http://localhost:3000/",
response_type: "id_token",
scope: "openid",
state: SecureRandom.hex(16)
}
expect(response.location).to match("error=invalid_request")
end end
end end
# TODO: Implement tests for missing/invalid parameters
end end
describe "#create" do describe "#create" do
@ -38,16 +136,21 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
response_type: "id_token", response_type: "id_token",
scope: "openid", scope: "openid",
nonce: SecureRandom.hex(16), nonce: SecureRandom.hex(16),
state: SecureRandom.hex(16) state: 4180930983
} }
end end
context "when authorization is approved" do context "when authorization is approved" do
it "should return the id token in a fragment" do before do
post :create, post :create,
{ {
approve: "true" approve: "true"
} }
expect(response.location).to have_content("#id_token=") end
it "should return the id token in a fragment" do
expect(response.location).to have_content("id_token=")
end
it "should return the passed in state" do
expect(response.location).to have_content("state=4180930983")
end end
end end
context "when authorization is denied" do context "when authorization is denied" do
@ -58,10 +161,10 @@ describe OpenidConnect::AuthorizationsController, type: :controller do
} }
end end
it "should return an error in the fragment" do it "should return an error in the fragment" do
expect(response.location).to have_content("#error=") expect(response.location).to have_content("error=")
end end
it "should NOT contain a id token in the fragment" do it "should NOT contain a id token in the fragment" do
expect(response.location).to_not have_content("#id_token=") expect(response.location).to_not have_content("id_token=")
end end
end end
end end