All posts

8 min readMicrosoft Entra · App Registrations · Certificates · Security

Client secret or certificate? Choosing credentials for Entra app registrations

Client secret or certificate for Entra app registrations: how each works, security properties, Microsoft's recommendation, lifetimes, PowerShell setup, federated identity credentials and a decision table for MSPs and IT teams.

Read this post in German

Every app registration that calls Microsoft Graph or another API without a signed-in user needs a credential. Entra offers three kinds: client secret, certificate and federated identity credential. In practice most people pick the secret because it takes two clicks. This article explains how the options differ technically, what Microsoft recommends and when each one is the right choice.

Client secret: a shared password

A client secret is a string generated by Entra, 16 to 64 characters long. The value is shown exactly once when you create it and cannot be read back later. The application stores it in plain text and sends it as client_secret together with its client ID on every token request.

This is a shared secret: whoever knows the value is the application. In Microsoft Graph the secret appears as an entry in passwordCredentials with keyId, displayName, endDateTime and a hint made of the first three characters.

Certificate: a key pair

With a certificate you upload only the public part (.cer, .pem or .crt) to the app registration. The private key stays with the application, in the server's certificate store or in a key vault. To request a token, the application builds a short-lived JWT, the client assertion, and signs it with the private key. The header carries the SHA-256 thumbprint of the certificate (x5t#S256), the claims carry the client ID as iss and sub, the token endpoint as aud and an expiry time. Microsoft documents PS256 as the signing algorithm and recommends a validity of five to ten minutes.

Instead of client_secret the application sends two parameters: client_assertion_type with the fixed value urn:ietf:params:oauth:client-assertion-type:jwt-bearer and client_assertion with the signed JWT. Entra verifies the signature against the uploaded public key. The private key never leaves the application. In Graph the certificate lives in keyCredentials with type AsymmetricX509Cert and usage Verify. Entra currently supports only RSA for app certificates, and 2048 bits is the recommended size.

Security properties compared

The difference is not the cryptography. It is what happens after a leak.

  • A client secret sits in plain text in a config file, an environment variable, a script or a pipeline variable. Sooner or later each of those ends up in a log, a repository or a ticket. Whoever has the value can impersonate the app from anywhere.
  • A certificate sends only a signature with each request, valid for a few minutes. A captured request is useless to an attacker. What remains attackable is the private key itself, the .pfx file or the certificate store. You can protect that with file system permissions, a non-exportable key or a key vault.

What Microsoft recommends

Microsoft's Learn articles are unambiguous: the certificate is the recommended credential type. Client secrets are considered less secure and should not be used in production. For testing, a self-signed certificate is fine; for production, Microsoft recommends a certificate from a well-known CA and Azure Key Vault to manage access and lifetime. The article Migrate applications away from secret-based authentication summarises the reasoning.

You can enforce this in the tenant. With app management policies an administrator can block new secrets (passwordAddition), cap secret lifetime (passwordLifetime) and cap certificate lifetime (asymmetricKeyLifetime). Microsoft's own settings in the accompanying tutorial: disable secrets entirely, limit certificates to 180 days at most. These policies require a premium license.

Lifetimes: both expire

  • Client secrets get their lifetime when created. The portal suggests 180 days and allows at most 24 months. Microsoft recommends less than 12 months.
  • Certificates bring their own validity. Whoever issues the certificate sets it: New-SelfSignedCertificate defaults to one year, an internal PKI follows its template. Entra takes start and end date from the certificate on upload.

So the common claim that certificates last longer is only true if you issue them longer. Either way, both types carry an endDateTime, and both stop the application when it is reached.

Creating and uploading a certificate

For a script or service on a Windows server, a self-signed certificate in the local store is enough. The example creates one valid for two years with a non-exportable key and writes the public part to a .cer file:

$cert = New-SelfSignedCertificate -Subject "CN=Backup Job Customer A" `
  -CertStoreLocation "Cert:\LocalMachine\My" -KeyExportPolicy NonExportable `
  -KeySpec Signature -KeyLength 2048 -KeyAlgorithm RSA -HashAlgorithm SHA256 `
  -NotAfter (Get-Date).AddYears(2)

Export-Certificate -Cert $cert -FilePath ".\backup-job-customer-a.cer"

The upload happens in the Entra admin center: open the app registration, Certificates & secrets, Certificates tab, Upload certificate, pick the .cer file, add a description, Add. Entra then shows thumbprint, start date and expiry. You need the thumbprint in the application.

If the application runs elsewhere, for example in an Automation account, create the certificate with an exportable key, export it with Export-PfxCertificate and import the .pfx file there. Entra still only receives the .cer file. With an internal PKI you issue the certificate there instead and upload the public part the same way.

How the application authenticates

For Microsoft Graph PowerShell it is one extra parameter when connecting. The certificate must be in Cert:\CurrentUser\My or Cert:\LocalMachine\My:

Connect-MgGraph -ClientId "<app-id>" -TenantId "<tenant-id>" -CertificateThumbprint "<thumbprint>"

In your own code MSAL builds the assertion for you: in MSAL.NET, .WithCertificate(cert) replaces .WithClientSecret(secret) on the ConfidentialClientApplicationBuilder. If the private key lives in Azure Key Vault, .WithClientAssertion() lets you sign there without downloading the key.

What certificates cost in operation

  • Distribution. The private key has to reach every host the application runs on. For one server that is an import, for ten hosts an automation, for containers a key vault or secret store.
  • Key Vault. Azure Key Vault stores the certificate, can renew it automatically and can sign the assertion without the key ever leaving the vault.
  • Renewal. A new certificate has a new thumbprint. It must be uploaded to Entra and replaced in the configuration. The effort is similar to a secret swap, but different people are often involved.
  • Many customer tenants. The same public key can be uploaded to any number of app registrations. Convenient, but a compromised key then affects every customer. One certificate per customer is cleaner.

Third option: federated identity credentials

If the application runs in GitHub Actions, Azure DevOps, Kubernetes, Google Cloud or under a managed identity, it needs neither a secret nor a certificate. A federated identity credential under Certificates & secrets, Federated credentials tab, links the app registration to the token of an external identity provider. Entra compares issuer, subject and audience of the incoming token exactly against the stored entry and issues an access token on a match. There is no credential that can expire or leak. The prerequisite is an identity provider that issues OIDC tokens. A PowerShell script on a classic Windows server does not have one.

Decision table

SituationRecommendation
Local development, test tenant, lifetime under 90 daysClient secret is acceptable
Third-party tool that only supports secretsClient secret, short lifetime, secret store, rotation on the calendar
Script or service on your own server, Graph PowerShell, Automation accountCertificate
Application in Azure App Service, Functions or Container AppsManaged identity; if the app registration must stay, federate it to the managed identity
GitHub Actions, Azure DevOps, Kubernetes, another cloudFederated identity credential
Many customer tenants as an MSPOne certificate per customer, private keys held centrally in Key Vault

Rotation: several credentials at once

passwordCredentials and keyCredentials are lists. An app registration can hold several secrets and several certificates at the same time. That allows rotation without downtime: add the new credential, switch the application, check in the sign-in log that the new keyId is being used, then delete the old one. Moving from secret to certificate works the same way: upload the certificate, switch the app, delete the secret. As long as the old secret exists it remains valid. The switch is only complete once it is deleted.

Monitoring expiry

Secrets and certificates both carry an endDateTime, and Entra does not warn the application when it is reached. A certificate that runs out after two years stops the backup job just as surely as a secret after six months. The expiry dates of all app registrations can be read through Graph with Application.Read.All; a script for that is in the article Entra app registration secrets expire. The Entra recommendation "Renew expiring application credentials" (preview) under Entra ID, Overview, Recommendations lists credentials with less than 30 days left, but only for the tenant you have open and only if somebody looks. Federated identity credentials have no expiry date and fall outside this monitoring.

How SecretExpiry helps

SecretExpiry reads the expiry dates of client secrets and certificates from all connected tenants with the Application.Read.All permission. The dashboard shows an urgent list across all customers. Notifications go out by email per event or as a weekly digest, through a Teams or Slack webhook, or as a calendar subscription (ICS). Each tenant can have its own notification address, and individual credentials can be snoozed. The service starts at 10 € per tenant per month and comes with a 14-day free trial. Details are in the documentation.