From 33bc52b6191cab84aa984b88d050093f9cc44406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Oudot?= Date: Mon, 16 Mar 2015 17:00:56 +0000 Subject: [PATCH] Skeleton to manage different OIDC response types (#184) --- .../NG/Portal/IssuerDBOpenIDConnect.pm | 281 ++++++++++-------- 1 file changed, 158 insertions(+), 123 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/IssuerDBOpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/IssuerDBOpenIDConnect.pm index b79cce33f..29323d050 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/IssuerDBOpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/IssuerDBOpenIDConnect.pm @@ -288,7 +288,8 @@ sub issuerForAuthUser { # Get and save parameters my $oidc_request = {}; - foreach my $param (qw/response_type scope client_id state redirect_uri/) + foreach + my $param (qw/response_type scope client_id state redirect_uri nonce/) { $oidc_request->{$param} = $self->getHiddenFormValue($param) || $self->param($param); @@ -298,145 +299,160 @@ sub issuerForAuthUser { $self->setHiddenFormValue( $param, $oidc_request->{$param} ); } + # Detect requested flow + my $response_type = $oidc_request->{'response_type'}; + + my $response_types = { + "code" => "authorizationcode", + "id_token" => "implicit", + "id_token token" => "implicit", + "code id_token" => "hybrid", + "code token" => "hybrid", + "code id_token token" => "hybrid", + }; + + my $flow = $response_types->{$response_type}; + + unless ($flow) { + $self->lmLog( "Unknown response type: $response_type", 'error' ); + return PE_ERROR; + } + $self->lmLog( + "OIDC $flow flow requested (response type: $response_type)", + 'debug' ); + # TODO check all required parameters - # TODO validate parameters against OAuth 2.0 spec + if ( $flow eq "implicit" and not defined $oidc_request->{'nonce'} ) { + $self->lmLog( "Nonce is required for implicit flow", 'error' ); + return PE_ERROR; + } - # Authorization Code Flow - if ( $oidc_request->{'response_type'} eq "code" ) { - $self->lmLog( "OIDC auhtorization code flow requested", 'debug' ); + # Check openid scope + unless ( $oidc_request->{'scope'} =~ /\bopenid\b/ ) { + $self->lmLog( "No openid scope found", 'debug' ); - # Check openid scope - unless ( $oidc_request->{'scope'} =~ /\bopenid\b/ ) { - $self->lmLog( "No openid scope found", 'debug' ); + #TODO manage standard OAuth request + return PE_OK; + } - #TODO manage standard OAuth request - return PE_OK; - } + # Check client_id + $self->lmLog( "Request from client id " . $oidc_request->{'client_id'}, + 'debug' ); - # Check client_id + # Verify that client_id is registered in configuration + my $rp = $self->getRP( $oidc_request->{'client_id'} ); + + unless ($rp) { $self->lmLog( - "Request from client id " . $oidc_request->{'client_id'}, + "No registered Relying Party found with client_id " + . $oidc_request->{'client_id'}, + 'error' + ); + return PE_ERROR; + } + else { + $self->lmLog( + "Client id " . $oidc_request->{'client_id'} . " match RP $rp", 'debug' ); + } - # Verify that client_id is registered in configuration - my $rp = $self->getRP( $oidc_request->{'client_id'} ); + # Obtain consent + my $ask_for_consent = 1; + if ( $self->{sessionInfo}->{"_oidc_consent_time_$rp"} + and $self->{sessionInfo}->{"_oidc_consent_scope_$rp"} ) + { + $ask_for_consent = 0; + my $consent_time = $self->{sessionInfo}->{"_oidc_consent_time_$rp"}; + my $consent_scope = + $self->{sessionInfo}->{"_oidc_consent_scope_$rp"}; - unless ($rp) { - $self->lmLog( - "No registered Relying Party found with client_id " - . $oidc_request->{'client_id'}, - 'error' - ); - return PE_ERROR; - } - else { - $self->lmLog( - "Client id " - . $oidc_request->{'client_id'} - . " match RP $rp", - 'debug' - ); - } - - # Obtain consent - my $ask_for_consent = 1; - if ( $self->{sessionInfo}->{"_oidc_consent_time_$rp"} - and $self->{sessionInfo}->{"_oidc_consent_scope_$rp"} ) - { - $ask_for_consent = 0; - my $consent_time = - $self->{sessionInfo}->{"_oidc_consent_time_$rp"}; - my $consent_scope = - $self->{sessionInfo}->{"_oidc_consent_scope_$rp"}; - - $self->lmLog( + $self->lmLog( "Consent already given for Relying Party $rp (time: $consent_time, scope: $consent_scope)", - 'debug' - ); + 'debug' + ); - # Check accepted scope - foreach my $requested_scope ( - split( /\s+/, $oidc_request->{'scope'} ) ) - { - if ( $consent_scope =~ /\b$requested_scope\b/ ) { - $self->lmLog( "Scope $requested_scope already accepted", - 'debug' ); - } - else { - $self->lmLog( -"Scope $requested_scope was not previously accepted", - 'debug' - ); - $ask_for_consent = 1; - last; - } - } - } - if ($ask_for_consent) { - if ( $self->param('confirm') == 1 ) { - $self->updatePersistentSession( - { "_oidc_consent_time_$rp" => time } ); - $self->updatePersistentSession( - { - "_oidc_consent_scope_$rp" => - $oidc_request->{'scope'} - } - ); - $self->lmLog( "Consent given for Relying Party $rp", + # Check accepted scope + foreach + my $requested_scope ( split( /\s+/, $oidc_request->{'scope'} ) ) + { + if ( $consent_scope =~ /\b$requested_scope\b/ ) { + $self->lmLog( "Scope $requested_scope already accepted", 'debug' ); } else { - $self->lmLog( "Obtain user consent for Relying Party $rp", + $self->lmLog( + "Scope $requested_scope was not previously accepted", 'debug' ); - - my $display_name = $self->{oidcRPMetaDataOptions}->{$rp} - ->{oidcRPMetaDataOptionsDisplayName}; - my $icon = $self->{oidcRPMetaDataOptions}->{$rp} - ->{oidcRPMetaDataOptionsIcon}; - my $portalPath = $self->{portal}; - $portalPath =~ s#^https?://[^/]+/?#/#; - $portalPath =~ s#[^/]+\.pl$##; - - $self->info(''); - $self->{activeTimer} = 0; - return PE_CONFIRM; + $ask_for_consent = 1; + last; } } + } + if ($ask_for_consent) { + if ( $self->param('confirm') == 1 ) { + $self->updatePersistentSession( + { "_oidc_consent_time_$rp" => time } ); + $self->updatePersistentSession( + { + "_oidc_consent_scope_$rp" => $oidc_request->{'scope'} + } + ); + $self->lmLog( "Consent given for Relying Party $rp", 'debug' ); + } + else { + $self->lmLog( "Obtain user consent for Relying Party $rp", + 'debug' ); + + my $display_name = $self->{oidcRPMetaDataOptions}->{$rp} + ->{oidcRPMetaDataOptionsDisplayName}; + my $icon = $self->{oidcRPMetaDataOptions}->{$rp} + ->{oidcRPMetaDataOptionsIcon}; + my $portalPath = $self->{portal}; + $portalPath =~ s#^https?://[^/]+/?#/#; + $portalPath =~ s#[^/]+\.pl$##; + + $self->info(''); + $self->{activeTimer} = 0; + return PE_CONFIRM; + } + } + + # Authorization Code Flow + if ( $flow eq "authorizationcode" ) { # Generate code my $codeSession = $self->getOpenIDConnectSession(); @@ -465,6 +481,25 @@ sub issuerForAuthUser { $self->_sub('autoRedirect'); } + + # Implicit Flow + if ( $flow eq "implicit" ) { + + #TODO + + return PE_ERROR; + } + + # Hybrid Flow + if ( $flow eq "hybrid" ) { + + #TODO + + return PE_ERROR; + } + + $self->lmLog( "No flow has been selected", 'debug' ); + return PE_OK; } # TOKEN