# Authentication

The Public API uses the standard **Microsoft Entra client-credentials flow** against the Entra application the setup wizard provisioned in your tenant. There is no Nexus365Desk-specific auth layer.

## What you have after setup

The setup wizard returns four values. You got them once; save them somewhere secure (Key Vault, your config service, a password manager):

| Name | Example | Where used |
|------|---------|------------|
| `tenantId` | `1234abcd-...` | In the token endpoint URL |
| `clientId` | `aaaabbbb-cccc-...` | `client_id` form field |
| `clientSecret` | `YourSecret~Here.` | `client_secret` form field (rotate every 2 years) |
| `siteId` | `contoso.sharepoint.com,<guid>,<guid>` | In Graph URLs |

## Step 1 — Get an access token

```
POST https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
Content-Type: application/x-www-form-urlencoded

grant_type=client_credentials
&client_id={clientId}
&client_secret={clientSecret}
&scope=https://graph.microsoft.com/.default
```

The response has `access_token`, `expires_in` (seconds, usually 3599), and `token_type` (`Bearer`). Cache it until 5 minutes before expiry — do **not** request a new token per call.

## Step 2 — Call Microsoft Graph

```
GET https://graph.microsoft.com/v1.0/sites/{siteId}/lists/SD_Tickets/items?$expand=fields&$top=50
Authorization: Bearer {access_token}
```

Always include `?$expand=fields` on list item calls — Graph returns only the envelope otherwise.

## Token caching

Tokens are cache-safe. Store one per `(tenantId, clientId, scope)` tuple. Most MSAL SDKs do this for you:

- **Python**: `msal.ConfidentialClientApplication` — calls `acquire_token_for_client` with internal caching.
- **Node.js**: `@azure/msal-node` — same pattern via `confidentialClientApplication.acquireTokenByClientCredential`.
- **.NET**: `Microsoft.Identity.Client.ConfidentialClientApplication`.

If you roll your own fetch, add a 60-second safety margin before expiry:

```js
if (!token || Date.now() > token.expiresAt - 60_000) { /* fetch new */ }
```

## Rotating the client secret

The wizard created a secret that expires in 2 years. To rotate earlier (compromise, employee departure, policy), open the SPFx admin → Integrations → Public API → **Rotate secret**. The old secret stops working the moment the new one is displayed.

Calls with the old secret will return `401 AADSTS7000215: Invalid client secret` — catch that, pull the new secret from your vault, retry.

## Scope limits

The app's only permission is Microsoft Graph `Sites.Selected` on the single Nexus365Desk site. Attempts to read from other SharePoint sites or other Graph resources will return `403 Forbidden`.

## What the app *cannot* do

- Read or write any SharePoint site other than the Nexus365Desk site.
- Read users, mail, calendars, Teams, or any non-SharePoint Graph resource.
- Delete the site or its top-level configuration.
- Grant itself additional permissions (requires tenant-admin consent, which Nexus365Desk never requests after setup).

This is enforced by Azure AD, not by Nexus365Desk — even if the Nexus365Desk service is compromised, the blast radius is this one site.
