@opengeni/api-router 3.0.0-canary.0 → 3.0.1-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/app.js +1 -1
  2. package/dist/{chunk-7IAWEYBH.js → chunk-XZGTKYOA.js} +3626 -2525
  3. package/dist/{chunk-7IAWEYBH.js.map → chunk-XZGTKYOA.js.map} +1 -1
  4. package/dist/index.js +1 -1
  5. package/dist/integrations/fiken.d.ts +3 -1
  6. package/dist/integrations/github-lens-connect.d.ts +2 -2
  7. package/dist/integrations/integration-commit-authority.d.ts +13 -0
  8. package/dist/integrations/oauth-client.d.ts +118 -1
  9. package/dist/integrations/slack-interactions.d.ts +1 -1
  10. package/dist/mcp/first-party-tool-permissions.d.ts +4 -0
  11. package/dist/mcp/scheduled-task-view.d.ts +3 -3
  12. package/dist/routes/api-integrations.d.ts +1 -0
  13. package/dist/routes/organization-integration-policy.d.ts +3 -0
  14. package/package.json +19 -19
  15. package/src/app.ts +7 -2
  16. package/src/integrations/atlassian.ts +135 -65
  17. package/src/integrations/fiken.ts +57 -35
  18. package/src/integrations/github-app-connect.ts +34 -15
  19. package/src/integrations/google-drive.ts +295 -124
  20. package/src/integrations/integration-commit-authority.ts +69 -0
  21. package/src/integrations/oauth-client.ts +220 -65
  22. package/src/integrations/personal-github.ts +55 -31
  23. package/src/integrations/provider-oauth.ts +54 -31
  24. package/src/integrations/social-oauth.ts +83 -56
  25. package/src/mcp/first-party-tool-permissions.ts +1 -0
  26. package/src/mcp/knowledge-entries.ts +23 -6
  27. package/src/routes/api-integrations.ts +116 -4
  28. package/src/routes/billing.ts +52 -0
  29. package/src/routes/capabilities.ts +51 -1
  30. package/src/routes/connect.ts +161 -57
  31. package/src/routes/connections.ts +430 -305
  32. package/src/routes/github.ts +48 -19
  33. package/src/routes/integration-facets.ts +66 -25
  34. package/src/routes/knowledge.ts +48 -1
  35. package/src/routes/organization-integration-policy.ts +113 -0
  36. package/src/routes/plugins.ts +12 -2
  37. package/src/routes/pr-review-github.ts +60 -30
  38. package/src/routes/pr-review.ts +69 -45
  39. package/src/routes/social.ts +24 -13
@@ -1,7 +1,17 @@
1
+ import {
2
+ withOrganizationIntegrationAcquisition,
3
+ withOrganizationIntegrationPolicyFence,
4
+ } from "@opengeni/db/organization-integration-policy";
5
+ import {
6
+ claimOAuthAcquisition,
7
+ finishOAuthAcquisition,
8
+ integrationSourceSelectionRequiresAcquisition,
9
+ } from "./oauth-client";
1
10
  import {
2
11
  scheduledTaskKnowledgeSource,
3
12
  requireScheduledTaskKnowledgeSource,
4
13
  knowledgeSourceAgentConfig,
14
+ assertOrganizationIntegrationAllowed,
5
15
  } from "@opengeni/contracts";
6
16
  import { createHash, randomBytes } from "node:crypto";
7
17
  import { ExternalActorContinuation } from "@opengeni/contracts/external-identities";
@@ -48,6 +58,7 @@ import {
48
58
  } from "@opengeni/contracts";
49
59
  import {
50
60
  GOOGLE_DRIVE_INTEGRATION_DEFINITION,
61
+ CORE_INTEGRATION_DEFINITIONS,
51
62
  integrationDefinitionProviderDomain,
52
63
  } from "@opengeni/capabilities";
53
64
  import {
@@ -68,10 +79,10 @@ import {
68
79
  ConnectionDisconnectGenerationError,
69
80
  ConnectionDisconnectIdempotencyError,
70
81
  consumeIntegrationOAuthStateNonce,
71
- claimConnectOperation,
72
82
  finishConnectOperation,
73
83
  getConnectAttempt,
74
84
  type Database,
85
+ listInstalledApiIntegrations,
75
86
  createConnection,
76
87
  decryptEnvironmentValue,
77
88
  disconnectConnectionIdempotently,
@@ -254,6 +265,7 @@ export async function startGoogleDriveOAuth(
254
265
  externalContinuation?: ExternalActorContinuation;
255
266
  },
256
267
  ): Promise<GoogleDriveOAuthStartResponse> {
268
+ await withOrganizationIntegrationAcquisition(deps.db, input, ["google-drive"], async () => {});
257
269
  const google = requireGoogleDriveSettings(deps.settings);
258
270
  const existing = input.payload.connectionId
259
271
  ? await getConnectionMetadata(
@@ -364,12 +376,18 @@ export async function completeGoogleDriveOAuthCallback(
364
376
  operationId: `oauth:${state.nonce}`,
365
377
  inputDigest: createHash("sha256").update(input.state!).digest("hex"),
366
378
  };
367
- const claim = await claimConnectOperation(deps.db, state, {
368
- ...operation,
369
- expectedRevision: stored.attempt.revision,
370
- authorize: (tx, _attempt, origin) =>
371
- requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
372
- });
379
+ const claim = await claimOAuthAcquisition(
380
+ deps.db,
381
+ state,
382
+ {
383
+ ...operation,
384
+ expectedRevision: stored.attempt.revision,
385
+ authorize: (tx, _attempt, origin) =>
386
+ requireConnectOwnerAuthority(tx, state!, "connections:write", origin),
387
+ },
388
+ "google-drive",
389
+ Boolean(input.code && !input.error),
390
+ );
373
391
  if (claim.status === "replayed") return { redirectTo: exactReturnUrl, exactReturn: true };
374
392
  }
375
393
  await requireGoogleDriveCallbackGrant(deps, state);
@@ -409,6 +427,7 @@ export async function completeGoogleDriveOAuthCallback(
409
427
  if (!input.code) {
410
428
  throw new GoogleDriveCallbackError("missing_code");
411
429
  }
430
+ await withOrganizationIntegrationAcquisition(deps.db, state, ["google-drive"], async () => {});
412
431
 
413
432
  const google = requireGoogleDriveSettings(deps.settings);
414
433
  const key = requireEnvironmentEncryption(deps.settings);
@@ -562,36 +581,46 @@ export async function completeGoogleDriveOAuthCallback(
562
581
  });
563
582
  };
564
583
  if (operation) {
565
- await finishConnectOperation(deps.db, acceptedState, {
566
- ...operation,
567
- authorize: (tx, _attempt, origin) =>
568
- requireConnectOwnerAuthority(tx, acceptedState, "connections:write", origin),
569
- commit: async (tx, current) => {
570
- const connection = await persist(tx);
571
- if (!connection) throw new GoogleDriveCallbackError("connection_conflict");
572
- return {
573
- ...current,
574
- revision: current.revision + 1,
575
- state: "complete",
576
- credentialsCommitted: true,
577
- nextAction: { type: "none" },
578
- account: {
579
- id: connection.id,
580
- version: connection.version,
581
- providerId: current.providerId,
582
- label: identity.emailAddress ?? identity.displayName,
583
- ownership: "personal",
584
- status: "connected",
585
- },
586
- };
584
+ await finishOAuthAcquisition(
585
+ deps.db,
586
+ acceptedState,
587
+ {
588
+ ...operation,
589
+ authorize: (tx, _attempt, origin) =>
590
+ requireConnectOwnerAuthority(tx, acceptedState, "connections:write", origin),
591
+ commit: async (tx, current) => {
592
+ const connection = await persist(tx);
593
+ if (!connection) throw new GoogleDriveCallbackError("connection_conflict");
594
+ return {
595
+ ...current,
596
+ revision: current.revision + 1,
597
+ state: "complete",
598
+ credentialsCommitted: true,
599
+ nextAction: { type: "none" },
600
+ account: {
601
+ id: connection.id,
602
+ version: connection.version,
603
+ providerId: current.providerId,
604
+ label: identity.emailAddress ?? identity.displayName,
605
+ ownership: "personal",
606
+ status: "connected",
607
+ },
608
+ };
609
+ },
587
610
  },
588
- });
611
+ "google-drive",
612
+ );
589
613
  return { redirectTo: exactReturnUrl!, exactReturn: true };
590
614
  }
591
- const connection = await deps.db.transaction(async (tx) => {
592
- await requireGoogleDriveCallbackGrant({ ...deps, db: tx }, acceptedState);
593
- return persist(tx);
594
- });
615
+ const connection = await withOrganizationIntegrationAcquisition(
616
+ deps.db,
617
+ acceptedState,
618
+ ["google-drive"],
619
+ async (tx) => {
620
+ await requireGoogleDriveCallbackGrant({ ...deps, db: tx }, acceptedState);
621
+ return persist(tx);
622
+ },
623
+ );
595
624
  if (!connection) {
596
625
  throw new GoogleDriveCallbackError("connection_conflict");
597
626
  }
@@ -682,19 +711,45 @@ export async function transitionGoogleDriveLifecycle(
682
711
  );
683
712
  }
684
713
 
685
- const updated = await transitionConnectionState(deps.db, {
686
- workspaceId: input.workspaceId,
687
- connectionId: existing.id,
688
- visibleToSubjectId: input.subjectId,
689
- expectedVersion: existing.version,
690
- status: "active",
691
- metadata: GoogleDriveConnectionMetadata.parse({
692
- ...metadata,
693
- lifecycle: googleDriveLifecycle(targetState),
694
- }),
695
- lastError: null,
696
- updatedBySubjectId: input.subjectId,
697
- });
714
+ const persist = (tx: Database) =>
715
+ transitionConnectionState(tx, {
716
+ workspaceId: input.workspaceId,
717
+ connectionId: existing.id,
718
+ visibleToSubjectId: input.subjectId,
719
+ expectedVersion: existing.version,
720
+ status: "active",
721
+ metadata: GoogleDriveConnectionMetadata.parse({
722
+ ...metadata,
723
+ lifecycle: googleDriveLifecycle(targetState),
724
+ }),
725
+ lastError: null,
726
+ updatedBySubjectId: input.subjectId,
727
+ });
728
+ const updated =
729
+ input.payload.action === "resume"
730
+ ? await withOrganizationIntegrationPolicyFence(
731
+ deps.db,
732
+ { accountId: existing.accountId, workspaceId: input.workspaceId },
733
+ async (tx, policy) => {
734
+ const current = await getConnectionMetadata(
735
+ tx,
736
+ input.workspaceId,
737
+ input.connectionId,
738
+ input.subjectId,
739
+ );
740
+ if (
741
+ current?.status === "active" &&
742
+ effectiveGoogleDriveLifecycle(
743
+ current,
744
+ requireGoogleDriveConnection(current, input.subjectId),
745
+ ).state === "active"
746
+ )
747
+ return current;
748
+ assertOrganizationIntegrationAllowed(policy, "google-drive");
749
+ return persist(tx);
750
+ },
751
+ )
752
+ : await persist(deps.db);
698
753
  if (!updated) {
699
754
  const converged = await getConnectionMetadata(
700
755
  deps.db,
@@ -972,58 +1027,141 @@ export async function saveGoogleDriveFacetSource(
972
1027
  }
973
1028
  const requestedDestination = bindGoogleDriveDocumentDestination(input, payload);
974
1029
  const requestedConfig = googleDriveFacetConfig(requestedSources, requestedDestination, payload);
975
- const replayed = await replayCompletedIntegrationFacetOperation<IntegrationFacetMutationResult>(
1030
+ const admitted = await withOrganizationIntegrationPolicyFence(
976
1031
  deps.db,
977
- {
978
- accountId: input.accountId,
979
- workspaceId: input.workspaceId,
980
- subjectId: input.subjectId,
981
- capabilityId: input.capabilityId,
982
- instanceKey: input.instanceKey,
983
- facetKey: input.facetKey,
984
- idempotencyKey: payload.idempotencyKey,
985
- kind: "configure",
986
- expectedRequestDigest: (result) => {
987
- const displayName = integrationFacetReceiptDisplayName(result);
988
- if (displayName === null) return "invalid-integration-facet-receipt";
989
- return integrationFacetConfigureRequestDigest({
1032
+ input,
1033
+ async (tx, policy) => {
1034
+ const replayed =
1035
+ await replayCompletedIntegrationFacetOperation<IntegrationFacetMutationResult>(tx, {
1036
+ accountId: input.accountId,
1037
+ workspaceId: input.workspaceId,
1038
+ subjectId: input.subjectId,
990
1039
  capabilityId: input.capabilityId,
991
1040
  instanceKey: input.instanceKey,
992
1041
  facetKey: input.facetKey,
993
- displayName,
994
- config: requestedConfig,
995
- ...(payload.expectedVersion !== undefined
996
- ? { expectedVersion: payload.expectedVersion }
997
- : {}),
1042
+ idempotencyKey: payload.idempotencyKey,
1043
+ kind: "configure",
1044
+ expectedRequestDigest: (result) => {
1045
+ const displayName = integrationFacetReceiptDisplayName(result);
1046
+ if (displayName === null) return "invalid-integration-facet-receipt";
1047
+ return integrationFacetConfigureRequestDigest({
1048
+ capabilityId: input.capabilityId,
1049
+ instanceKey: input.instanceKey,
1050
+ facetKey: input.facetKey,
1051
+ displayName,
1052
+ config: requestedConfig,
1053
+ ...(payload.expectedVersion !== undefined
1054
+ ? { expectedVersion: payload.expectedVersion }
1055
+ : {}),
1056
+ });
1057
+ },
998
1058
  });
999
- },
1059
+ if (replayed) return { replayed: IntegrationFacetMutationResult.parse(replayed) };
1060
+ const context = await requireGoogleDriveIntegrationFacet({ ...deps, db: tx }, input);
1061
+ googleDriveDocumentDestination(input, payload);
1062
+ const acquiring = googleDriveFacetSelectionRequiresAcquisition(context, requestedConfig);
1063
+ if (acquiring) {
1064
+ const installed = (
1065
+ await listInstalledApiIntegrations(tx, input.workspaceId, input.subjectId)
1066
+ ).find(
1067
+ (candidate) =>
1068
+ candidate.capabilityId === input.capabilityId &&
1069
+ candidate.instanceKey === input.instanceKey,
1070
+ );
1071
+ assertOrganizationIntegrationAllowed(
1072
+ policy,
1073
+ installed
1074
+ ? installedGoogleDriveFacetKey({
1075
+ apiProtocol: installed.protocol,
1076
+ definitionId: installed.definitionId,
1077
+ definitionProvenance: installed.definitionProvenance,
1078
+ })
1079
+ : null,
1080
+ );
1081
+ }
1082
+ return { context, acquiring };
1000
1083
  },
1001
1084
  );
1002
- if (replayed) return IntegrationFacetMutationResult.parse(replayed);
1003
- const context = await requireGoogleDriveIntegrationFacet(deps, input);
1004
- googleDriveDocumentDestination(input, payload);
1005
- await verifyGoogleDriveSources(deps, {
1006
- workspaceId: input.workspaceId,
1007
- subjectId: input.subjectId,
1008
- connectionId: context.connection.id,
1009
- sources: requestedSources,
1010
- });
1011
- return IntegrationFacetMutationResult.parse(
1012
- await configureIntegrationFacet(deps.db, {
1013
- accountId: input.accountId,
1085
+ if (admitted.replayed) return admitted.replayed;
1086
+ const context = admitted.context!;
1087
+ if (admitted.acquiring)
1088
+ await verifyGoogleDriveSources(deps, {
1014
1089
  workspaceId: input.workspaceId,
1015
1090
  subjectId: input.subjectId,
1016
- capabilityId: input.capabilityId,
1017
- instanceKey: input.instanceKey,
1018
- facetKey: input.facetKey,
1019
- displayName:
1020
- context.facet.binding?.displayName ?? `${input.instanceKey} — Google Drive content`,
1021
- config: requestedConfig,
1022
- ...(payload.expectedVersion !== undefined
1023
- ? { expectedVersion: payload.expectedVersion }
1024
- : {}),
1025
- idempotencyKey: payload.idempotencyKey,
1026
- }),
1091
+ connectionId: context.connection.id,
1092
+ sources: requestedSources,
1093
+ });
1094
+ return IntegrationFacetMutationResult.parse(
1095
+ await withOrganizationIntegrationPolicyFence(deps.db, input, (tx, policy) =>
1096
+ configureIntegrationFacet(tx, {
1097
+ accountId: input.accountId,
1098
+ workspaceId: input.workspaceId,
1099
+ subjectId: input.subjectId,
1100
+ capabilityId: input.capabilityId,
1101
+ instanceKey: input.instanceKey,
1102
+ facetKey: input.facetKey,
1103
+ displayName:
1104
+ context.facet.binding?.displayName ?? `${input.instanceKey} — Google Drive content`,
1105
+ config: requestedConfig,
1106
+ ...(payload.expectedVersion !== undefined
1107
+ ? { expectedVersion: payload.expectedVersion }
1108
+ : {}),
1109
+ idempotencyKey: payload.idempotencyKey,
1110
+ beforeAcquire: async (locked, installed) => {
1111
+ const current = await requireGoogleDriveIntegrationFacet({ ...deps, db: locked }, input);
1112
+ if (googleDriveFacetSelectionRequiresAcquisition(current, requestedConfig)) {
1113
+ assertOrganizationIntegrationAllowed(policy, installedGoogleDriveFacetKey(installed));
1114
+ if (!admitted.acquiring)
1115
+ throw new HTTPException(409, {
1116
+ message: "Google Drive source binding changed; verify the selection again",
1117
+ });
1118
+ }
1119
+ },
1120
+ }),
1121
+ ),
1122
+ );
1123
+ }
1124
+
1125
+ /** This is installed server-authored version provenance, not a facet's mutable
1126
+ * capabilities.provider, URL, domain, or client-authored credential metadata. */
1127
+ function installedGoogleDriveFacetKey(context: {
1128
+ apiProtocol: string | null;
1129
+ definitionId: string | null;
1130
+ definitionProvenance: string | null;
1131
+ }): string | null {
1132
+ if (context.apiProtocol !== "openapi" && context.apiProtocol !== "graphql") return null;
1133
+ if (context.definitionProvenance === "workspace") return `custom:${context.apiProtocol}`;
1134
+ return context.definitionProvenance === "curated" &&
1135
+ CORE_INTEGRATION_DEFINITIONS.some((definition) => definition.id === context.definitionId)
1136
+ ? context.definitionId
1137
+ : null;
1138
+ }
1139
+
1140
+ function googleDriveFacetSelectionRequiresAcquisition(
1141
+ context: Awaited<ReturnType<typeof requireGoogleDriveIntegrationFacet>>,
1142
+ requested: ReturnType<typeof googleDriveFacetConfig>,
1143
+ ): boolean {
1144
+ const binding = context.facet.binding;
1145
+ if (
1146
+ !binding ||
1147
+ binding.status !== "active" ||
1148
+ !binding.directlyOwned ||
1149
+ binding.connectionId !== context.connection.id
1150
+ )
1151
+ return true;
1152
+ const previous = GoogleDriveKnowledgeSourceConfig.safeParse(binding.config);
1153
+ if (!previous.success) return true;
1154
+ const selection = (config: ReturnType<typeof googleDriveFacetConfig>) =>
1155
+ config.sources.map((source) => ({
1156
+ ...source,
1157
+ destination: config.destination,
1158
+ syncCadence: config.syncCadence,
1159
+ syncEnabled: true,
1160
+ readPolicy: config.readPolicy,
1161
+ }));
1162
+ return integrationSourceSelectionRequiresAcquisition(
1163
+ selection(previous.data),
1164
+ selection(requested),
1027
1165
  );
1028
1166
  }
1029
1167
 
@@ -1065,14 +1203,34 @@ export async function saveGoogleDriveSource(
1065
1203
  if (existing.accountId !== input.accountId || existing.workspaceId !== input.workspaceId) {
1066
1204
  throw new HTTPException(403, { message: "Google Drive connection authority mismatch" });
1067
1205
  }
1068
- await requireGoogleDriveSourceConnection(deps, existing, input.subjectId);
1206
+ const existingMetadata = await requireGoogleDriveSourceConnection(
1207
+ deps,
1208
+ existing,
1209
+ input.subjectId,
1210
+ );
1069
1211
  const documentDestination = googleDriveDocumentDestination(input, payload);
1070
- const verifiedSources = await verifyGoogleDriveSources(deps, {
1071
- workspaceId: input.workspaceId,
1072
- subjectId: input.subjectId,
1073
- connectionId: input.connectionId,
1074
- sources: payload.sources,
1075
- });
1212
+ const requestedSelection = payload.sources.map((source) => ({
1213
+ ...source,
1214
+ destination: documentDestination,
1215
+ syncCadence: payload.syncCadence,
1216
+ syncEnabled: payload.syncEnabled,
1217
+ readPolicy: payload.readPolicy,
1218
+ }));
1219
+ const acquiring = integrationSourceSelectionRequiresAcquisition(
1220
+ existingMetadata.selectedSources ??
1221
+ (existingMetadata.selectedSource ? [existingMetadata.selectedSource] : []),
1222
+ requestedSelection,
1223
+ );
1224
+ if (acquiring)
1225
+ await withOrganizationIntegrationAcquisition(deps.db, input, ["google-drive"], async () => {});
1226
+ const verifiedSources = acquiring
1227
+ ? await verifyGoogleDriveSources(deps, {
1228
+ workspaceId: input.workspaceId,
1229
+ subjectId: input.subjectId,
1230
+ connectionId: input.connectionId,
1231
+ sources: payload.sources,
1232
+ })
1233
+ : payload.sources;
1076
1234
  const latest =
1077
1235
  (await getConnectionMetadata(
1078
1236
  deps.db,
@@ -1084,33 +1242,46 @@ export async function saveGoogleDriveSource(
1084
1242
  const previousSources =
1085
1243
  latestMetadata.selectedSources ??
1086
1244
  (latestMetadata.selectedSource ? [latestMetadata.selectedSource] : []);
1087
- const updated = await transitionConnectionState(deps.db, {
1088
- workspaceId: input.workspaceId,
1089
- connectionId: latest.id,
1090
- visibleToSubjectId: input.subjectId,
1091
- expectedVersion: latest.version,
1092
- metadata: GoogleDriveConnectionMetadata.parse({
1093
- ...latestMetadata,
1094
- documentDestination,
1095
- selectedSource: null,
1096
- selectedSources: verifiedSources.map((verified) => {
1097
- const previous = previousSources.find((source) => source.id === verified.id);
1098
- return {
1099
- id: verified.id,
1100
- name: verified.name,
1101
- mimeType: verified.mimeType,
1102
- driveId: verified.driveId,
1103
- destination: documentDestination,
1104
- syncCadence: payload.syncCadence,
1105
- syncEnabled: payload.syncEnabled,
1106
- configGeneration: (previous?.configGeneration ?? 0) + 1,
1107
- readPolicy: payload.readPolicy,
1108
- selectedAt: new Date().toISOString(),
1109
- };
1110
- }),
1111
- }),
1112
- updatedBySubjectId: input.subjectId,
1113
- });
1245
+ const updated = await withOrganizationIntegrationPolicyFence(
1246
+ deps.db,
1247
+ input,
1248
+ async (tx, policy) => {
1249
+ if (integrationSourceSelectionRequiresAcquisition(previousSources, requestedSelection)) {
1250
+ assertOrganizationIntegrationAllowed(policy, "google-drive");
1251
+ if (!acquiring)
1252
+ throw new HTTPException(409, {
1253
+ message: "Google Drive sources changed; verify the selection again",
1254
+ });
1255
+ }
1256
+ return transitionConnectionState(tx, {
1257
+ workspaceId: input.workspaceId,
1258
+ connectionId: latest.id,
1259
+ visibleToSubjectId: input.subjectId,
1260
+ expectedVersion: latest.version,
1261
+ metadata: GoogleDriveConnectionMetadata.parse({
1262
+ ...latestMetadata,
1263
+ documentDestination,
1264
+ selectedSource: null,
1265
+ selectedSources: verifiedSources.map((verified) => {
1266
+ const previous = previousSources.find((source) => source.id === verified.id);
1267
+ return {
1268
+ id: verified.id,
1269
+ name: verified.name,
1270
+ mimeType: verified.mimeType,
1271
+ driveId: verified.driveId,
1272
+ destination: documentDestination,
1273
+ syncCadence: payload.syncCadence,
1274
+ syncEnabled: payload.syncEnabled,
1275
+ configGeneration: (previous?.configGeneration ?? 0) + 1,
1276
+ readPolicy: payload.readPolicy,
1277
+ selectedAt: new Date().toISOString(),
1278
+ };
1279
+ }),
1280
+ }),
1281
+ updatedBySubjectId: input.subjectId,
1282
+ });
1283
+ },
1284
+ );
1114
1285
  if (!updated) {
1115
1286
  throw new HTTPException(409, { message: "Google Drive connection changed; try again" });
1116
1287
  }
@@ -0,0 +1,69 @@
1
+ import { verifyDelegatedAccessToken, type AccessGrant, type Permission } from "@opengeni/contracts";
2
+ import { resolveFirstPartyDelegationSecret, type Settings } from "@opengeni/config";
3
+ import { HTTPException } from "hono/http-exception";
4
+ import {
5
+ externalActorContinuationForAuthorization,
6
+ requireConnectOwnerAuthority,
7
+ hasPermission,
8
+ type AccessGrantAuthorization,
9
+ } from "@opengeni/core";
10
+ import type { Database } from "@opengeni/db";
11
+
12
+ export type IntegrationCommitGrant = AccessGrant & {
13
+ authorizeCommit: (tx: Database) => Promise<void>;
14
+ };
15
+
16
+ /** Keep server-verified provenance alongside the grant across provider work.
17
+ * Call only inside the policy transaction, before persistence locks. */
18
+ export async function integrationCommitGrant(
19
+ authorization: AccessGrantAuthorization,
20
+ permissions: readonly Permission[],
21
+ request: { settings: Settings; authorizationHeader?: string | undefined },
22
+ ): Promise<IntegrationCommitGrant> {
23
+ const grant = structuredClone(authorization.grant);
24
+ const continuation = externalActorContinuationForAuthorization(authorization);
25
+ const state = {
26
+ accountId: grant.accountId,
27
+ workspaceId: grant.workspaceId,
28
+ subjectId: grant.subjectId,
29
+ personalOwnerVerified: authorization.canonicalManagedHumanSession,
30
+ ...(continuation ? { externalContinuation: structuredClone(continuation) } : {}),
31
+ };
32
+ const required = [...permissions];
33
+ const token = request.authorizationHeader?.startsWith("Bearer ")
34
+ ? request.authorizationHeader.slice(7)
35
+ : null;
36
+ const secret = resolveFirstPartyDelegationSecret(request.settings);
37
+ const signed = token && secret ? await verifyDelegatedAccessToken(secret, token) : null;
38
+ if (signed) {
39
+ const matches = (value: NonNullable<typeof signed>) =>
40
+ value.accountId === state.accountId &&
41
+ value.workspaceId === state.workspaceId &&
42
+ value.subjectId === state.subjectId &&
43
+ required.every((permission) => hasPermission(value.permissions, permission));
44
+ if (!matches(signed))
45
+ throw new HTTPException(403, { message: "Integration bearer authority mismatch" });
46
+ return {
47
+ ...grant,
48
+ authorizeCommit: async (tx) => {
49
+ const currentSecret = resolveFirstPartyDelegationSecret(request.settings);
50
+ const current =
51
+ currentSecret && token ? await verifyDelegatedAccessToken(currentSecret, token) : null;
52
+ if (!current || !matches(current))
53
+ throw new HTTPException(403, {
54
+ message: "Integration bearer authority expired or changed",
55
+ });
56
+ // A signed external continuation still has live host/key/link authority.
57
+ if (continuation)
58
+ for (const permission of required)
59
+ await requireConnectOwnerAuthority(tx, state, permission);
60
+ },
61
+ };
62
+ }
63
+ return {
64
+ ...grant,
65
+ authorizeCommit: async (tx) => {
66
+ for (const permission of required) await requireConnectOwnerAuthority(tx, state, permission);
67
+ },
68
+ };
69
+ }