Commit 25f51c60 authored by theworldbright's avatar theworldbright

Add support for prompt parameter

parent 8be3be3e
......@@ -9,7 +9,13 @@ module Api
before_action :authenticate_user!
def new
request_authorization_consent_form
auth = Api::OpenidConnect::Authorization.find_by_client_id_and_user(params[:client_id], current_user)
if params[:prompt]
prompt = params[:prompt].split(" ")
handle_prompt(prompt, auth)
else
handle_authorization_form(auth)
end
end
def create
......@@ -29,12 +35,50 @@ module Api
private
def request_authorization_consent_form # TODO: Add support for prompt params
if Api::OpenidConnect::Authorization.find_by_client_id_and_user(params[:client_id], current_user)
def handle_prompt(prompt, auth)
if prompt.include? "select_account"
handle_prompt_params_error("account_selection_required",
"There is no support for choosing among multiple accounts")
elsif prompt.include? "none"
handle_prompt_none(prompt, auth)
elsif prompt.include?("login") && logged_in_more_than_5_minutes_ago?
handle_prompt_params_error("login_required",
"There is no support for re-authenticating already authenticated users")
elsif prompt.include? "consent"
request_authorization_consent_form
else
handle_authorization_form(auth)
end
end
def handle_authorization_form(auth)
if auth
process_authorization_consent("true")
else
endpoint = Api::OpenidConnect::AuthorizationPoint::EndpointStartPoint.new(current_user)
handle_start_point_response(endpoint)
request_authorization_consent_form
end
end
def request_authorization_consent_form
endpoint = Api::OpenidConnect::AuthorizationPoint::EndpointStartPoint.new(current_user)
handle_start_point_response(endpoint)
end
def logged_in_more_than_5_minutes_ago?
(current_user.current_sign_in_at.to_i - Time.zone.now.to_i) > 300
end
def handle_prompt_none(prompt, auth)
if prompt == ["none"]
if auth
process_authorization_consent("true")
else
handle_prompt_params_error("interaction_required",
"The Authentication Request cannot be completed without end-user interaction")
end
else
handle_prompt_params_error("invalid_request",
"The 'none' value cannot be used with any other prompt value")
end
end
......@@ -115,6 +159,26 @@ module Api
session[:response_type]
end
end
def handle_prompt_params_error(error, error_description)
if params[:client_id] && params[:redirect_uri]
app = Api::OpenidConnect::OAuthApplication.find_by(client_id: params[:client_id])
if app && app.redirect_uris.include?(params[:redirect_uri])
redirect_prompt_error_display(error, error_description)
else
render json: {error: "bad_request",
description: "No client with client_id " + params[:client_id] + " found"}
end
else
render json: {error: "bad_request", description: "Missing client id or redirect URI"}
end
end
def redirect_prompt_error_display(error, error_description)
redirect_params_hash = {error: error, error_description: error_description, state: params[:state]}
redirect_fragment = redirect_params_hash.compact.map {|key, value| key.to_s + "=" + value }.join("&")
redirect_to params[:redirect_uri] + "#" + redirect_fragment
end
end
end
end
......@@ -31,7 +31,7 @@ class UserApplicationsPresenter
def find_scopes(application)
scopes = Api::OpenidConnect::Authorization.find_by_client_id_and_user(
application.client_id, @current_user).scopes
scopes.each_with_object([]){|scope, array| array << scope.name }
scopes.each_with_object([]) {|scope, array| array << scope.name }
end
def find_id(application)
......
......@@ -4,6 +4,7 @@ o_auth_query_params = %i(
scope=openid%20read
nonce=hello
state=hi
prompt=login
).join("&")
Given /^I send a post request from that client to the implicit flow authorization endpoint$/ do
......
......@@ -84,6 +84,51 @@ describe Api::OpenidConnect::AuthorizationsController, type: :controller do
expect(response.location).to match("error=invalid_request")
end
end
context "when prompt is none" do
it "should return an interaction required error" do
post :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", state: 1234, display: "page", prompt: "none"
expect(response.location).to match("error=interaction_required")
expect(response.location).to match("state=1234")
end
end
context "when prompt is none and consent" do
it "should return an interaction required error" do
post :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", state: 1234, display: "page", prompt: "none consent"
expect(response.location).to match("error=invalid_request")
expect(response.location).to match("state=1234")
end
end
context "when prompt is select_account" do
it "should return an account_selection_required error" do
post :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", state: 1234, display: "page", prompt: "select_account"
expect(response.location).to match("error=account_selection_required")
expect(response.location).to match("state=1234")
end
end
context "when prompt is none and client ID is invalid" do
it "should return an account_selection_required error" do
post :new, client_id: "random", redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", state: 1234, display: "page", prompt: "none"
json_body = JSON.parse(response.body)
expect(json_body["error"]).to match("bad_request")
end
end
context "when prompt is none and redirect URI does not match pre-registered URIs" do
it "should return an account_selection_required error" do
post :new, client_id: client.client_id, redirect_uri: "http://randomuri:3000/",
response_type: "id_token", scope: "openid", state: 1234, display: "page", prompt: "none"
json_body = JSON.parse(response.body)
expect(json_body["error"]).to match("bad_request")
end
end
end
context "when already authorized" do
let!(:auth) {
......@@ -110,6 +155,29 @@ describe Api::OpenidConnect::AuthorizationsController, type: :controller do
expect(response.location).to have_content("state=4130930983")
end
end
context "when prompt is none" do
it "should return the id token in a fragment" do
post :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", nonce: 413_093_098_3, state: 413_093_098_3,
display: "page", prompt: "none"
expect(response.location).to have_content("id_token=")
encoded_id_token = response.location[/(?<=id_token=)[^&]+/]
decoded_token = OpenIDConnect::ResponseObject::IdToken.decode encoded_id_token,
Api::OpenidConnect::IdTokenConfig.public_key
expect(decoded_token.nonce).to eq("4130930983")
expect(decoded_token.exp).to be > Time.zone.now.utc.to_i
end
end
context "when prompt contains consent" do
it "should return a consent form page" do
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/",
response_type: "id_token", scope: "openid", nonce: 413_093_098_3, state: 413_093_098_3,
display: "page", prompt: "consent"
expect(response.body).to match("Diaspora Test Client")
end
end
end
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment