Files
PaperClipAI/server/src/services/chat-teams-credentials.ts
T
DottaandPaperclip 6abeb67334 feat: add opt-in chat provider and data foundation (#13100)
Add dormant provider contracts, qualified patched adapters, tenant-scoped persistence and lifecycle ownership without activating chat routes. Preserve the experimental integration as dependent PR #13038.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 13:49:12 -05:00

44 lines
1.5 KiB
TypeScript

import { microsoftTeamsCredentialIdSchema } from "@paperclipai/shared";
import { unprocessable } from "../errors.js";
const MICROSOFT_TEAMS_ID_FIELDS = ["clientId", "tenantId"] as const;
/**
* Keep the persisted Teams identity in the same canonical form Microsoft puts
* in Bot Framework activities. This is also called inside the service so
* direct service consumers cannot bypass the HTTP validator.
*/
export function normalizeMicrosoftTeamsCredentialIds(
credentials: Record<string, string>,
): Record<string, string> {
const normalized = { ...credentials };
for (const field of MICROSOFT_TEAMS_ID_FIELDS) {
const parsed = microsoftTeamsCredentialIdSchema.safeParse(
normalized[field],
);
if (!parsed.success) {
throw unprocessable(`${field} must be a canonical Microsoft Entra UUID`, {
code: "chat_provider_credentials_invalid",
provider: "microsoft-teams",
field,
});
}
normalized[field] = parsed.data;
}
return normalized;
}
/**
* Bot Framework can vary the casing of the same Entra object id between
* activities. Keep that case-insensitive identity on one Paperclip principal,
* while retaining the adapter's opaque user id when the activity omits a
* usable Entra id (for example, some federated or anonymous participants).
*/
export function normalizeMicrosoftTeamsExternalPrincipalId(
aadObjectId: unknown,
fallbackId: string,
): string {
const parsed = microsoftTeamsCredentialIdSchema.safeParse(aadObjectId);
return parsed.success ? parsed.data : fallbackId;
}