Add support for request_uri and claims
This commit is contained in:
parent
2a002d90c4
commit
0fbcb71255
10 changed files with 112 additions and 21 deletions
|
|
@ -76,10 +76,22 @@ module Api
|
|||
end
|
||||
|
||||
def request_authorization_consent_form
|
||||
add_claims_to_scopes
|
||||
endpoint = Api::OpenidConnect::AuthorizationPoint::EndpointStartPoint.new(current_user)
|
||||
handle_start_point_response(endpoint)
|
||||
end
|
||||
|
||||
def add_claims_to_scopes
|
||||
return unless params[:claims]
|
||||
claims_json = JSON.parse(params[:claims])
|
||||
return unless claims_json
|
||||
claims_array = claims_json["userinfo"].try(:keys)
|
||||
return unless claims_array
|
||||
claims = claims_array.join(" ")
|
||||
req = build_rack_request
|
||||
req.update_param("scope", req[:scope] + " " + claims)
|
||||
end
|
||||
|
||||
def logged_in_before?(seconds)
|
||||
if seconds.nil?
|
||||
false
|
||||
|
|
@ -112,14 +124,12 @@ module Api
|
|||
end
|
||||
|
||||
def save_params_and_render_consent_form(endpoint)
|
||||
@o_auth_application, @response_type, @redirect_uri, @scopes, @request_object = *[
|
||||
@o_auth_application, @response_type, @redirect_uri, @scopes = *[
|
||||
endpoint.o_auth_application, endpoint.response_type,
|
||||
endpoint.redirect_uri, endpoint.scopes, endpoint.request_object
|
||||
endpoint.redirect_uri, endpoint.scopes
|
||||
]
|
||||
save_request_parameters
|
||||
|
||||
@app = UserApplicationPresenter.new @o_auth_application, @scopes
|
||||
|
||||
render :new
|
||||
end
|
||||
|
||||
|
|
@ -128,7 +138,6 @@ module Api
|
|||
session[:response_type] = @response_type
|
||||
session[:redirect_uri] = @redirect_uri
|
||||
session[:scopes] = scopes_as_space_seperated_values
|
||||
session[:request_object] = @request_object
|
||||
session[:nonce] = params[:nonce]
|
||||
end
|
||||
|
||||
|
|
@ -153,7 +162,6 @@ module Api
|
|||
session.delete(:response_type)
|
||||
session.delete(:redirect_uri)
|
||||
session.delete(:scopes)
|
||||
session.delete(:request_object)
|
||||
session.delete(:nonce)
|
||||
end
|
||||
|
||||
|
|
@ -167,7 +175,6 @@ module Api
|
|||
req.update_param("redirect_uri", session[:redirect_uri])
|
||||
req.update_param("response_type", response_type_as_space_seperated_values)
|
||||
req.update_param("scope", session[:scopes])
|
||||
req.update_param("request_object", session[:request_object])
|
||||
req.update_param("nonce", session[:nonce])
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,14 @@ module Api
|
|||
jwks_uri: api_openid_connect_url,
|
||||
scopes_supported: Api::OpenidConnect::Authorization::SCOPES,
|
||||
response_types_supported: Api::OpenidConnect::OAuthApplication.available_response_types,
|
||||
request_object_signing_alg_values_supported: %i(HS256 HS384 HS512),
|
||||
request_object_signing_alg_values_supported: %i(none),
|
||||
request_parameter_supported: true,
|
||||
request_uri_parameter_supported: true,
|
||||
subject_types_supported: %w(public pairwise),
|
||||
id_token_signing_alg_values_supported: %i(RS256),
|
||||
token_endpoint_auth_methods_supported: %w(client_secret_basic client_secret_post private_key_jwt),
|
||||
claims_supported: %w(sub nickname profile picture)
|
||||
claims_parameter_supported: true,
|
||||
claims_supported: %w(sub name nickname profile picture)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -8,7 +8,13 @@ module Api
|
|||
end
|
||||
|
||||
def show
|
||||
render json: current_user, serializer: UserInfoSerializer, authorization: current_token.authorization
|
||||
serializer = UserInfoSerializer.new(current_user)
|
||||
auth = current_token.authorization
|
||||
serializer.serialization_options = { authorization: auth }
|
||||
attributes_without_essential = serializer.attributes.with_indifferent_access.select{|scope| auth.scopes.include? scope }
|
||||
attributes = attributes_without_essential.merge(
|
||||
sub: serializer.sub)
|
||||
render json: attributes.to_json
|
||||
end
|
||||
|
||||
def current_user
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ module Api
|
|||
|
||||
scope :with_redirect_uri, ->(given_uri) { where redirect_uri: given_uri }
|
||||
|
||||
SCOPES = %w(openid read write)
|
||||
SCOPES = %w(openid sub aud name nickname profile picture read write)
|
||||
|
||||
def setup
|
||||
self.refresh_token = SecureRandom.hex(32)
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
class UserInfoSerializer < ActiveModel::Serializer
|
||||
attributes :sub, :nickname, :profile, :picture
|
||||
attributes :sub, :name, :nickname, :profile, :picture
|
||||
|
||||
def sub
|
||||
auth = serialization_options[:authorization]
|
||||
Api::OpenidConnect::SubjectIdentifierCreator.createSub(auth)
|
||||
end
|
||||
|
||||
def name
|
||||
(object.first_name || "") + (object.last_name || "")
|
||||
end
|
||||
|
||||
def nickname
|
||||
object.name
|
||||
end
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@ module Api
|
|||
module AuthorizationPoint
|
||||
class Endpoint
|
||||
attr_accessor :app, :user, :o_auth_application, :redirect_uri, :response_type,
|
||||
:scopes, :_request_, :request_uri, :request_object, :nonce
|
||||
:scopes, :request_uri, :request_object, :nonce
|
||||
delegate :call, to: :app
|
||||
|
||||
def initialize(user)
|
||||
@user = user
|
||||
@app = Rack::OAuth2::Server::Authorize.new do |req, res|
|
||||
build_from_request_object(req)
|
||||
build_attributes(req, res)
|
||||
if OAuthApplication.available_response_types.include? Array(req.response_type).join(" ")
|
||||
handle_response_type(req, res)
|
||||
|
|
@ -29,10 +30,6 @@ module Api
|
|||
raise NotImplementedError # Implemented by subclass
|
||||
end
|
||||
|
||||
def scopes
|
||||
Api::OpenidConnect::Authorization::SCOPES
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_client(req)
|
||||
|
|
@ -48,12 +45,17 @@ module Api
|
|||
end
|
||||
|
||||
def build_scopes(req)
|
||||
replace_profile_scope_with_specific_claims(req)
|
||||
@scopes = req.scope.map {|scope|
|
||||
scope.tap do |scope_name|
|
||||
req.invalid_scope! "Unknown scope: #{scope_name}" unless scopes.include? scope_name
|
||||
req.invalid_scope! "Unknown scope: #{scope_name}" unless auth_scopes.include? scope_name
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def auth_scopes
|
||||
Api::OpenidConnect::Authorization::SCOPES
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -19,6 +19,14 @@ module Api
|
|||
end
|
||||
end
|
||||
|
||||
def replace_profile_scope_with_specific_claims(req)
|
||||
# Empty
|
||||
end
|
||||
|
||||
def build_from_request_object(req)
|
||||
# Empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def approved!(req, res)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,35 @@ module Api
|
|||
module OpenidConnect
|
||||
module AuthorizationPoint
|
||||
class EndpointStartPoint < Endpoint
|
||||
def build_from_request_object(req)
|
||||
request_object = build_request_object(req)
|
||||
return unless request_object
|
||||
claims = request_object.raw_attributes.with_indifferent_access[:claims].try(:[], :userinfo).try(:keys)
|
||||
return unless claims
|
||||
req.update_param("scope", req.scope + claims)
|
||||
end
|
||||
|
||||
def handle_response_type(req, _res)
|
||||
@response_type = req.response_type
|
||||
end
|
||||
|
||||
def replace_profile_scope_with_specific_claims(req)
|
||||
profile_claims = %w(sub aud name nickname profile picture)
|
||||
scopes_as_claims = req.scope.map { |scope| scope == "profile" ? profile_claims : [scope] }.flatten!.uniq
|
||||
req.update_param("scope", scopes_as_claims)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_request_object(req)
|
||||
if req.request_uri.present?
|
||||
OpenIDConnect::RequestObject.fetch req.request_uri
|
||||
elsif req.request.present?
|
||||
OpenIDConnect::RequestObject.decode req.request
|
||||
else
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -22,6 +22,41 @@ describe Api::OpenidConnect::AuthorizationsController, type: :controller do
|
|||
end
|
||||
end
|
||||
|
||||
context "using claims" do
|
||||
it "should return a form page" do
|
||||
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
|
||||
scope: "openid", claims: "{\"userinfo\": {\"name\": {\"essential\": true}}}", nonce: SecureRandom.hex(16),
|
||||
state: SecureRandom.hex(16)
|
||||
expect(response.body).to match("Diaspora Test Client")
|
||||
end
|
||||
end
|
||||
|
||||
context "as a request object" do
|
||||
it "should return a form page" do
|
||||
header = JWT.encoded_header("none")
|
||||
payload_hash = { client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
|
||||
scope: "openid", nonce: "hello", state: "hello", claims: { userinfo: { name: { essential: true } } } }
|
||||
payload = JWT.encoded_payload(JSON.parse(payload_hash.to_json))
|
||||
request_object = header + "." + payload + "."
|
||||
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
|
||||
scope: "openid", nonce: "hello", state: "hello", request: request_object
|
||||
expect(response.body).to match("Diaspora Test Client")
|
||||
end
|
||||
end
|
||||
|
||||
context "as a request object with no claims" do
|
||||
it "should return a form page" do
|
||||
header = JWT.encoded_header("none")
|
||||
payload_hash = { client_id: client.client_id, redirect_uri: "http://localhost:3000/",
|
||||
response_type: "id_token", scope: "openid", nonce: "hello", state: "hello" }
|
||||
payload = JWT.encoded_payload(JSON.parse(payload_hash.to_json))
|
||||
request_object = header + "." + payload + "."
|
||||
get :new, client_id: client.client_id, redirect_uri: "http://localhost:3000/", response_type: "id_token",
|
||||
scope: "openid", nonce: "hello", state: "hello", request: request_object
|
||||
expect(response.body).to match("Diaspora Test Client")
|
||||
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",
|
||||
|
|
|
|||
|
|
@ -348,19 +348,19 @@ FactoryGirl.define do
|
|||
factory :auth_with_read, class: Api::OpenidConnect::Authorization do
|
||||
o_auth_application
|
||||
user
|
||||
scopes %w(openid read)
|
||||
scopes %w(openid sub aud profile picture nickname name read)
|
||||
end
|
||||
|
||||
factory :auth_with_read_and_ppid, class: Api::OpenidConnect::Authorization do
|
||||
association :o_auth_application, factory: :o_auth_application_with_ppid
|
||||
user
|
||||
scopes %w(openid read)
|
||||
scopes %w(openid sub aud profile picture nickname name read)
|
||||
end
|
||||
|
||||
factory :auth_with_read_and_write, class: Api::OpenidConnect::Authorization do
|
||||
o_auth_application
|
||||
user
|
||||
scopes %w(openid read write)
|
||||
scopes %w(openid sub aud profile picture nickname name read write)
|
||||
end
|
||||
|
||||
# Factories for the DiasporaFederation-gem
|
||||
|
|
|
|||
Loading…
Reference in a new issue