Skip to content

Entra: validate access tokens and map claims to PostKit Principal #87

Description

@patoperpetua

Parent: #84
Depends on: #83, #86

Goal

Add Microsoft Entra authentication to the PostKit runtime so REST and MCP requests can authenticate with Singleton Entra-issued access tokens and map them into the shared PostKit Principal authorization model.

Reference

Use the InkAds firmware Entra implementation as a security reference, especially its strict validation approach:

InkAds validates tenant-specific issuer/audience/expiry/tenant/role claims against Entra signing keys. PostKit should follow the same principles but use standard Node/TypeScript OIDC/JWT libraries rather than porting embedded-device code.

Scope

Implement an Entra authentication provider in the shared auth layer introduced by #83.

Conceptually:

interface Principal {
  id: string;
  tenantId?: string;
  authType: 'api-key' | 'entra';
  scopes: string[];
  clientId?: string;
  userId?: string;
}

Exact shape/naming should follow existing code.

Token validation

Validate access tokens using the Singleton tenant's OIDC metadata/JWKS.

At minimum enforce:

  • cryptographic signature;
  • expected issuer;
  • expected PostKit audience/Application ID URI;
  • exp / nbf;
  • Singleton Entra tenant ID (tid);
  • required delegated scope (scp) or application role (roles).

Reject:

  • Microsoft Graph tokens;
  • tokens for another Singleton API;
  • /common/multi-tenant issuer assumptions;
  • personal Microsoft-account tokens;
  • tokens from another Entra tenant;
  • expired/not-yet-valid tokens;
  • tokens without the permission required by the requested operation.

Claim mapping

Map Entra claims to internal PostKit permissions rather than letting application code depend directly on Entra role names.

Example mapping:

Templates.Read / Templates.Read.All       -> templates:read
Templates.Preview / Templates.Preview.All -> templates:preview
Templates.Validate / ...                  -> templates:validate
Email.Send / Email.Send.All               -> email:send

MCP tools and REST handlers must authorize against the internal permissions.

Delegated vs application identity

Support both:

Delegated user token

  • use scp claim;
  • preserve user identity (oid/sub) where useful for audit;
  • preserve calling client/application identity (azp/appid) where available.

App-only token

  • use roles claim;
  • represent the service/client identity in the Principal;
  • do not require a user identity.

PostKit tenant boundary

Entra tenant (tid) identifies the Singleton identity directory. It does not identify a PostKit application/customer tenant.

PostKit must separately determine which PostKit tenant(s) the principal/client is authorized to access. Never trust a tenantId supplied only in the request body/tool arguments.

Document the initial mapping strategy. For the PoC this can be an explicit config/allowlist if a full persistence model is not yet warranted.

REST + MCP integration

Configuration

Consume the non-secret values produced/documented by #86:

  • ENTRA_TENANT_ID;
  • expected issuer/authority;
  • POSTKIT_ENTRA_CLIENT_ID / API application ID;
  • expected audience/Application ID URI;
  • permission mapping.

No client secret is needed for PostKit merely to validate inbound access tokens.

Performance / resilience

  • Cache OIDC metadata/JWKS appropriately.
  • Handle signing-key rotation.
  • Do not fetch JWKS for every request.
  • Fail closed if a token cannot be verified.
  • Do not log raw bearer tokens.

Tests

Cover at minimum:

  • valid delegated token;
  • valid app-only token;
  • wrong audience;
  • wrong issuer;
  • wrong Entra tenant;
  • expired token;
  • missing scope/role;
  • correct token but unauthorized PostKit tenant;
  • REST and MCP using the same Principal/permission mapping.

Use mocked/local signing keys for unit tests where practical; keep real-Entra verification in integration/E2E tests.

Acceptance criteria

  • REST and MCP accept valid Singleton Entra access tokens for PostKit.
  • Wrong issuer/audience/tenant/expiry/signature tokens are rejected.
  • Delegated scopes and app roles map into PostKit internal permissions.
  • App-only and user-delegated identities are distinguishable for audit.
  • PostKit tenant isolation remains independent from Entra tenant identity.
  • JWKS metadata is cached and signing-key rotation can be handled.
  • No raw tokens or secrets are logged.
  • Existing API-key authentication remains functional where configured.
  • Authentication/authorization tests cover representative failure modes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions