# OAuth: Exchange Server vs Microsoft 365

Source: https://developer.nylas.com/docs/cookbook/use-cases/build/oauth-exchange-vs-m365/

You build one auth flow for Microsoft and assume it covers every "Exchange" account a customer throws at you. Then a finance company connects, their mailboxes sit on a server in their own data center, and your OAuth redirect goes nowhere. The word "Exchange" hides two completely different auth models, and picking the wrong one breaks the connection before you read a single message.

This page compares how authentication works for on-premises Exchange Server versus Microsoft 365, shows the request shape for each, and explains how one API connects both behind a single grant. For the protocol-level split, see [EWS vs Microsoft Graph for email](/docs/cookbook/email/ews-vs-microsoft-graph/).

## How does OAuth work differently for Exchange Server versus Microsoft 365?

Microsoft 365 uses standard OAuth 2.0: you redirect the user to Microsoft, they consent, and you exchange a `code` for an `access_token` and `refresh_token`. On-premises Exchange Server has no OAuth provider. It authenticates with a username and password over EWS, commonly through Basic or NTLM, against a server the customer runs themselves.

That difference shapes the whole integration. Microsoft 365 flows through Microsoft Entra and a managed cloud endpoint, so consent, token refresh, and scopes all follow OAuth conventions. On-prem Exchange Server predates that model entirely. Microsoft disabled Basic authentication for Exchange Online on October 1, 2022, but that change touched only the cloud. A self-hosted Exchange 2007 or later server keeps accepting whatever auth its administrator configured, which is why a single OAuth flow can't cover both deployments.

## Exchange Server vs Microsoft 365 auth, side by side

The two deployments split on every part of the auth flow, from where the credential lives to how long it lasts. Microsoft 365 issues short-lived OAuth tokens you refresh automatically; on-prem Exchange Server validates a password directly against the server on each connection. The table below maps the 6 auth dimensions that matter most, with the unified path in the last column.

| Auth dimension | On-prem Exchange Server (EWS) | Microsoft 365 (Graph) | **Through Nylas (unified)** |
| --- | --- | --- | --- |
| **Mechanism** | Username + password, Basic or NTLM | OAuth 2.0 redirect + token | **One Hosted auth flow** |
| **Credential store** | Customer's own server | Microsoft Entra | **A grant ID** |
| **Consent** | None, direct sign-in | Admin or user consent per tenant | **`provider=ews` or `microsoft`** |
| **Token refresh** | No tokens, re-auth on expiry | `refresh_token` rotates access | **Handled for you** |
| **Basic Auth status** | Still supported on-prem | Disabled since Oct 1, 2022 | **Both covered** |
| **Read mail call** | `FindItem` SOAP | `GET /me/messages` | **`GET /v3/grants/{id}/messages`** |

## What is the difference between the Outlook calendar API and the Microsoft Graph?

There's no separate "Outlook calendar API" anymore. Microsoft Graph is the single REST API for Outlook and Microsoft 365 calendars, reached at `graph.microsoft.com`. The older standalone Outlook REST API was decommissioned on March 31, 2024, so Graph endpoints like `GET /me/events` and `POST /me/calendar/events` are now the only supported cloud path for Outlook calendar data.

For Microsoft 365 calendars, Graph requires an OAuth token scoped with `Calendars.Read` or `Calendars.ReadWrite`, approved through Microsoft Entra. On-premises Exchange Server is the exception: it has no Graph endpoint at all, and you read calendar data over the EWS SOAP protocol instead. So "Outlook calendar" can mean two unrelated APIs depending on where the mailbox lives. Microsoft documents the cloud surface in its [calendar API overview](https://learn.microsoft.com/en-us/graph/api/resources/calendar). A unified layer hides that fork: you call `GET /v3/grants/{grant_id}/events?calendar_id=primary` for either deployment and get the same JSON back.

## Which email APIs support Microsoft Exchange Server calendar API integration?

Few email APIs reach on-premises Exchange Server, because it requires SOAP over EWS rather than REST and OAuth. Microsoft Graph covers only Exchange Online and Microsoft 365, not self-hosted servers. Nylas connects both: the `ews` connector reaches on-prem Exchange 2007 and later, while the `microsoft` connector handles Microsoft 365 through Graph, both behind one schema.

Most modern providers target the cloud only, so on-prem Exchange Server falls outside their coverage and you're left writing raw EWS SOAP yourself. Supporting a self-hosted Exchange calendar means handling autodiscovery, NTLM, and a specific server address and port, none of which a cloud-only OAuth integration does. The unified approach treats both as grants: connect an on-prem mailbox with `provider=ews`, connect a Microsoft 365 mailbox with `provider=microsoft`, then call `GET /v3/grants/{grant_id}/events?calendar_id=primary` against either. The [connect Exchange (EWS) accounts](/docs/cookbook/email/connect-exchange-ews/) guide covers the on-prem connect flow end to end.

## Connect a Microsoft 365 account with OAuth

For Microsoft 365, you start the OAuth flow by redirecting the user to `GET /v3/connect/auth` with `provider=microsoft`. The user consents in Microsoft Entra, then Microsoft sends a `code` back to your `redirect_uri`. This is the standard three-leg OAuth handshake, and it usually completes in under 30 seconds once admin consent is in place for the tenant.

The request below sends the user into the hosted Microsoft 365 consent screen. Set `redirect_uri` to a callback you registered, and pass `provider=microsoft` so the API routes to Graph and OAuth. The flow returns a `code` you exchange next.

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/connect/auth?client_id=<NYLAS_CLIENT_ID>&provider=microsoft&redirect_uri=<YOUR_CALLBACK_URL>&response_type=code"
```

After the redirect, swap the `code` for a grant. Send a `POST` to `/v3/connect/token` with `grant_type` set to `authorization_code`. The response includes a `grant_id` plus `access_token` and `refresh_token`, and the API rotates the access token for you on later calls so you never refresh it by hand.

```bash
curl --request POST \
  --url "https://api.us.nylas.com/v3/connect/token" \
  --header "Content-Type: application/json" \
  --data '{
    "client_id": "<NYLAS_CLIENT_ID>",
    "client_secret": "<NYLAS_API_KEY>",
    "grant_type": "authorization_code",
    "code": "<RETURNED_CODE>",
    "redirect_uri": "<YOUR_CALLBACK_URL>"
  }'
```

## Connect an on-prem Exchange Server account

On-premises Exchange Server skips OAuth entirely. You still call `GET /v3/connect/auth`, but with `provider=ews`, and the hosted page collects an Exchange username and password instead of showing a consent screen. The user signs in with a username formatted as `user@example.com` or `DOMAIN\username`, and the API authenticates straight against the server, typically in under 5 seconds when autodiscovery succeeds.

The request below starts the EWS connect flow. The shape matches the Microsoft 365 call, only `provider` changes, which is the whole point of the unified model. Replace the placeholders before running it.

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/connect/auth?client_id=<NYLAS_CLIENT_ID>&provider=ews&redirect_uri=<YOUR_CALLBACK_URL>&response_type=code"
```

One honest tradeoff: if every customer you serve is cloud-only Microsoft 365 and you need Teams, SharePoint, or OneDrive too, calling Microsoft Graph directly gives you the full surface and one less layer. The unified API earns its place when you must support self-hosted Exchange Server, or more than one provider, without writing a separate SOAP integration for the on-prem case.

## Read mail and calendar through one grant

Once connected, both deployments answer the same calls. You read mail with `GET /v3/grants/{grant_id}/messages` and calendar events with `GET /v3/grants/{grant_id}/events?calendar_id=primary`, whether the grant points at an on-prem EWS server or a Microsoft 365 mailbox on Graph. The response shape is identical, so you write one parser instead of branching on provider.

The request below lists up to 50 messages from any connected account. Swap the grant ID and the call stays the same across EWS and Graph, and across Google or iCloud grants too. Replace the bearer token with your API key.

```bash
curl --request GET \
  --url "https://api.us.nylas.com/v3/grants/<NYLAS_GRANT_ID>/messages?limit=50" \
  --header "Authorization: Bearer <NYLAS_API_KEY>"
```

```python [unifiedRead-Python SDK]
from nylas import Client

nylas = Client(api_key="<NYLAS_API_KEY>")

messages = nylas.messages.list(
    "<NYLAS_GRANT_ID>",
    query_params={"limit": 50},
)

for message in messages.data:
    print(message.subject, message.from_)
```

To audit which deployment a grant uses, list grants filtered by provider with `GET /v3/grants?provider=ews` or `provider=microsoft`. That tells you at a glance how many on-prem versus cloud mailboxes you've connected, which matters when you plan for the Exchange Online EWS retirement.

## What's next

- [Connect Exchange (EWS) accounts](/docs/cookbook/email/connect-exchange-ews/) for the full on-prem connect and grant flow
- [EWS vs Microsoft Graph for email](/docs/cookbook/email/ews-vs-microsoft-graph/) for the protocol-level comparison and retirement timeline
- [OAuth scopes for email and calendar](/docs/cookbook/use-cases/build/oauth-scopes-email-calendar/) for the exact Microsoft Graph scope strings
- [Getting started with Nylas](/docs/v3/getting-started/) to create a project, connector, and your first grant