Authentication
Marginalia supports three authentication modes that control how users are identified and how access to the API is protected. The mode you choose depends on whether you are running the application for a single user or multiple users, and how much protection you need.
| Mode | Users | Use case |
|---|---|---|
| Anonymous | Single | Local development, private networks with no access control needed |
| Access Code | Single | Personal deployments — lightweight protection without user accounts |
| Entra ID | Multi | Shared team or organizational deployments requiring individual identity |
Anonymous mode
Anonymous mode is the default. No environment variables or configuration are required. All API requests are accepted without any access check, and every request is attributed to a single shared identity: _anonymous.
All documents, sessions, and suggestions are stored in Cosmos DB under the _anonymous user partition. There is no way to distinguish between different browser sessions or end users.
When to use: Local development, a personal instance on a private network, or any scenario where you do not need access control.
Configure for local development
No configuration is needed. Start the app normally and it runs in anonymous mode.
If a previously set access code is present in your user secrets, remove it:
cd marginalia-service/src/Api
dotnet user-secrets remove AccessControl:AccessCodeEnsure the ACCESS_CODE environment variable is not set, then run:
cd marginalia-service
aspire runConfigure for Azure deployment
Do not set the ACCESS_CODE GitHub Actions secret or azd environment variable. Ensure no value is present:
azd env set ACCESS_CODE ""Then deploy as normal:
azd upAccess Code mode
Access Code mode adds a lightweight password gate to a single-user deployment. When an access code is configured:
- The frontend checks
GET /api/config/access-statuson startup. - If an access code is required, a modal prompts for the code before the app loads.
- The code is stored in browser session storage (cleared when the tab closes).
- Every API request includes the code in the
X-Access-Codeheader. - The backend
AccessCodeMiddlewarevalidates the header using constant-time comparison and returns401 Unauthorizedif the code is missing or incorrect.
Health and status endpoints (/health, /alive, /api/config/access-status) are exempt from the check and always accessible.
The user identity remains _anonymous — this mode protects access to a single-user instance, it does not support multiple users.
Security note: Access Code mode is a lightweight access gate, not a high-security authentication mechanism. For production multi-user deployments, use Entra ID mode.
Configure for local development
Option 1 — User secrets (recommended for development)
cd marginalia-service/src/Api
dotnet user-secrets set AccessControl:AccessCode "your-access-code"Option 2 — appsettings.Development.json
Add or update the AccessControl section in marginalia-service/src/Api/appsettings.Development.json:
{
"AccessControl": {
"AccessCode": "your-access-code"
}
}Do not commit access codes to source control. Prefer user secrets for local development.
Option 3 — Environment variable
On Linux/macOS:
export ACCESS_CODE="your-access-code"On Windows (PowerShell):
$env:ACCESS_CODE = "your-access-code"Configure for Azure deployment
The access code flows from a GitHub Actions secret into the deployed Container App environment variable.
- Go to your GitHub repository Settings → Secrets and variables → Actions.
- Click New repository secret (or add to the
prodenvironment). - Set Name to
ACCESS_CODEand Value to your desired access code.
The secret flows through the pipeline: continuous-delivery.yml → deploy-production.yml → provision-infrastructure.yml → Bicep accessCode parameter → Container App ACCESS_CODE environment variable.
Alternatively, set the value in the azd environment before deploying:
azd env set ACCESS_CODE "your-access-code"
azd upTo remove access code protection from an existing deployment, clear the value and redeploy:
azd env set ACCESS_CODE ""
azd provisionEntra ID mode
Entra ID mode uses Microsoft Entra ID (formerly Azure Active Directory) to authenticate users. Each user signs in with their organizational account. The user's Entra ID object ID is used as the user partition key in Cosmos DB, so each user's documents and sessions are isolated from all other users.
Two Entra ID app registrations are created automatically by the pre-provision hook:
| Registration | Purpose |
|---|---|
{env}-marginalia-api | Represents the backend API; exposes the access_as_user OAuth 2.0 scope |
{env}-marginalia-spa | Represents the frontend SPA; acquires tokens for the API scope |
Note: Entra ID multi-user mode requires Azure AD tenant admin consent and the
Application.ReadWrite.AllMicrosoft Graph permission to create app registrations during provisioning.
Configure for local development
Entra ID mode is not supported for local Aspire development. Run in Anonymous mode or Access Code mode locally, and use Entra ID only for deployed Azure environments.
Configure for Azure deployment
Prerequisites
- An Azure subscription with a linked Entra ID tenant
- An account with permission to create Entra ID app registrations (
Application.ReadWrite.AllMicrosoft Graph permission, or equivalent) - The
azdenvironment initialized (azd env new <env-name>)
Step 1 — Enable Entra ID authentication
Set the ENABLE_ENTRA_AUTH variable in your azd environment:
azd env set ENABLE_ENTRA_AUTH trueStep 2 — Provision
Run azd provision (or azd up to provision and deploy together):
azd upDuring the pre-provision hook, the pipeline:
- Runs
infra/hooks/preprovision.ps1(Windows) orinfra/hooks/preprovision.sh(Linux/macOS). - Deploys
infra/entra-id/app-registrations.bicepusing the Microsoft Graph Bicep extension to create both app registrations. - Writes the resulting
AZURE_AD_API_CLIENT_IDandAZURE_AD_SPA_CLIENT_IDvalues back to theazdenvironment.
The Bicep deployment then passes these values to the backend Container App as environment variables:
| Environment variable | Value |
|---|---|
AzureAd__ClientId | API app registration client ID |
AzureAd__TenantId | Entra ID tenant ID |
AzureAd__Instance | Entra ID login endpoint (e.g., https://login.microsoftonline.com/) |
Step 3 — Verify
After deployment, confirm the app registrations were created in the Azure Portal under Microsoft Entra ID → App registrations. Look for:
{env}-marginalia-api{env}-marginalia-spa
Skipping re-provisioning of app registrations
The pre-provision hook skips app registration creation if AZURE_AD_API_CLIENT_ID is already set in the azd environment. This prevents duplicate registrations on subsequent azd up or azd provision runs.
To force recreation (for example, after a azd down), clear the stored client IDs:
azd env set AZURE_AD_API_CLIENT_ID ""
azd env set AZURE_AD_SPA_CLIENT_ID ""
azd provisionDisabling Entra ID authentication
To switch back to Anonymous or Access Code mode:
azd env set ENABLE_ENTRA_AUTH false
azd env set AZURE_AD_API_CLIENT_ID ""
azd env set AZURE_AD_SPA_CLIENT_ID ""
azd provisionComparing modes
| Capability | Anonymous | Access Code | Entra ID |
|---|---|---|---|
| No configuration required | Yes | No | No |
| Protects against unauthorized access | No | Yes | Yes |
| Supports multiple users | No | No | Yes |
| Per-user data isolation | No | No | Yes |
| Requires Azure AD tenant | No | No | Yes |
| Works locally (Aspire) | Yes | Yes | No |
| Recommended for production | No | Personal use only | Yes |