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 )