Lemonldap-NG-Portal
view release on metacpan or search on metacpan
lib/Lemonldap/NG/Portal/Plugins/OIDC/DynamicRegistration.pm view on Meta::CPAN
# Get client metadata first (needed for hook)
my $client_metadata_json = $req->content;
return $self->p->sendError( $req, 'Missing POST data', 400 )
unless ($client_metadata_json);
$self->logger->debug("Client metadata received: $client_metadata_json");
my $client_metadata = $self->oidc->decodeJSON($client_metadata_json)
or return $self->p->sendError( $req, 'invalid_client_metadata', 400 );
my $registration_response = {};
# Check dynamic registration is allowed
unless ( $self->conf->{oidcServiceAllowDynamicRegistration} ) {
$self->logger->error("Dynamic registration is not allowed");
return $self->p->sendError( $req, 'server_error' );
}
# Let hooks filter registration requests
# PE_SENDRESPONSE means the hook took over the response
my $h = $self->p->processHook( $req, 'oidcGotRegistrationRequest',
$client_metadata );
return $req->response if $h == PE_SENDRESPONSE;
if ( $h != PE_OK ) {
$self->logger->error("oidcGotRegistrationRequest hook failed");
return $self->p->sendError( $req, 'invalid_request', 400 );
}
# Check redirect_uris
my $redirect_uris = $client_metadata->{redirect_uris};
unless ( $redirect_uris and ref($redirect_uris) eq 'ARRAY' ) {
$self->logger->error("Field redirect_uris (array) is mandatory");
return $self->p->sendError( $req, 'invalid_client_metadata', 400 );
}
else {
foreach (@$redirect_uris) {
if ( /^\s*((?:java|vb)script|data):/i
or $self->p->checkXSSAttack( 'redirect_uri', $_ ) )
{
$self->userLogger->error(
"Registration tried with a forbidden redirect_uri: $_");
return $self->p->sendError( $req, 'invalid_client_metadata',
400 );
}
}
}
# RP identifier
my $registration_time = time;
my $rp = "register-$registration_time";
# Handle token_endpoint_auth_method
my $token_endpoint_auth_method =
$client_metadata->{token_endpoint_auth_method} || 'client_secret_basic';
my $is_public = ( $token_endpoint_auth_method eq 'none' );
my $needs_secret =
$token_endpoint_auth_method =~ /^client_secret_(?:basic|post|jwt)$/;
# Validate token_endpoint_auth_method value
unless ( $is_public
or $needs_secret
or $token_endpoint_auth_method eq 'private_key_jwt' )
{
$self->logger->error(
"Unsupported token_endpoint_auth_method: $token_endpoint_auth_method"
);
return $self->p->sendError( $req, 'invalid_client_metadata', 400 );
}
# Reject public clients when only confidential clients are allowed
if ( !$needs_secret
and $self->conf->{oidcServiceAllowDynamicRegistration} < 2 )
{
$self->logger->error( "Public client registration is not allowed "
. "(token_endpoint_auth_method=$token_endpoint_auth_method)" );
return $self->p->sendError( $req, 'invalid_client_metadata', 400 );
}
# private_key_jwt requires jwks_uri
if ( $token_endpoint_auth_method eq 'private_key_jwt'
and not $client_metadata->{jwks_uri} )
{
$self->logger->error(
"private_key_jwt requires jwks_uri in registration request");
return $self->p->sendError( $req, 'invalid_client_metadata', 400 );
}
# Generate Client ID and secret
my $client_id = random_string(RS_MSK);
my $client_secret = $needs_secret ? random_string(RS_MSK) : undef;
my $default_signing_key_type = $self->oidc->_getKeyType(
$self->oidc->get_public_key("default-oidc-sig") );
# Register known parameters
my $client_name =
$client_metadata->{client_name} || "Self registered client";
my $logo_uri = $client_metadata->{logo_uri};
my $id_token_signed_response_alg =
$client_metadata->{id_token_signed_response_alg}
|| ( $default_signing_key_type eq 'EC' ? 'ES256' : 'RS256' );
my $userinfo_signed_response_alg =
$client_metadata->{userinfo_signed_response_alg};
my $request_uris = $client_metadata->{request_uris};
my $backchannel_logout_uri = $client_metadata->{backchannel_logout_uri};
my $backchannel_logout_session_required =
$client_metadata->{backchannel_logout_session_required};
my $frontchannel_logout_uri = $client_metadata->{frontchannel_logout_uri};
my $frontchannel_logout_session_required =
$client_metadata->{frontchannel_logout_session_required};
my $jwksUri = $client_metadata->{jwks_uri};
my $encryptedResponseAlg =
$client_metadata->{id_token_encrypted_response_alg};
my $encryptedResponseEnc =
$client_metadata->{id_token_encrypted_response_enc};
my $userInfoEncAlg = $client_metadata->{userinfo_encrypted_response_alg};
my $userInfoEncEnc = $client_metadata->{userinfo_encrypted_response_enc};
my $introspectionSignAlg =
$client_metadata->{introspection_signed_response_alg};
my $introspectionEncAlg =
$client_metadata->{introspection_encrypted_response_alg};
my $introspectionEncEnc =
$client_metadata->{introspection_encrypted_response_enc};
# Register RP in global configuration
my $conf = $self->confAcc->getConf( { raw => 1, noCache => 1 } );
$conf->{cfgAuthor} = "OpenID Connect Registration ($client_name)";
$conf->{cfgAuthorIP} = $source_ip;
$conf->{cfgVersion} = $VERSION;
# Build RP options from client metadata
my $rp_options = {
oidcRPMetaDataOptionsClientID => $client_id,
oidcRPMetaDataOptionsDisplayName => $client_name,
oidcRPMetaDataOptionsIcon => $logo_uri,
oidcRPMetaDataOptionsIDTokenSignAlg => $id_token_signed_response_alg,
oidcRPMetaDataOptionsRedirectUris => join( ' ', @$redirect_uris ),
};
$rp_options->{oidcRPMetaDataOptionsClientSecret} = $client_secret
if defined $client_secret;
$rp_options->{oidcRPMetaDataOptionsPublic} = 1 if $is_public;
$rp_options->{oidcRPMetaDataOptionsRequirePKCE} = 2;
$rp_options->{oidcRPMetaDataOptionsRequestUris} =
( run in 0.695 second using v1.01-cache-2.11-cpan-13bb782fe5a )