An Entra app registration gives your code an identity that Microsoft Graph accepts: a client ID, a way to prove it (secret, certificate or federated credential), and a set of Graph permissions. This guide covers the app-only path (client credentials flow), which suits scripts, services and automation that run without a signed-in user.
Overview
Pick the permission type first, because it decides the rest of the setup:
Application permissions
Delegated permissions
Runs as
The app itself
The signed-in user, limited by the app’s scopes
Typical use
Daemons, scheduled jobs, automation
Web, mobile and desktop apps with sign-in
Consent
Admin consent always required
User or admin consent
Token flow
Client credentials
Authorization code with PKCE
Reach
Tenant-wide for each granted permission
Only what the user can access
Steps 1 to 5 use application permissions. Where delegated access differs, the step says so.
Permission to register apps. By default any member user can, unless your tenant turned off “Users can register applications”. If it is off, you need the Application Developer, Cloud Application Administrator or Application Administrator role.
Someone who can grant admin consent. For Microsoft Graph application permissions that means Privileged Role Administrator or Global Administrator. Application Administrator and Cloud Application Administrator can consent to other APIs but not to Graph app roles.
The Graph permission you need. Find it on the Microsoft Graph permissions reference by looking up the API you plan to call; each API page lists the least-privileged permission.
A secure place to keep credentials, such as Azure Key Vault or your CI system’s secret store.
Step 1: Register the application
Registering creates the app object in your tenant and establishes a one-way trust: your app trusts the Microsoft identity platform.
Go to Entra ID > App registrations and select New registration.
Enter a Name. Users may see it on consent screens, so make it descriptive, for example Contoso Graph Reporting Job.
Under Supported account types, choose:
Accounts in this organizational directory only (single tenant) for an internal app or automation. This is the right choice for most Graph scripts.
Accounts in any organizational directory (multitenant) only if other tenants will use the app.
Leave Redirect URI blank for app-only access. For delegated access, pick the platform (Web, SPA or Public client/native) and enter the URI your app receives the sign-in response on.
Select Register. The portal opens the app’s Overview pane.
Step 2: Record the identifiers
Copy these two values from the Overview pane; every token request needs them.
Portal label
Used as
Example
Application (client) ID
client_id
11111111-2222-3333-4444-555555555555
Directory (tenant) ID
The {tenant} segment of the token URL
aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
Neither value is secret, but treat them as configuration, not as credentials.
Step 3: Configure credentials
The app needs a credential to prove its identity when it requests a token. Open Certificates & secrets in the app registration and choose one:
Option
Best for
Trade-off
Federated credential
Workloads on GitHub Actions, Azure, Kubernetes or other trusted identity providers
No secret to store or rotate; needs a supported issuer
Certificate
Production services and long-lived jobs
Stronger than a secret; you manage the key pair and renewal
Client secret
Quick tests and local development
Easiest to set up; a plain string that leaks easily and expires
Use a federated credential or certificate for anything that outlives a test. If you use a client secret:
Add a description and an expiry. Keep the lifetime short, such as 6 months.
Select Add, then copy the Value immediately. The portal never shows it again.
Store it in Key Vault or your secret manager. Never commit it to source control.
For a certificate, upload the public key (.cer, .pem or .crt) and keep the private key in your secret store.
Step 4: Add Graph permissions and grant admin consent
A token only carries the permissions you add here and an admin approves.
In the app registration, open API permissions > Add a permission > Microsoft Graph.
Choose Application permissions for app-only access (or Delegated permissions for a signed-in user).
Search for the permission your API needs, for example User.Read.All, tick it and select Add permissions.
Select Grant admin consent for <your tenant> and confirm. The Status column should show a green check.
Apply least privilege:
Pick the narrowest permission that works. Prefer .Read over .ReadWrite, and a specific scope such as Mail.ReadBasic.All over a broad one.
Avoid .All permissions where a resource-specific alternative exists, such as resource-specific consent for Teams or SharePoint (Sites.Selected).
Remove the default User.Read delegated permission if the app is app-only and does not need it.
Re-check consent after any permission change; new permissions are not active until an admin consents.
Step 5: Get a token and make a test call
The client credentials flow posts your credentials to the tenant’s token endpoint and receives a bearer token. Set scope to https://graph.microsoft.com/.default, which requests every application permission already consented for the app. Use User.Read.All as the example permission; the test call below lists users.
import msal, requests
app = msal.ConfidentialClientApplication(
client_id="<client-id>",
client_credential="<client-secret>",
authority="https://login.microsoftonline.com/<tenant-id>",
)
result = app.acquire_token_for_client(
scopes=["https://graph.microsoft.com/.default"]
)
if "access_token" not in result:
raise SystemExit(result.get("error_description"))
resp = requests.get(
"https://graph.microsoft.com/v1.0/users?$top=5",
headers={"Authorization": f"Bearer {result['access_token']}"},
)
print(resp.json())
A 200 response with user objects means the registration, credential and consent all work. Read secrets from environment variables or a vault in real code, never from literals. To confirm which permissions a token carries, paste it into jwt.ms and check the roles claim.
Troubleshooting
Error
Likely cause
Fix
AADSTS7000215 Invalid client secret provided
You copied the secret ID instead of the secret Value, or the secret has a typo
Create a new secret and copy the Value column immediately
AADSTS7000222 Client secret keys expired
The secret passed its expiry date
Add a new secret or certificate and update the app’s config
AADSTS700016 Application not found in the directory
Wrong client ID, or wrong tenant in the token URL
Re-check both IDs on the Overview pane
AADSTS70011 Invalid scope
scope is not https://graph.microsoft.com/.default for the client credentials flow
Use .default for app-only tokens
401 Unauthorized
Token missing, expired (about 1 hour) or sent to the wrong API
Request a fresh token and send it as Authorization: Bearer <token>
403 Authorization_RequestDenied
Permission not added, admin consent not granted, or the wrong permission type
Check API permissions for a green consent check; decode the token and confirm the roles claim
403 after adding a permission
The old token was issued before consent
Request a new token after granting consent
If a call still fails, copy the request-id and date from the response headers; they let you trace the request in the Entra sign-in logs (Monitoring & health > Sign-in logs > Service principal sign-ins).
Security best practices and maintenance
Prefer federated credentials or certificates over secrets, and never embed credentials in code, scripts or container images.
Keep secret lifetimes short and put a calendar reminder or alert on each expiry date.
Rotate without downtime: add the new credential, deploy it, confirm it works, then delete the old one.
Grant least privilege, and limit scope further where Graph supports it (for example Sites.Selected, or Exchange application access policies for mail).
Name an owner or two under Owners in the app registration so the app is never orphaned.
Use one registration per app and environment (dev, test, prod) so credentials and permissions can be changed independently.
Review regularly: audit Enterprise applications for unused apps and over-broad permissions, and watch service principal sign-in logs for unexpected activity.
Protect the app with Conditional Access for workload identities if your tenant licenses it.
Portal labels and menu paths change from time to time; the two Learn articles above are the first two worth rechecking if a step no longer matches what you see.