Commit 9520bef4 authored by Clément OUDOT's avatar Clément OUDOT

Manager UserInfo signature (JWT response) (#184)

parent e84963ac
......@@ -339,7 +339,7 @@ sub cstruct {
},
oidcRPMetaDataOptions => {
_nodes => [
qw(oidcRPMetaDataOptionsAuthentication oidcRPMetaDataOptionsDisplay oidcRPMetaDataOptionsUserIDAttr oidcRPMetaDataOptionsIDTokenSignAlg oidcRPMetaDataOptionsIDTokenExpiration oidcRPMetaDataOptionsAccessTokenExpiration oidcRPMetaDataOptionsRedirectUris)
qw(oidcRPMetaDataOptionsAuthentication oidcRPMetaDataOptionsDisplay oidcRPMetaDataOptionsUserIDAttr oidcRPMetaDataOptionsIDTokenSignAlg oidcRPMetaDataOptionsIDTokenExpiration oidcRPMetaDataOptionsAccessTokenExpiration oidcRPMetaDataOptionsRedirectUris oidcRPMetaDataOptionsUserInfoSignAlg)
],
oidcRPMetaDataOptionsAuthentication => {
_nodes => [
......@@ -369,6 +369,8 @@ sub cstruct {
"int:/oidcRPMetaDataOptions/$k2/oidcRPMetaDataOptionsAccessTokenExpiration",
oidcRPMetaDataOptionsRedirectUris =>
"text:/oidcRPMetaDataOptions/$k2/oidcRPMetaDataOptionsRedirectUris",
oidcRPMetaDataOptionsUserInfoSignAlg =>
"text:/oidcRPMetaDataOptions/$k2/oidcRPMetaDataOptionsUserInfoSignAlg",
},
},
},
......
......@@ -303,45 +303,46 @@ sub en {
oidcRPMetaDataOptionsDisplayName => 'Display name',
oidcRPMetaDataOptionsIcon => 'Logo',
oidcRPMetaDataOptionsIDTokenExpiration => 'ID Token expiration',
oidcRPMetaDataOptionsIDTokenSignAlg => 'ID Token signature algorithm',
oidcRPMetaDataOptionsRedirectUris => 'Redirection addresses',
oidcRPMetaDataOptionsUserIDAttr => 'User ID attribute',
oidcRPStateTimeout => 'State session timeout',
oidcServiceMetaData => 'OpenID Connect Service',
oidcServiceMetaDataAuthnContext => 'Authentication context',
oidcServiceMetaDataAuthorizeURI => 'Autorization',
oidcServiceMetaDataEndPoints => 'End points',
oidcServiceMetaDataEndSessionURI => 'End session',
oidcServiceMetaDataIssuer => 'Issuer identifier',
oidcServiceMetaDataJWKSURI => 'JWKS',
oidcServiceMetaDataRegistrationURI => 'Registration',
oidcServiceMetaDataSecurity => 'Security',
oidcServiceMetaDataTokenURI => 'Token',
oidcServiceMetaDataUserInfoURI => 'User Info',
oidcServicePrivateKeySig => 'Signing private key',
oidcServicePublicKeySig => 'Signing public key',
openIdAttr => 'OpenID login',
openIdAuthnLevel => 'Authentication level',
openIdExportedVars => 'Exported variables',
openIdIDPList => 'Authorizated domains',
openIdIssuerSecret => 'Secret token',
openIdParams => 'OpenID parameters',
openIdSecret => 'Secret token',
openIdSreg => 'SREG mapping',
openIdSreg_fullname => 'Full name',
openIdSreg_nickname => 'Nick name',
openIdSreg_language => 'Language',
openIdSreg_postcode => 'Postal code',
openIdSreg_timezone => 'Timezone',
openIdSreg_country => 'Country',
openIdSreg_gender => 'Gender',
openIdSreg_email => 'Email',
openIdSreg_dob => 'Date of birth',
openIdSPList => 'Authorizated domains',
passwordDB => 'Password module',
passwordManagement => 'Password management',
persistentSessions => 'Persistent sessions',
persistentStorage => 'Apache::Session module',
oidcRPMetaDataOptionsIDTokenSignAlg => 'ID Token signature algorithm',
oidcRPMetaDataOptionsRedirectUris => 'Redirection addresses',
oidcRPMetaDataOptionsUserIDAttr => 'User ID attribute',
oidcRPMetaDataOptionsUserInfoSignAlg => 'UserInfo signature algorithm',
oidcRPStateTimeout => 'State session timeout',
oidcServiceMetaData => 'OpenID Connect Service',
oidcServiceMetaDataAuthnContext => 'Authentication context',
oidcServiceMetaDataAuthorizeURI => 'Autorization',
oidcServiceMetaDataEndPoints => 'End points',
oidcServiceMetaDataEndSessionURI => 'End session',
oidcServiceMetaDataIssuer => 'Issuer identifier',
oidcServiceMetaDataJWKSURI => 'JWKS',
oidcServiceMetaDataRegistrationURI => 'Registration',
oidcServiceMetaDataSecurity => 'Security',
oidcServiceMetaDataTokenURI => 'Token',
oidcServiceMetaDataUserInfoURI => 'User Info',
oidcServicePrivateKeySig => 'Signing private key',
oidcServicePublicKeySig => 'Signing public key',
openIdAttr => 'OpenID login',
openIdAuthnLevel => 'Authentication level',
openIdExportedVars => 'Exported variables',
openIdIDPList => 'Authorizated domains',
openIdIssuerSecret => 'Secret token',
openIdParams => 'OpenID parameters',
openIdSecret => 'Secret token',
openIdSreg => 'SREG mapping',
openIdSreg_fullname => 'Full name',
openIdSreg_nickname => 'Nick name',
openIdSreg_language => 'Language',
openIdSreg_postcode => 'Postal code',
openIdSreg_timezone => 'Timezone',
openIdSreg_country => 'Country',
openIdSreg_gender => 'Gender',
openIdSreg_email => 'Email',
openIdSreg_dob => 'Date of birth',
openIdSPList => 'Authorizated domains',
passwordDB => 'Password module',
passwordManagement => 'Password management',
persistentSessions => 'Persistent sessions',
persistentStorage => 'Apache::Session module',
persistentStorageOptions => 'Apache::Session module parameters',
port => 'Port',
portal => 'URL',
......@@ -879,8 +880,10 @@ sub fr {
"Expiration des jetons d'identité",
oidcRPMetaDataOptionsIDTokenSignAlg =>
"Algorithme de signature des jetons d'identité",
oidcRPMetaDataOptionsRedirectUris => 'Adresses de redirection',
oidcRPMetaDataOptionsUserIDAttr => "Attribut de l'identifiant",
oidcRPMetaDataOptionsRedirectUris => 'Adresses de redirection',
oidcRPMetaDataOptionsUserIDAttr => "Attribut de l'identifiant",
oidcRPMetaDataOptionsUserInfoSignAlg =>
"Algorithme de signature des informations utilisateur",
oidcRPStateTimeout => 'Durée d\'une session state',
oidcServiceMetaData => "Service OpenID Connect",
oidcServiceMetaDataAuthnContext => "Contextes d'authentifiation",
......
......@@ -50,6 +50,9 @@ $configuration->{id_token_signing_alg_values_supported} =
# $configuration->{id_token_encryption_alg_values_supported}
# $configuration->{id_token_encryption_enc_values_supported}
$configuration->{userinfo_signing_alg_values_supported} =
[qw/none HS256 HS384 HS512 RS256 RS384 RS512/];
# $configuration->{userinfo_encryption_alg_values_supported}
# $configuration->{userinfo_encryption_enc_values_supported}
# $configuration->{request_object_signing_alg_values_supported}
......
......@@ -327,7 +327,19 @@ sub issuerForUnAuthUser {
my $userinfo_response =
$self->buildUserInfoResponse( $scope, $rp, $user_session_id );
$self->returnJSON($userinfo_response);
my $userinfo_sign_alg = $self->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsUserInfoSignAlg};
unless ($userinfo_sign_alg) {
$self->returnJSON($userinfo_response);
}
else {
my $userinfo_jwt =
$self->createJWT( $userinfo_response, $userinfo_sign_alg, $rp );
print $self->header('application/jwt');
print $userinfo_jwt;
$self->lmLog( "Return UserInfo as JWT: $userinfo_jwt", 'debug' );
}
$self->lmLog( "UserInfo response sent", 'debug' );
......
......@@ -1189,30 +1189,25 @@ sub buildUserInfoResponse {
return $userinfo_response;
}
## @method String createIDToken(hashref payload, String rp)
# Return ID Token
# @param payload ID Token content
## @method String createJWT(hashref payload, String alg, String rp)
# Return JWT
# @param payload JWT content
# @param alg Signature algorithm
# @param rp Internal Relying Party identifier
# @return String id_token ID Token as JWT
sub createIDToken {
my ( $self, $payload, $rp ) = splice @_;
# Get signature algorithm
my $alg = $self->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
$self->lmLog( "JWT signature algorithm: $alg", 'debug' );
# @return String jwt JWT
sub createJWT {
my ( $self, $payload, $alg, $rp ) = splice @_;
# Payload encoding
my $id_token_payload = encode_base64( encode_json($payload), "" );
my $jwt_payload = encode_base64( encode_json($payload), "" );
# ID Token header
my $id_token_header_hash = { typ => "JWT", alg => $alg };
my $id_token_header =
encode_base64( encode_json($id_token_header_hash), "" );
# JWT header
my $jwt_header_hash = { typ => "JWT", alg => $alg };
my $jwt_header = encode_base64( encode_json($jwt_header_hash), "" );
if ( $alg eq "none" ) {
return $id_token_header . "." . $id_token_payload;
return $jwt_header . "." . $jwt_payload;
}
if ( $alg eq "HS256" or $alg eq "HS384" or $alg eq "HS512" ) {
......@@ -1225,24 +1220,21 @@ sub createIDToken {
my $digest;
if ( $alg eq "HS256" ) {
$digest =
hmac_sha256_base64( $id_token_header . "." . $id_token_payload,
$digest = hmac_sha256_base64( $jwt_header . "." . $jwt_payload,
$client_secret );
}
if ( $alg eq "HS384" ) {
$digest =
hmac_sha384_base64( $id_token_header . "." . $id_token_payload,
$digest = hmac_sha384_base64( $jwt_header . "." . $jwt_payload,
$client_secret );
}
if ( $alg eq "HS512" ) {
$digest =
hmac_sha512_base64( $id_token_header . "." . $id_token_payload,
$digest = hmac_sha512_base64( $jwt_header . "." . $jwt_payload,
$client_secret );
}
return $id_token_header . "." . $id_token_payload . "." . $digest;
return $jwt_header . "." . $jwt_payload . "." . $digest;
}
if ( $alg eq "RS256" or $alg eq "RS384" or $alg eq "RS512" ) {
......@@ -1264,16 +1256,32 @@ sub createIDToken {
}
my $digest = encode_base64url(
$rsa_priv->sign( $id_token_header . "." . $id_token_payload ) );
$rsa_priv->sign( $jwt_header . "." . $jwt_payload ) );
return $id_token_header . "." . $id_token_payload . "." . $digest;
return $jwt_header . "." . $jwt_payload . "." . $digest;
}
$self->lmLog( "Algorithm $alg not supported to sign ID Token", 'debug' );
$self->lmLog( "Algorithm $alg not supported to sign JWT", 'debug' );
return;
}
## @method String createIDToken(hashref payload, String rp)
# Return ID Token
# @param payload ID Token content
# @param rp Internal Relying Party identifier
# @return String id_token ID Token as JWT
sub createIDToken {
my ( $self, $payload, $rp ) = splice @_;
# Get signature algorithm
my $alg = $self->{oidcRPMetaDataOptions}->{$rp}
->{oidcRPMetaDataOptionsIDTokenSignAlg};
$self->lmLog( "ID Token signature algorithm: $alg", 'debug' );
return $self->createJWT( $payload, $alg, $rp );
}
## @method String getFlowType(String response_type)
# Return flow type
# @param response_type Response type
......@@ -1522,6 +1530,10 @@ Return list of attributes authorized for a claim
Return Hash of UserInfo data
=head2 createJWT
Return JWT
=head2 createIDToken
Return ID Token
......
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