Expiring app secrets: the outage nobody sees coming
01 Aug 2026
Expiring app secrets: the outage nobody sees coming
Somewhere in your tenant there is an app registration with a client secret
that expires in the next 90 days. When it does, something stops working: the
backup job, the ticketing integration, the SSO for a line-of-business app, the
script that provisions new hires. Nobody gets an email. The app doesn't degrade
gracefully — it returns AADSTS7000215: Invalid client secret provided
to a service account, once, and then keeps returning it until a human notices
that the nightly export hasn't landed for four days. This is the most
predictable outage in Microsoft 365, and almost nobody has it on a calendar.
Two credential types, two places to look
An app registration in Entra ID can hold two kinds of credential, and the
Graph API exposes them as two separate collections on
/applications:
passwordCredentials— client secrets. A string, created in the portal with a maximum lifetime of 24 months (the API allows longer, which is how tenants end up with secrets nobody remembers creating). Each entry has adisplayName,startDateTimeandendDateTime.keyCredentials— certificates. Same shape, longer lifetimes, and usually attached to the integrations that matter most, which makes them the ones that break loudest.
Then there is the second place: /servicePrincipals. Every app
registration has a service principal in your tenant, but not every service
principal has an app registration you own — third-party SaaS apps and Microsoft
first-party apps appear there too, some with their own credentials. Auditing
only /applications gives you the apps your team created. Auditing
only the portal blade gives you whichever one you happen to be looking at.
The four failure modes
| Situation | What happens | Cost of finding out late |
|---|---|---|
| Secret expires on a live integration | Auth fails immediately, every call. | Hours of outage plus a backlog to replay. |
| Certificate expires on SAML SSO | Every user of that app is locked out at once. | A full-department incident, in business hours. |
| The app has three secrets, one used | Nothing — until you rotate the wrong one. | Two live credentials nobody can account for. |
| Secret expired months ago | Nothing broke, so the app is dead. | An over-permissioned app registration, still consented. |
| Owner left the company | The rotation runbook left with them. | Nobody knows where the secret is configured. |
The fourth row deserves a second look, because it isn't an availability
problem — it's the same category as
orphaned Azure
resources: something that was provisioned, stopped being used, and was never
removed. An app registration with an expired secret and
Mail.ReadWrite application permission is dormant, not harmless.
Someone can add a new secret to it in thirty seconds and inherit consent that
was granted in 2022.
Getting ahead of it in four steps
One row per credential, with app name, app id,
credential type, display name and endDateTime. An app with four
secrets is four rows. Sorted by expiry date, this list is the whole
report — and it immediately shows the apps carrying credentials they don't
need.
Expired means "investigate whether this app is dead" — delete or document, don't rotate by reflex. Thirty days is an incident waiting for a date and belongs in this week's queue. Ninety days is planning: enough time to find the owner, find where the secret is configured, and schedule the change.
Add the new credential first, deploy it to the consumer, verify a successful call, then delete the old one. App registrations accept multiple valid credentials precisely so rotation doesn't need a maintenance window. Deleting the old secret in the same change is how a routine rotation becomes an outage.
Workload identity federation and managed identities remove the credential entirely: no string to store, no expiry to track, no rotation runbook. Every app you migrate is a row that leaves this report permanently. What remains — third-party SaaS that only speaks client secret — is a much shorter list to babysit.
Why the portal won't tell you
The Entra portal shows credentials per app registration, one blade at a
time. There is no tenant-wide "expiring in the next 90 days" view, no alert, and
no export that spans applications and service principals together. Doing it by
hand means a Get-MgApplication -All loop that flattens
PasswordCredentials and KeyCredentials, a second pass
over service principals, and a date filter — a script that works fine the first
time and then quietly stops being run. Across multiple tenants, multiply by the
number of tenants and add the delegated-access dance for each one.
GraphPaaS collects expiring app secrets as part of the governance domain it already syncs per tenant, alongside admin roles and Conditional Access policies, so "what expires in the next 90 days, across every tenant" is a filter rather than a scripting afternoon. It sits next to the same identity hygiene questions as MFA registration gaps — because an app credential is an identity too, and it's the one nobody offboards.
See every client secret and certificate in every tenant you manage, sorted by the day it stops working.
See what expires next →