MOFAKH.COM
← Back to profile
Azure Identity

Credentials: how an app proves who it is

Sep 4, 202613 min readWritten

An app has an identity, but it still has to prove it — that is what a credential does. There are three kinds: a client secret (a password for the app, which expires and causes outages), a certificate (a stronger signed proof), and a federated credential (the modern, secretless way). This part explains how each proves identity, and why the industry is moving toward storing no secret at all.

Part 3 gave the app an identity — a service principal in a tenant. But holding an identity is not the same as proving it. When the app asks Entra for a token, Entra needs proof that the request really comes from the genuine app and not an impostor. That proof is a credential. This is the authentication half of Part 1, made concrete, and the choices here have real security and reliability consequences.

What a credential is

A credential is the thing only the real app possesses, used to prove "I truly am this app" when requesting a token. There are three kinds, and they differ in how the proof works and — crucially — in whether a secret has to be stored at all:

  • a client secret — a shared password for the app,
  • a certificate — a key pair, where the app proves possession without sending the secret,
  • a federated credential — trust in an external identity provider, with no stored secret.

They form a ladder from simplest-and-weakest to most-secure.

Client secret: a password for the app

A client secret is a string generated for the app registration — effectively a password. The app sends its ClientId and this secret to Entra's token endpoint; Entra checks the secret and issues a token.

csharp
var app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientSecret(clientSecret)   // the shared secret is sent to Entra
    .WithAuthority(authority)
    .Build();

It is the quickest way to get started, which is why nearly every tutorial uses it. But it has real drawbacks: it is a shared secret that must be stored somewhere safe, anyone who obtains it can impersonate the app entirely, and — the big one — it expires.

The expiry outage

A client secret is created with an expiry date (at most around 24 months, often set shorter). When it expires, the app can no longer authenticate: token requests start failing, and calls that depend on them return 401. This is the classic "it worked last month and now it is broken" outage, and it is one of the most common real-world identity failures.

The failure to recognise: a sudden wave of authentication failures on an app that was working fine, with an error about an invalid client secret, almost always means the secret expired. The fix is to generate a new secret and update the configuration — and to rotate secrets before they lapse. An app registration can hold more than one secret at a time precisely so a new one can be added and rolled out before the old one expires, avoiding downtime.

The fragility and maintenance burden of secrets are exactly why the stronger options exist.

Certificate: prove without sending the secret

A certificate replaces the shared password with a key pair: a private key the app keeps, and a public key uploaded to the app registration. Instead of sending a secret over the wire, the app signs a small proof (a JWT assertion) with its private key. Entra verifies that signature using the public key it already holds.

csharp
X509Certificate2 cert = LoadCertificate();   // from the OS store, a file, or Key Vault
 
var app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithCertificate(cert)   // the app signs a proof; the private key never travels
    .WithAuthority(authority)
    .Build();

The security win is that the private key never leaves the app. With a client secret, the secret itself travels to Entra on every request and sits in configuration; with a certificate, only a signature travels, and the private key that produced it stays put. A certificate still has to be stored and still expires, so it is not maintenance-free — but it is meaningfully stronger than a shared secret.

Federated credentials: no stored secret at all

The modern answer removes the stored credential entirely. Workload identity federation configures the app registration to trust an external identity provider — GitHub Actions, another cloud, a Kubernetes cluster, or another Entra tenant. Instead of holding a secret, the app gets a token from that trusted provider and presents it to Entra:

Diagram
1. The external system (e.g. a GitHub Actions run) authenticates its own workload
   and issues a short-lived OIDC token that says "this is workflow X in repo Y".
2. The app presents that token to Entra.
3. Entra checks it against a pre-configured "federated credential" trust
   (which issuer, which subject, which audience are allowed).
4. If it matches, Entra issues its own access token.

No secret is stored anywhere — not in the app, not in configuration, not in a pipeline. The trust is set up once (a federated credential on the app registration describing which external identity is allowed), and from then on the external provider vouches for the workload.

csharp
// no stored secret — the app presents a token from a trusted external provider:
var app = ConfidentialClientApplicationBuilder
    .Create(clientId)
    .WithClientAssertion(() => GetTokenFromTrustedProvider())
    .WithAuthority(authority)
    .Build();

This is exactly how the GitHub Actions deployment scenario should authenticate (Part 11): GitHub proves the workflow's identity, Entra trusts GitHub, and no secret ever needs to be stored in the repository.

Managed identity: the Azure-hosted equivalent

There is one more secretless option, for code that runs inside Azure (an App Service, a Function, a VM): a managed identity, where Azure itself injects an identity into the resource and handles all credentials invisibly. It is the sibling of federated credentials — federation for external workloads, managed identity for Azure-hosted ones — and it gets its own treatment in Part 8. The shared theme is the same: no secret to store, rotate, or leak.

The security ladder

Putting the options in order clarifies the direction the whole industry is moving:

Three ways an app proves who it is
client secret
a shared password
stronger
certificate
signed with a private key
best
federated
no stored secret
Client secretCertificateFederated credential
What it isa generated stringa key pair (app holds the private key)trust in an external provider
Sent to Entrathe secret itselfa proof signed by the private keya token from the trusted provider
Stored secret?yesyes (the private key)no
Expires?yes, and shortyesnothing stored to expire
Relative strengthweakeststrongerstrongest
Good forquick starts, local devstronger app authenticationCI/CD, cross-cloud, external workloads

The trajectory is clear: from a shared secret, to a certificate that never sends its key, to federation that stores nothing at all. New systems should prefer the secretless options; secrets are fine for getting started and for local development, but they are the thing to move away from.

Where credentials must live

One rule overrides everything, regardless of credential type: credentials never belong in source code or version control. A secret committed to a repository is a leaked secret. Credentials belong in configuration, environment variables, or — best — a secret store like Azure Key Vault (Part 10), which keeps them out of both the code and the config files. The secretless options sidestep the problem entirely by having no stored credential to misplace.

Gotchas worth remembering

  • Secrets expire and cause outages. Track expiry dates and rotate ahead of time; keep a second secret ready during rollover.
  • An invalid-client-secret error usually means expiry (or a mistyped/rotated value), not a code bug.
  • Never commit a secret. Use configuration, environment variables, or Key Vault — never the repository.
  • A certificate is stronger than a secret because the private key never travels, but it still needs storage and rotation.
  • Prefer secretless where possible. Federated credentials for external workloads and managed identity for Azure-hosted code remove the store-and-rotate burden entirely.

The one idea to hold onto

A credential is how an app proves it is the identity it claims. A client secret is a shared password — simple but it expires and leaks easily. A certificate signs a proof with a private key that never travels — stronger. A federated credential stores nothing, trusting an external provider to vouch for the workload — strongest. The whole field is moving toward storing no secret at all, and credentials must never live in source code.

What comes next

An app can now prove who it is. The next question is what it is allowed to do — authorization. Part 5 covers the first of the two permission systems: API permissions, and the pivotal distinction between delegated permissions (the app acts as a signed-in user) and application permissions (the app acts as itself, no user). This is the concept that explains why an app-only workload needs no user password at all.