Introduction
It’s very common for your end-users to want to use Microsoft SSO to login to your client portal. In order for that to happen though, someone - either in your organization, or a privileged user within your customer’s organization - needs to approve the installation of the “MSP Process SSO” enterprise app. Depending on the security settings within your customer’s M365 tenant, they may not be able to approve the installation of the MSP Process SSO enterprise app - this KB article walks you through how to pre-approve it, so that your rollout of the Client Portal is as smooth and user-friendly as possible.How it works
Customer end-users sign in to the client portal (and it’s Teams app variant) through the MSP Process SSO enterprise application. By default, the first user to login user is asked to approve the app the first time, and in many tenants they aren’t allowed to. Granting admin consent in the customer’s Microsoft tenant approves the app for all of that tenant’s users at once, so nobody sees a prompt. Consent is granted once per customer tenant. You’ll likely use a combination of the two methods below - the Partner Center approach as the first step, and then the per-tenant Admin consent link for any outliers.App details
Option A: Admin consent link
Get the customer’s tenant ID (a GUID) or a verified domain such ascontoso.com .
Build the link below, replacing {TENANT} with the tenant ID or domain (in both places):
- https://login.microsoftonline.com/\{TENANT\}/adminconsent?client\_id=b38f995e-3812-43f1-97d5-5c4ff7578286&redirect\_uri=https%3A%2F%2Fapp.mspprocess.com%2F&state=\{TENANT\}
- Open the link while signed in as an admin of the customer tenant. This can be a customer admin, or you through GDAP. The account needs one of these roles in the customer tenant: Global Administrator, Privileged Role Administrator, Cloud Application Administrator, or Application Administrator.
- Review the consent screen. It shows the verified badge for MSP Process. Select Accept.
-
You are redirected to
https://app.mspprocess/com/withadmin_consent=Trueand the tenant ID in the address. That confirms it worked.
Option B: Script through Admin Relationships
If you manage many customers through Admin Relationships in the Microsoft Partner Center, the script below does Option A’s work for all of them in one run. LimitationThe script signs in with Microsoft’s “Microsoft Graph Command Line Tools” app. In customer tenants where that app hasn’t been authorized, sign-in fails withAADSTS90099 , and GDAP can’t work around it.
For those customers, use Option A. The script marks them Failed and includes the link for you.
Prerequisites
- An active Admin Relationship with each customer that includes Cloud Application Administrator (or Application Administrator).
- That role assigned to a security group in your tenant, with you as a member.
- PowerShell 7 and the Microsoft Graph SDK:
Download and Run the script
Save the PowerShell code below asGrant-MspProcessSsoConsent.ps1 :
<#.SYNOPSISPre-approves the “MSP Process SSO” enterprise app in every GDAP customer tenant,so end-users can sign in to the MSP Process client portal with Microsoft SSOwithout seeing a consent prompt.
.DESCRIPTIONRun by a technician in the MSP (partner) tenant. For each customer:1. Finds active GDAP relationships that include Cloud Application Administratoror Application Administrator (or uses the -TenantId list you supply).2. Signs in to the customer tenant through GDAP.3. Creates the MSP Process SSO service principal if it doesn’t exist.4. Grants tenant-wide (AllPrincipals) delegated consent for the scopes below.5. Verifies the result and writes a CSV report.Safe to re-run: existing service principals and grants are reused or updated.
.PARAMETER TenantIdOne or more customer tenant GUIDs. If omitted, tenants come from active GDAP relationships.
.PARAMETER OutputPathCSV report path. Default: .\MspProcessSso-Consent-<timestamp>.csv
.EXAMPLE.\Grant-MspProcessSsoConsent.ps1 -WhatIf.\Grant-MspProcessSsoConsent.ps1.\Grant-MspProcessSsoConsent.ps1 -TenantId 11111111-2222-3333-4444-555555555555#>[CmdletBinding(SupportsShouldProcess)]param([string[]]TenantId,\[string\]OutputPath = ”.\MspProcessSso-Consent-$(Get-Date -Format ‘yyyyMMdd-HHmmss’).csv”)
$ErrorActionPreference = ‘Stop’
# ---- MSP Process SSO settings --------------------------------------------Scopes = ‘openid profile email User.Read’ # keep identical to the app’s API permissions$RedirectUri = ‘https://app.mspprocess.com/’ # must be a registered Web redirect URI# ---------------------------------------------------------------------------
RoleCloudAppAdmin = ‘158c047a-c907-4556-b7ef-446551a6b5f7’$RoleAppAdmin = ‘9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3’
# Microsoft Graph PowerShell SDK (only the modules we need)foreach (m)) {throw “Missing module m -ErrorAction Stop}
function Get-ConsentUrl([string]enc = [uri]::EscapeDataString(Tenant/adminconsent?client_id=AppId&redirect\_uri=enc&state=$Tenant”}
# ---- Step 1: build the list of customer tenants ---------------------------$targets = @()
if (targets = TenantId | ForEach-Object \{ \[pscustomobject\]@\{ TenantId = _; Name = ‘(supplied)’ } }}else {Write-Host “Signing in to your PARTNER tenant to list GDAP relationships…” -ForegroundColor CyanConnect-MgGraph -Scopes ‘DelegatedAdminRelationship.Read.All’ -NoWelcome -ContextScope Process
byTenant = @{}foreach (rels) {r.Customer.TenantIdif (-not roles = @(hasRole = (RoleCloudAppAdmin) -or (RoleAppAdmin)if (-not tid)) {tid] = [pscustomobject]@{ TenantId = r.Customer.DisplayName; HasRole = hasRole) { tid].HasRole = $true }}Disconnect-MgGraph | Out-Null
byTenant.Values | Sort-Object NameWrite-Host (“Found {0} customer tenants with active GDAP.” -f $targets.Count)}
# ---- Step 2: process each customer ---------------------------------------$results = New-Object System.Collections.Generic.List[object]
foreach (targets) {row = \[ordered\]@\{Customer = t.Name; TenantId = (($t.TenantId)]” -ForegroundColor Cyan
if (t.HasRole) {row.Detail = ‘GDAP relationship lacks Cloud Application Administrator / Application Administrator’t.TenantIdWrite-Warning results.Add([pscustomobject]$row); continue}
if (-not t.TenantId, “Create service principal and grant tenant-wide consent for row.Status = ‘WhatIf’results.Add(\[pscustomobject\]row); continue}
try {Connect-MgGraph -TenantId $t.TenantId `-Scopes ‘Application.ReadWrite.All’, ‘DelegatedPermissionGrant.ReadWrite.All’ `-NoWelcome -ContextScope Process
# Service principal for MSP Process SSOAppId’” | Select-Object -First 1falseif (-not sp = New-MgServicePrincipal -AppId created = $trueWrite-Host ” Created enterprise app.”} else { Write-Host ” Enterprise app already present.” }
# Microsoft Graph service principal in this tenantGraphAppId’” | Select-Object -First 1
# Tenant-wide delegated grant (retry to ride out replication delay after creation)nullfor (i -le 6; grant = Get-MgOauth2PermissionGrant -Filter “clientId eq ‘sp.Id)’ and resourceId eq ‘graph.Id)’” |Where-Object ConsentType -eq ‘AllPrincipals’ | Select-Object -First 1if (-not grant = New-MgOauth2PermissionGrant -ClientId graph.Id -Scope grant.Scope -split ‘\s+’ | Where-Object { \_ \}) | Sort-Object) -join ' ' \`-ne ((Scopes -split ‘\s+’) | Sort-Object) -join ’ ’) {Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId ScopesWrite-Host ” Updated existing grant scopes.”}else { Write-Host ” Consent already granted.” }break}catch {if (i)}}
# Verify(verify) { throw ‘Verification failed: no AllPrincipals grant found after granting.’ }
created) { ‘Created+Consented’ } else { ‘Consented’ }sp.Idverify.Scope.Trim())}catch {row.Detail = row.Detail -match ‘AADSTS90099’) {row.FallbackConsentUrl = Get-ConsentUrl (results.Add([pscustomobject]$row)}
# ---- Step 3: report --------------------------------------------------------OutputPath -NoTypeInformation -Encoding UTF8Write-Host “`nSummary:” -ForegroundColor Green_.Name, OutputPath”Write-Host “Send the TenantId column to MSP Process (or register it in the portal) so sign-ins can be matched to the right customer.”if (_.Status -in ‘Failed’, ‘Skipped’ }).Count) {Write-Host “For Failed/Skipped tenants, a customer admin can approve with the FallbackConsentUrl in the CSV.” -ForegroundColor Yellow}
Run the script:
.\Grant-MspProcessSsoConsent.ps1 -WhatIf # dry run: shows which customers would be processed.\Grant-MspProcessSsoConsent.ps1 # all customers with active GDAP.\Grant-MspProcessSsoConsent.ps1 -TenantId <guid> # a single customer
You sign in to your own tenant first, to list your GDAP relationships, then to each customer tenant. Expect a sign-in or MFA prompt per customer.
What it does for each customer
- Creates the MSP Process SSO enterprise app, if it isn’t already there.
- Grants tenant-wide consent for
openid profile email User.Read. - Verifies the grant.
- Writes a CSV report named
MspProcessSso-Consent-<timestamp>.csv.
Report statuses
Every
Failed or Skipped row includes a FallbackConsentUrl. A customer admin can use it as in Option A.
After consent
Make a note of each customer’s tenant ID. You’ll find it in the script’s CSV report (TenantId column), or in the tenant= value of the Option A redirect address. MSP Process uses it to match Microsoft sign-ins to the right customer. If you need help linking tenants to your customers, contact MSP Process Support.

