Public/Grant-SPPermissionAppConsent.ps1

function Grant-SPPermissionAppConsent {
    <#
    .SYNOPSIS
        Customer onboarding for a multi-tenant app: builds the admin-consent URL and can
        verify the consent with a live connection test.
    .DESCRIPTION
        With a multi-tenant app registration (Register-SPPermissionApp -MultiTenant) a
        customer is onboarded by a single click of their admin on the consent URL - no app
        registration or certificate in the customer tenant. This helper builds the URL,
        optionally opens it in the browser and, with -TestSiteUrl, verifies afterwards that
        certificate authentication against the customer tenant works.
    .PARAMETER ClientId
        Client id of the multi-tenant app registration.
    .PARAMETER Tenant
        The CUSTOMER tenant (kunde.onmicrosoft.com or tenant id).
    .PARAMETER OpenBrowser
        Opens the consent URL in the default browser (for consenting on behalf of the
        customer or forwarding the session).
    .PARAMETER TestSiteUrl
        A site in the customer tenant to verify the consent with a certificate login.
    .EXAMPLE
        Grant-SPPermissionAppConsent -ClientId $appId -Tenant kunde.onmicrosoft.com
    .EXAMPLE
        Grant-SPPermissionAppConsent -ClientId $appId -Tenant kunde.onmicrosoft.com `
            -TestSiteUrl https://kunde.sharepoint.com -CertificatePath .\spm.pfx
    #>

    [CmdletBinding()]
    [OutputType([pscustomobject])]
    param(
        [Parameter(Mandatory, Position = 0)]
        [string]$ClientId,

        [Parameter(Mandatory, Position = 1)]
        [string]$Tenant,

        [switch]$OpenBrowser,

        [string]$TestSiteUrl,

        [string]$CertificatePath,

        [securestring]$CertificatePassword,

        [string]$Thumbprint
    )

    $consentUrl = "https://login.microsoftonline.com/$Tenant/adminconsent?client_id=$ClientId"

    if ($OpenBrowser) {
        Start-Process $consentUrl
    }

    $testResult = $null
    if ($TestSiteUrl) {
        if (-not $CertificatePath -and -not $Thumbprint) {
            throw '-TestSiteUrl requires -CertificatePath or -Thumbprint.'
        }
        try {
            $auth = @{
                ClientId            = $ClientId
                Tenant              = $Tenant
                CertificatePath     = $CertificatePath
                CertificatePassword = $CertificatePassword
                Thumbprint          = $Thumbprint
                Interactive         = $false
            }
            Connect-SPMSite -Url $TestSiteUrl -Auth $auth
            $web = Invoke-SPMWithRetry { Get-PnPWeb }
            $testResult = [pscustomobject]@{ Success = $true; SiteTitle = $web.Title; Error = $null }
            Write-Verbose "Consent verified - connected to '$($web.Title)'."
        }
        catch {
            $testResult = [pscustomobject]@{ Success = $false; SiteTitle = $null; Error = $_.Exception.Message }
            Write-Warning "Consent test failed: $($_.Exception.Message)"
        }
    }

    return [pscustomobject]@{
        ClientId   = $ClientId
        Tenant     = $Tenant
        ConsentUrl = $consentUrl
        TestResult = $testResult
    }
}