Parent: #81
Depends on: #82
Goal
Replace the Iteration 1 PoC credential check with a reusable authentication/authorization layer that works consistently across REST and MCP.
Core model
Introduce a transport-independent principal, for example:
interface Principal {
id: string;
tenantId?: string;
authType: 'api-key' | 'entra';
scopes: string[];
}
Exact naming should follow repository conventions.
Scope
- Create a shared authentication package/module used by REST and MCP.
- Support PostKit-issued API keys for internal/PoC machine access.
- Store only a secure hash of API keys; never persist the plaintext key after creation.
- Support revocation and optional expiry.
- Bind credentials to a tenant where appropriate.
- Enforce explicit permissions/scopes at the application boundary.
- Make authorization independent of whether the request came from REST or MCP.
Initial permissions
Use a small semantic permission set such as:
templates:read
templates:validate
templates:preview
email:send (defined but not necessarily enabled through MCP yet)
Avoid permissions tied to specific transport routes.
Security rules
- Tenant ID supplied in tool/API input must never override the authenticated principal's tenant authorization.
- Reject credentials without the required permission.
- Never log raw API keys.
- Provide a clear path to replace/augment API-key authentication with Microsoft Entra ID in Iteration 3.
Acceptance criteria
- REST and MCP can obtain the same
Principal abstraction.
- MCP tools enforce permissions centrally rather than individually implementing ad-hoc checks.
- API keys can be created/provisioned, authenticated, expired and revoked.
- A tenant-bound credential cannot read or preview another tenant's templates.
- Permission failures return consistent, non-sensitive errors.
- Tests cover invalid credentials, revoked credentials, missing scopes and tenant boundary violations.
Out of scope
- Full Microsoft Entra ID OAuth/OIDC integration.
- Interactive consent flows.
- Public third-party MCP clients.
- MCP email sending.
Parent: #81
Depends on: #82
Goal
Replace the Iteration 1 PoC credential check with a reusable authentication/authorization layer that works consistently across REST and MCP.
Core model
Introduce a transport-independent principal, for example:
Exact naming should follow repository conventions.
Scope
Initial permissions
Use a small semantic permission set such as:
templates:readtemplates:validatetemplates:previewemail:send(defined but not necessarily enabled through MCP yet)Avoid permissions tied to specific transport routes.
Security rules
Acceptance criteria
Principalabstraction.Out of scope