Microsoft 365 OAuth apps: audit consent and Microsoft Graph permissions

Inventory OAuth apps, delegated and application permissions, owners, credentials and logs, then remove access through a controlled process.

01

Why OAuth applications need a dedicated audit

An application connected to Microsoft 365 can read profiles, files, SharePoint sites, mailboxes, or directory data. With delegated permissions, it acts in the context of a signed-in user. With application permissions, it can operate without a user and continue as long as its identity and consent remain valid.

The risk is not limited to unknown apps. A legitimate integration can become excessive after a vendor change, a new permission, the departure of its owner, or a leaked credential. Valid admin consent confirms that an administrator authorized access; it does not prove the application is still needed or that its identity remains well protected.

This guide provides a triage method and read-only collection examples. It does not replace a complete review of each application's dependencies, code, logs, and actual data access.

02

Understand the objects before drawing conclusions

The same appId can refer to an application object in its home tenant and a service principal in every customer tenant. Deleting only the app registration or only the enterprise application therefore has different consequences. For a multitenant SaaS app, you normally govern its local service principal and the grants made in your organization.

Delegated permissions are also bounded by the privileges of the user who is present, while an application permission is carried directly by the workload. That distinction should strongly influence priority without implying that delegated access is automatically harmless.

ObjectWhat it representsWhat to review
App registrationGlobal definition created in its home tenantAudience, URIs, requested permissions, owners, secrets and certificates
Enterprise applicationService principal, or local instance of the app in your tenantEnabled state, assignments, grants, owners and activity
Delegated grantAuthorization to act for a signed-in userScope, user or tenant-wide consent, need for offline_access
Application permissionApp role usable without a signed-in userData scope, admin consent and documented justification
CredentialSecret, certificate, managed identity or workload federationOwner, expiry, storage and rotation
03

Define scope and business ownership

Set a decision date as well. An app without ownership or justification should not remain in a queue indefinitely, but a lack of visible activity is not enough evidence to delete it. Some integrations run only at month-end, during disaster recovery, or at annual renewal.

  • Which services are in scope: Exchange Online, SharePoint, Teams, OneDrive, Entra ID, Power BI, or custom APIs?
  • Is the app internal, Microsoft-published, vendor-supplied, or added directly by a user?
  • Who accepts the risk and confirms the business need? A technical owner in Entra is not a substitute for that accountable person.
  • How critical is it, which data can it reach, and which operations would stop if access were removed?
  • Is it single-tenant or multitenant, interactive or app-only, and used for production, testing, or recovery?
  • Which licenses provide access to logs, Defender for Cloud Apps, App governance, or workload identity protection?
04

Build a read-only PowerShell inventory

The Microsoft Graph PowerShell SDK can export service principals and local app registrations. Use an administrative workstation, a dedicated account, and read-only scopes. Application.Read.All is the documented least-privileged permission for several inventory commands; delegated grants and logs can require additional scopes. Always check the Permissions section for each cmdlet and obtain approval for the privileged session.

The example keeps the two inventories separate instead of assuming that every service principal has a local registration. It changes nothing and exports no secret value.

PowerShell — enterprise apps and local registrations
Install-Module Microsoft.Graph -Scope CurrentUser
Connect-MgGraph -Scopes @(
  'Application.Read.All',
  'Directory.Read.All',
  'DelegatedPermissionGrant.Read.All'
) -NoWelcome

$servicePrincipals = Get-MgServicePrincipal -All -Property @(
  'id','appId','displayName','servicePrincipalType','accountEnabled',
  'appOwnerOrganizationId','verifiedPublisher','createdDateTime','tags'
)

$applications = Get-MgApplication -All -Property @(
  'id','appId','displayName','signInAudience','createdDateTime',
  'passwordCredentials','keyCredentials'
)

$servicePrincipals | Select-Object DisplayName,AppId,Id,
  ServicePrincipalType,AccountEnabled,AppOwnerOrganizationId,CreatedDateTime |
  Export-Csv .\EnterpriseApps.csv -NoTypeInformation -Encoding UTF8

$applications | Select-Object DisplayName,AppId,Id,SignInAudience,
  CreatedDateTime,@{N='SecretCount';E={$_.PasswordCredentials.Count}},
  @{N='CertificateCount';E={$_.KeyCredentials.Count}} |
  Export-Csv .\AppRegistrations.csv -NoTypeInformation -Encoding UTF8
05

Connect owners, delegated grants, and application permissions

Three separate collections are required. Owners tell you who should answer for the app. oAuth2PermissionGrants represent delegated permissions. AppRoleAssignments granted to the service principal represent application permissions. Their identifiers must then be resolved against the target API service principal to produce readable permission names.

For a delegated grant, ConsentType is commonly Principal for one user or AllPrincipals for the tenant. Scope contains a space-delimited set of scopes. For an app role assignment, AppRoleId must be matched to the resource's AppRoles collection; a report containing only GUIDs is of little operational value.

PowerShell — collect owners and grants
$results = foreach ($sp in $servicePrincipals) {
  $owners = @(Get-MgServicePrincipalOwner -ServicePrincipalId $sp.Id -All)
  $delegated = @(Get-MgServicePrincipalOauth2PermissionGrant `
    -ServicePrincipalId $sp.Id -All)
  $appRoles = @(Get-MgServicePrincipalAppRoleAssignment `
    -ServicePrincipalId $sp.Id -All)

  [pscustomobject]@{
    DisplayName = $sp.DisplayName
    AppId = $sp.AppId
    ObjectId = $sp.Id
    Enabled = $sp.AccountEnabled
    Owners = ($owners | ForEach-Object {
      $_.AdditionalProperties['displayName']
    }) -join '; '
    DelegatedScopes = ($delegated.Scope | Sort-Object -Unique) -join '; '
    TenantWideDelegated = ($delegated.ConsentType -contains 'AllPrincipals')
    ApplicationRoleIds = ($appRoles.AppRoleId | Sort-Object -Unique) -join '; '
  }
}

$results | Export-Csv .\OAuthPermissions.csv `
  -NoTypeInformation -Encoding UTF8
06

Interpret risk without a simplistic blacklist

These signals prioritize review; they do not label an app as malicious. A Microsoft 365 backup platform may legitimately require broad access. That access should be matched by clear ownership, strong credential architecture, logging, contractual controls, and a review cadence proportional to its reach.

Publisher verification improves provenance, but it does not certify code or future behavior. Conversely, an unverified internal app is not automatically dangerous. Context and evidence remain decisive.

SignalWhy review itValidation question
Broad application permissionUnattended access to large data setsWould a Selected or resource-specific permission work?
Mail.ReadWrite, Files.ReadWrite.All, or Sites.FullControl.AllRead or change business contentWhich mailboxes, sites, and operations are actually required?
Directory.ReadWrite.All or RoleManagement.ReadWrite.DirectoryChange directory objects or rolesCan the vendor demonstrate every privileged operation?
AllPrincipals consentDelegated scope across the tenantMust every user be able to use the application?
No owner or unverified publisherHarder to establish accountability and provenanceWho confirms the publisher, contract, and need?
Long-lived or expiring secretCredential theft or outage riskCan managed identity, federation, or a certificate be used?
Inactive according to one sourceLogs may be incomplete or outside retentionIs there a rare job, another log, or an external dependency?
07

Audit secrets, certificates, and identities without exposing them

Review each credential's type, expiry, owner, execution location, and rotation procedure. Never export secret values into a report. Microsoft only displays a client secret value when it is created.

For an Azure workload, prefer managed identity. For a compatible external platform, workload identity federation avoids a shared secret. When neither option fits, Microsoft recommends certificate credentials ahead of client secrets. A certificate remains an operational secret: its private key, storage, rotation, and monitoring still require governance.

PowerShell — expiry metadata without secret values
$credentialReport = foreach ($app in $applications) {
  foreach ($secret in $app.PasswordCredentials) {
    [pscustomobject]@{
      App=$app.DisplayName; AppId=$app.AppId; Type='ClientSecret'
      CredentialId=$secret.KeyId; Start=$secret.StartDateTime
      End=$secret.EndDateTime
    }
  }
  foreach ($cert in $app.KeyCredentials) {
    [pscustomobject]@{
      App=$app.DisplayName; AppId=$app.AppId; Type='Certificate'
      CredentialId=$cert.KeyId; Start=$cert.StartDateTime
      End=$cert.EndDateTime
    }
  }
}
$credentialReport | Sort-Object End |
  Export-Csv .\OAuthCredentialExpiry.csv -NoTypeInformation -Encoding UTF8
08

Use logs without treating missing evidence as evidence of no use

Service principal sign-in logs show nonuser authentication, including certificate and client-secret flows. Events are grouped when principal, status, IP address, and resource match; expand the row to see individual timestamps. Managed identity sign-ins appear in their own category.

Audit logs can reveal creation of a service principal, addition of a grant or app role, and credential changes. Availability, retention, and access depend on licensing and export configuration. For critical apps, route the required categories to Log Analytics or a SIEM according to your retention policy.

Never classify an app as unused solely from its creation date, last modification, or a short log window. Correlate sign-ins, audit records, owners, billing, documentation, network flows, and the business calendar.

09

Prioritize apps and produce an action plan

At minimum, the report should include name, appId, objectId, publisher tenant, audience, status, technical and business owners, resolved permissions, consent type, credential type and expiry, last demonstrable activity, criticality, decision, approver, and next review date.

Keep raw evidence separate from the executive summary. IDs, permissions, and events are essential to investigation, but they can also reveal architecture and should be protected.

PriorityExample findingAction
P1 — immediateUnknown app, broad access, suspicious activity, or unexplained new credentialPreserve evidence, contain access, engage incident response
P2 — highExcessive application permission, no owner, poorly managed secretAssign ownership, reduce scope, replace the credential, and test
P3 — plannedLegitimate app with incomplete documentation or rotationDocument it, set a deadline, and add it to periodic review
MonitorJustified app, least privilege, owners and logs in placeMonitor permission, owner, and credential changes
10

Remove access through a controlled process

Do not begin by deleting the app registration. Confirm the exact object, owner, dependencies, permissions, and rollback path. To contain a compromised app, disabling its service principal may be faster and reversible; to correct excessive scope, remove the precise grant or app role assignment. Rotating a credential does not automatically cancel every existing token and consent.

After the change, test both required functionality and expected denials, then watch for errors and sign-ins. Delete the service principal or app registration only after an observation period and owner confirmation. A multitenant application can continue to exist with its publisher even after its local instance is removed.

  • Preserve logs and grant details before acting when compromise is suspected.
  • Temporarily disable the service principal if a controlled interruption is acceptable.
  • Remove only the delegated grant or app role assignment that is no longer required.
  • Revoke or replace the compromised credential and update the workload.
  • Validate business scenarios, expected denials, and alerts after the change.
  • Document the decision, approver, rollback, and final deletion.
11

Reduce the next consent risk

The admin consent workflow gives users a controlled way to request an app instead of bypassing the process. It does not replace analysis: reviewers need to understand the permission, data, delegated or app-only mode, vendor, owner, and credential model before approving it.

Defender for Cloud Apps and App governance can enrich the analysis of OAuth apps and behavior when the corresponding licenses are available. Those signals accelerate triage, but the decision still needs business context and dependency validation.

  • Limit user consent to low-impact permissions and applications that meet policy.
  • Enable an admin consent request workflow with defined reviewers, context, and deadlines.
  • Require both technical and business ownership before approval.
  • Prefer Selected or resource-specific permissions over tenant-wide access when they meet the need.
  • Monitor new credentials, grants, app roles, and anomalous sign-ins.
  • Review applications periodically, including those from established vendors.
12

Reusable checklist

  • Export app registrations, service principals, enabled state, and owner tenant.
  • Resolve technical owners and confirm the accountable business owner.
  • Resolve every delegated and application permission to readable names.
  • Identify consent type, assignments, and affected users.
  • Inventory secrets, certificates, federation, dates, and rotation procedures without exporting secrets.
  • Correlate sign-ins, audit, SIEM, contracts, billing, and business schedules.
  • Rank risk by scope, data, activity, provenance, credential, and criticality.
  • Approve every reduction or removal with tests and rollback.
  • Monitor after the change and set the next review.
13

The goal is continuous governance, not an annual cleanup

A good audit does more than find apps to delete. It makes every access explainable: who owns it, why it exists, which data it reaches, which identity it uses, how long it remains valid, and how removal will be tested.

The scripts above speed up inventory, but they cannot see the contract, business process, vendor code, or every dependency. A full Microsoft 365 assessment puts OAuth permissions in context with roles, sharing, Conditional Access, privileged accounts, logging, and incident response.