Many modern APIs use tokens to verify identity and enforce access control while remaining stateless. In a prior Duende Software tutorial, Securing a .NET API With JWT Authentication: Step-by-Step Tutorial, you built a minimal .NET API that issues and validates JSON Web Tokens (JWTs). That tutorial demonstrates the mechanics of token creation, signing, and validation within a self-contained project and is referenced several times in this article.
The Microsoft Graph API
In production, the same principles power large-scale services that handle millions of requests per day. One such system is the Microsoft Graph API, the unified API for data access across Microsoft 365 — mail, calendars, Teams, OneDrive, and more. Every request to the Graph API must include a valid JWT access token. In this real-world scenario, the JWTs are issued by the cloud-based Microsoft Entra ID (formerly Azure Active Directory), rather than by the API itself, as in the tutorial article.
The Graph API acts as a single entry point to the Microsoft 365 cloud ecosystem. Instead of dozens of product-specific endpoints, developers call a common REST API to read or modify organizational data, such as users, messages, groups, and files. Because the Graph API exposes sensitive business information, it enforces strict, standards-based security. It uses OAuth 2.0 and OpenID Connect, with JWTs as the format for representing user or app identity.
Client applications never send credentials, such as passwords, to the Graph API directly. Instead, such applications authenticate with Microsoft Entra ID, requesting an access token that represents a specific user or application. That token is a signed JWT containing claims that describe who the caller is, what resource it wants, and how long access remains valid.
The Graph API verifies the token's authenticity and validity before processing the call. If the token fails signature, lifetime, or audience checks, the request is rejected with a 401 Unauthorized response. This model lets Microsoft run a global, stateless API infrastructure where identity travels with each request.
How the Microsoft Graph API Leverages JWTs
At its core, the Graph API's authentication pipeline mirrors the sequence from the earlier tutorial article — only at enterprise scale. Part of that pipeline is as follows:
- A client application authenticates with Entra ID using an OAuth flow (authorization code, client credentials, device code, etc.).
- Entra ID issues a signed JWT access token containing standard claims such as issuer (
iss), audience (aud), expiration (exp), and others. - The client application calls the Graph API, providing the JWT in the request's
Authorizationheader, as is shown in the tutorial. - The Graph API validates the token by downloading Entra ID's public signing keys from its OpenID Connect discovery document — essentially a manifest that tells clients where to find the issuer's keys and token endpoints.
- If validation succeeds, the Graph API authorizes the request.
Conceptually, this is similar to the minimal API implementation in the aforementioned tutorial article. The primary difference lies in responsibility boundaries: the tutorial's API both issues and validates tokens; the Microsoft Graph API delegates issuance to a trusted identity provider while focusing solely on validation and authorization. That separation is a hallmark of production architecture and one of the key evolutions from local examples to enterprise methodologies.
The following features show how the Graph API applies the same JWT authentication principles as the tutorial example, but within a production-scale environment. Each point highlights one aspect of the API's design and, where helpful, notes how it compares to the simpler .NET implementation.
- External Issuer (Microsoft Entra ID): The Graph API never creates tokens itself. It trusts Entra ID as the sole issuer. In the tutorial, the same API generates its own JWTs, which simplified the setup for educational purposes. Production systems often decouple these roles so that identity can be centralized and audited independently of the underlying API.
- Standardized JWT Claims: Access tokens for the Graph API include familiar claims such as
aud,iss, andexp, along with additional claims not seen in the tutorial. One example is the scope claim (scp), which lists granted permissions such as User.Read or Mail.Read. - Public Key Validation and Key Rotation: The Graph API uses asymmetric cryptography, as does the tutorial project's RSA implementation, but with the Graph API, the validation process is fully automated. Rather than loading keys from a local file, the Graph API retrieves Entra ID's public signing keys and caches them internally. When old signing keys are periodically replaced with new ones — referred to as key rotation — the Graph API picks up the change automatically, ensuring trust remains intact without developer intervention.
- Short Token Lifetimes and Refresh Tokens: JWTs used as access tokens by the Graph API typically have short lifetimes. To maintain a seamless user experience, clients also receive refresh tokens that can obtain new JWTs without re-authentication. The tutorial purposely omits this flow, but in real-world deployments, it's essential for balancing security and usability.
- Revocation and Centralized Control: If a user leaves an organization, administrators can revoke their access in Entra ID. The Graph API automatically honors those changes by validating tokens using the issuer's latest published keys. The tutorial does not cover revocation; once issued, a token remains valid until expiration.
- Multi-Tenant Audience: The
audclaim lets the Graph API serve many organizations from a single global endpoint. Each tenant in Microsoft Entra ID represents a distinct organization, but all can securely access the same Graph API resource. Each token's audience explicitly names that resource, allowing the shared infrastructure to validate tokens from thousands of tenants. The tutorial API assumed a single, static audience value. - Error Handling and Diagnostics: When validation fails, the Graph API returns standard OAuth error codes such as
invalid_tokenorinsufficient_scope. Its logging system records various data, enabling administrators to analyze authentication failures. By comparison, the JWT tutorial example simply returns 401 or 403 without context. Production systems treat logging as a critical part of security posture.
Together, these aspects show what the Microsoft Graph API shares with the simple tutorial example — and how a production system extends those same principles into a more complex environment.
Final Thoughts
The Microsoft Graph API exemplifies JWT authentication operating at a global scale. Its use of short-lived signed tokens, public-key validation, and delegated identity management shows how the principles from a simple .NET tutorial evolve into a robust, multi-tenant security model.
Developers who want to implement similar architectures within their own organizations can use a framework such as Duende Software's IdentityServer, which provides the same OpenID Connect and OAuth 2.0 foundations used by the Graph API. IdentityServer allows you to issue, validate, and manage JWTs inside your own domain, providing the same separation of responsibilities demonstrated here — an identity service that issues tokens and APIs that validate them.