From c09d2c4e00e368ef5a895758f9672742fc7c6a98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Oudot?= Date: Sat, 22 Nov 2014 08:53:17 +0000 Subject: [PATCH] Check ID Token validity (#183) --- .../Lemonldap/NG/Portal/AuthOpenIDConnect.pm | 15 ++++- .../lib/Lemonldap/NG/Portal/_OpenIDConnect.pm | 61 ++++++++++++++++++- 2 files changed, 74 insertions(+), 2 deletions(-) diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthOpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthOpenIDConnect.pm index c83d44296..888b7735d 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthOpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/AuthOpenIDConnect.pm @@ -119,6 +119,9 @@ sub extractFormInfo { $self->lmLog( "Token response is not valid", 'error' ); return PE_ERROR; } + else { + $self->lmLog( "Token response is valid", 'debug' ); + } my $access_token = $json->{access_token}; my $id_token = $json->{id_token}; @@ -140,12 +143,22 @@ sub extractFormInfo { $self->lmLog( "JWT signature check disabled", 'debug' ); } - # Get ID token content my $id_token_payload = $self->extractJWT($id_token)->[1]; my $id_token_payload_hash = $self->decodeJSON( decode_base64($id_token_payload) ); + # Check validity of ID Token + unless ( $self->checkIDTokenValidity( $op, $id_token_payload_hash ) ) { + $self->lmLog( "ID Token not valid", 'error' ); + return PE_ERROR; + } + else { + $self->lmLog( "ID Token is valid", 'debug' ); + $self->_dump($id_token_payload_hash); + } + + # Get user id defined in 'sub' field my $user_id = $id_token_payload_hash->{sub}; # Remember tokens diff --git a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_OpenIDConnect.pm b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_OpenIDConnect.pm index 935bf046a..1579bed4b 100644 --- a/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_OpenIDConnect.pm +++ b/lemonldap-ng-portal/lib/Lemonldap/NG/Portal/_OpenIDConnect.pm @@ -58,7 +58,7 @@ sub loadOPs { $self->decodeJSON( $self->{oidcOPMetaDataJWKS}->{$_} ); } - $oidcCache->{_oidcOPList} = $self->{_oidcList} unless $no_cache; + $oidcCache->{_oidcOPList} = $self->{_oidcOPList} unless $no_cache; return 1; } @@ -205,6 +205,65 @@ sub checkTokenResponseValidity { return 1; } +## @method boolean checkIDTokenValidity(String op, HashRef id_token) +# Check validity of ID Token +# @param op OpenIP Provider configuration key +# @param id_token ID Token payload as HashRef +# return boolean 1 if the token is valid, 0 else +sub checkIDTokenValidity { + my ( $self, $op, $id_token ) = splice @_; + + my $client_id = + $self->{oidcOPMetaDataOptions}->{$op}->{oidcOPMetaDataOptionsClientID}; + + # Check issuer + unless ( $id_token->{iss} eq $self->{_oidcOPList}->{$op}->{conf}->{issuer} ) + { + $self->lmLog( "Issuer mismatch", 'error' ); + return 0; + } + + # Check audience + if ( ref $id_token->{aud} ) { + my @audience = @{ $id_token->{aud} }; + unless ( grep $_ eq $client_id ) { + $self->lmLog( "Client ID not found in audience array", 'error' ); + return 0; + } + + if ( $#audience > 1 ) { + unless ( $id_token->{azp} eq $client_id ) { + $self->lmLog( + "More than one audiance, and azp not equal to client ID", + 'error' ); + return 0; + } + } + } + else { + unless ( $id_token->{aud} eq $client_id ) { + $self->lmLog( "Audience mismatch", 'error' ); + return 0; + } + } + + # Check time + unless ( time < $id_token->{exp} ) { + $self->lmLog( "ID token expired", 'error' ); + return 0; + } + + # TODO check iat + + # TODO check nonce + + # TODO check acr + + # TODO check auth_time + + return 1; +} + ## @method String getUserInfo(String op, String access_token) # Get UserInfo response # @param op OpenIP Provider configuration key