> ## Documentation Index
> Fetch the complete documentation index at: https://kb.mspprocess.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Pre-approving the "MSP Process SSO" Enterprise App

> IntroductionIt'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 -…

## 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.

| | **Option A: Admin consent link** | **Option B: Script** |
| - | - | - |
| **Best for** | A few customers, customers without GDAP, or when Option B fails | Bulk deployments, leveraging the Admin Relationships you've setup in the Microsoft Partner Center |
| **Who acts** | A customer admin, or you a privileged account in their M365 tenant | You, from your own M365 tenant |
| **Effort per** **customer** | One click-through | Automatic |

###

## App details

| App name | MSP Process SSO Verified publisher |
| - | - |
| Publisher | MSP Process (MPN ID 6887774) |
| Application (client) ID | **b38f995e-3812-43f1-97d5-5c4ff7578286** |
| Permissions requested | Delegated only: `opendid` , `profile` , `email` , `User.Read` . No admin-only permissions. |

##

## Option A: Admin consent link

Get the customer's **tenant ID** (a GUID) or a verified domain such as `contoso.com` .

Build the link below, replacing `{TENANT}` with the tenant ID or domain (in both places):

1. [https://login.microsoftonline.com/\\\{TENANT\\}/adminconsent?client\\\_id=b38f995e-3812-43f1-97d5-5c4ff7578286\&redirect\\\_uri=https%3A%2F%2Fapp.mspprocess.com%2F\&state=\\\{TENANT\\}](https://login.microsoftonline.com/\\\{TENANT\\}/adminconsent?client\\_id=b38f995e-3812-43f1-97d5-5c4ff7578286\&redirect\\_uri=https%3A%2F%2Fapp.mspprocess.com%2F\&state=\\\{TENANT\\})

2. 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.

3. Review the consent screen. It shows the verified badge for **MSP Process**. Select **Accept**.

4. You are redirected to `https://app.mspprocess/com/` with `admin_consent=True` and the tenant ID in the address. That confirms it worked.

Use the customer's own tenant ID **/common** and **/organizations** don't work in this link. It must be the customer's tenant ID or domain, not your own.

##

## 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 with `AADSTS90099` , 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**

1. An active Admin Relationship with each customer that includes Cloud Application Administrator (or Application Administrator).
2. That role assigned to a security group in your tenant, with you as a member.
3. PowerShell 7 and the Microsoft Graph SDK:

Install-Module Microsoft.Graph -Scope CurrentUser

###

### Download and Run the script

Save the PowerShell code below as `Grant-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 --------------------------------------------$AppId = 'b38f995e-3812-43f1-97d5-5c4ff7578286'$Scopes = 'openid profile email User.Read' # keep identical to the app's API permissions\$RedirectUri = '[https://app.mspprocess.com/](https://app.mspprocess.com/)' # must be a registered Web redirect URI# ---------------------------------------------------------------------------

$GraphAppId = '00000003-0000-0000-c000-000000000000'$RoleCloudAppAdmin = '158c047a-c907-4556-b7ef-446551a6b5f7'\$RoleAppAdmin = '9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3'

\# Microsoft Graph PowerShell SDK (only the modules we need)foreach ($m in 'Microsoft.Graph.Authentication','Microsoft.Graph.Applications','Microsoft.Graph.Identity.SignIns','Microsoft.Graph.Identity.Partner') \{if (-not (Get-Module -ListAvailable -Name $m)) \{throw "Missing module $m. Install with: Install-Module Microsoft.Graph -Scope CurrentUser"\}Import-Module $m -ErrorAction Stop}

function Get-ConsentUrl(\[string]$Tenant) \{$enc = \[uri]::EscapeDataString($RedirectUri)"https://login.microsoftonline.com/$Tenant/adminconsent?client\_id=$AppId&redirect\_uri=$enc\&state=\$Tenant"}

\# ---- Step 1: build the list of customer tenants ---------------------------\$targets = @()

if ($TenantId) \{$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

$rels = Get-MgTenantRelationshipDelegatedAdminRelationship -All -Filter "status eq 'active'"$byTenant = @\{}foreach ($r in $rels) \{$tid = $r.Customer.TenantIdif (-not $tid) \{ continue \}$roles = @($r.AccessDetails.UnifiedRoles.RoleDefinitionId)$hasRole = ($roles -contains $RoleCloudAppAdmin) -or ($roles -contains $RoleAppAdmin)if (-not $byTenant.ContainsKey($tid)) \{$byTenant\[$tid] = \[pscustomobject]@\{ TenantId = $tid; Name = $r.Customer.DisplayName; HasRole = $hasRole \}\}elseif ($hasRole) \{ $byTenant\[$tid].HasRole = \$true }}Disconnect-MgGraph | Out-Null

$targets = $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 ($t in $targets) \{$row = \[ordered\]@\{Customer = $t.Name; TenantId = $t.TenantId; Status = ''; Detail = ''ServicePrincipalId = ''; GrantedScopes = ''; FallbackConsentUrl = ''\}Write-Host "\`n== $($t.Name) \[$(\$t.TenantId)]" -ForegroundColor Cyan

if ($t.PSObject.Properties.Name -contains 'HasRole' -and -not $t.HasRole) \{$row.Status = 'Skipped'$row\.Detail = 'GDAP relationship lacks Cloud Application Administrator / Application Administrator'$row.FallbackConsentUrl = Get-ConsentUrl $t.TenantIdWrite-Warning $row.Detail$results.Add(\[pscustomobject]\$row); continue}

if (-not $PSCmdlet.ShouldProcess($t.TenantId, "Create service principal and grant tenant-wide consent for $Scopes")) \{$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 SSO$sp = Get-MgServicePrincipal -Filter "appId eq '$AppId'" | Select-Object -First 1$created = $falseif (-not $sp) \{$sp = New-MgServicePrincipal -AppId $AppId$created = \$trueWrite-Host " Created enterprise app."} else \{ Write-Host " Enterprise app already present." }

\# Microsoft Graph service principal in this tenant$graph = Get-MgServicePrincipal -Filter "appId eq '$GraphAppId'" | Select-Object -First 1

\# Tenant-wide delegated grant (retry to ride out replication delay after creation)$grant = $nullfor ($i = 1; $i -le 6; $i++) \{try \{$grant = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($sp.Id)' and resourceId eq '$($graph.Id)'" |Where-Object ConsentType -eq 'AllPrincipals' | Select-Object -First 1if (-not $grant) \{$grant = New-MgOauth2PermissionGrant -ClientId $sp.Id -ConsentType 'AllPrincipals' \`-ResourceId $graph.Id -Scope $ScopesWrite-Host " Granted tenant-wide consent."\}elseif ((($grant.Scope -split '\s+' | Where-Object \{ $\_ \}) | Sort-Object) -join ' ' \`-ne (($Scopes -split '\s+') | Sort-Object) -join ' ') \{Update-MgOauth2PermissionGrant -OAuth2PermissionGrantId $grant.Id -Scope $ScopesWrite-Host " Updated existing grant scopes."}else \{ Write-Host " Consent already granted." }break}catch \{if ($i -eq 6) \{ throw \}Start-Sleep -Seconds (3 \* $i)}}

\# Verify$verify = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($sp.Id)'" |Where-Object ConsentType -eq 'AllPrincipals' | Select-Object -First 1if (-not $verify) \{ throw 'Verification failed: no AllPrincipals grant found after granting.' }

$row.Status = if ($created) \{ 'Created+Consented' } else \{ 'Consented' }$row.ServicePrincipalId = $sp.Id$row.GrantedScopes = ($verify.Scope.Trim())}catch \{$row.Status = 'Failed'$row\.Detail = $\_.Exception.Messageif ($row\.Detail -match 'AADSTS90099') \{$row.Detail = 'AADSTS90099: Microsoft Graph Command Line Tools is not authorized in this customer tenant, so GDAP cannot use it. Use the FallbackConsentUrl (admin consent link) for this customer instead.'\}$row\.FallbackConsentUrl = Get-ConsentUrl $t.TenantIdWrite-Warning " $($row.Detail)"\}finally \{Disconnect-MgGraph -ErrorAction SilentlyContinue | Out-Null\}$results.Add(\[pscustomobject]\$row)}

\# ---- Step 3: report --------------------------------------------------------$results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8Write-Host "\`nSummary:" -ForegroundColor Green$results | Group-Object Status | ForEach-Object \{ Write-Host (" \{0,-18\} \{1\}" -f $\_.Name, $\_.Count) \}Write-Host "Report written to: $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 ($results.Where(\{ $\_.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

1. Creates the MSP Process SSO enterprise app, if it isn't already there.
2. Grants tenant-wide consent for `openid profile email User.Read`.
3. Verifies the grant.
4. Writes a CSV report named `MspProcessSso-Consent-<timestamp>.csv` .

It is safe to re-run. Existing apps and grants are reused.

## Report statuses

| **Status** | **Meaning** |
| - | - |
| `Created+Consented` | App added and consent granted |
| `Consented` | App already existed; consent granted or confirmed |
| `Skipped` | The GDAP relationship lacks the required role |
| `Failed` | See the `Detail` column |
| `WhatIf` | Dry run only; nothing changed |

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.

## Troubleshooting

| **Symptom** | **Likely cause and fix** |
| - | - |
| Users still see "Need admin approval" | Consent wasn't granted in that tenant, or was granted in the wrong one. Re-run for that tenant and confirm the tenant ID. |
| `AADSTS90099`: "Microsoft Graph Command Line Tools has not been authorized in the tenant" | Partners can't authorize the PowerShell sign-in app through GDAP. Use **Option A** for that customer, or ask a customer admin to approve "Microsoft Graph Command Line Tools" once. The script lists the link in `FallbackConsentUrl.` |
| "Insufficient privileges" when running the script | The GDAP relationship lacks the required role, or you aren't in the group it's assigned to. Check in Partner Center. |
| Sign-in to the customer tenant fails | The GDAP relationship has expired or isn't active. Renew it in Partner Center. |
| `AADSTS650051`: service principal already exists | Harmless. The app is already in the tenant. Re-run to apply consent. |
| User isn't found, or signs in from the wrong tenant | The user's account isn't in the tenant you consented for. Confirm which tenant their account belongs to. |
| Users blocked after consent | A Conditional Access policy, or "Assignment required" on the enterprise app. Allow the app in the policy, or assign the users. |

##

## Customer questions

### What data does the app see?

Basic sign-in profile only: name, email address, and a tenant-unique user ID. It cannot read mail, files, or any other Microsoft 365 data.

##

## Can the customer revoke it?

Yes. A customer admin can delete the enterprise app, or remove its permissions, in Entra admin center → Enterprise applications → MSP Process SSO.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.