Enterprise credential-exchange patterns for connectors
Bottom line. For first-class clouds, exchange a trust relationship, never a long-lived secret — this is what Wiz/Orca/Datadog converge on and what enterprise buyers expect. Recommended pattern per connector:
Connector Recommended pattern Per-tenant secret? AWS Cross-account assume-role + external-ID No (one platform AWS-caller cred) Entra / Graph Multi-tenant app + admin consent No (one platform app secret) GCP Workload Identity Federation No GitHub SV0 GitHub App No (one global private key) Atlassian Cloud Forge app + Forge Remote (not Connect — deprecated) No ServiceNow / other SaaS OAuth client_credentials, customer-pasted into portal Yes (in KV) ADR-027's broker interface stays; for the federated clouds the broker mints a token instead of fetching a stored secret. The Slice-1 flat-env provider is dev/demo only — not a production control. Full per-connector rationale in §6; the decision and follow-up issues are in §7.
1. Why this research
ADR-027 currently models tenant credentials as a flat "customer hands us a secret bundle, we put it in Azure Key Vault, broker resolves it per scan" flow. That works for any source system but is the worst-case shape — it assumes every connector requires a long-lived shared secret. For modern clouds (AWS, Azure, GCP) and well-designed SaaS (GitHub), the industry has moved to federated patterns where no long-lived secret is exchanged at all. The customer grants a trust relationship; tokens are minted per-call.
Two questions this research answers:
-
Per source system — what's the right trust model? Cross-account IAM role for AWS, multi-tenant app with admin consent for Azure/Entra, workload identity federation for GCP, GitHub App for GitHub. ServiceNow and other SaaS without federation models still need a secret, but the secret should never traverse email/Slack — it should be customer-pasted directly into our admin portal, encrypted-in-transit to KV.
-
Per CSPM peer (Wiz, Orca, Datadog) — what do they actually do? Public-doc evidence of how each one onboards a customer. Used as cover for our own choices: if Wiz, Orca, and Datadog all converge on the same pattern, we should have a strong reason to deviate.
2. Vendor survey
2.1 Wiz
Wiz's principle across every cloud surface is "federation-first, no shared secret." Onboarding is always bootstrapped by a Wiz-authored artifact (CloudFormation, Cloud-Shell script, Helm chart, GitHub App manifest) so the customer never edits IAM directly. The customer hands back only identifiers — Role ARN, External ID, tenant ID, service-account email, installation ID — never a credential value.
Read the table as inferred, not confirmed. Wiz's own docs are login-walled (
docs.wiz.io→ 403). The cells below are reconstructed from partner-published walkthroughs, the public Helm chart, and Google/Microsoft partner pages, and from the shape of the install artifact (e.g., awiz_managed_identity_external_idTerraform input implies WIF). Specific mechanics — trust principal account, exact GCP federation wiring, GitHub App scopes — are inferred. Treat the direction (federation-first) as well-evidenced and the specifics as best-effort reconstruction.
| Cloud | Customer-side action | What is exchanged | Trust mechanism |
|---|---|---|---|
| AWS | Deploy Wiz-supplied CloudFormation template (single account) or StackSet (org-wide). Copy stack-output ARN back to Wiz UI. | Role ARN + External ID. No AWS access key leaves the account. | Cross-account sts:AssumeRole with ExternalId condition. Trust principal = Wiz's own AWS account. Permissions = SecurityAudit managed policy + scoped snapshot/KMS verbs for SideScanning. |
| Azure | Run Wiz-supplied bash script in Azure Cloud Shell. Scope = management group / subscription / Entra-ID-only. | Service principal materialised in customer tenant for Wiz's multi-tenant Entra app; RBAC role assigned at chosen scope. No client secret exchanged. | Multi-tenant Entra app + admin consent + RBAC. |
| GCP | Run Wiz auto-generated shell script (wiz-gcp.sh) or Terraform module at org/project scope. | Service-account created in customer org with read + snapshot perms. Wiz-managed external_id parameter wires up federation. | Workload Identity Federation with service-account impersonation. No SA JSON key downloaded. |
| Kubernetes | Customer adds Wiz Helm repo and installs wiz-kubernetes-integration. | Wiz-issued service-account client-ID + token stored as a K8s Secret in the cluster. Direction inverted: broker authenticates outbound to Wiz. | Outbound HTTPS tunnel (useHATunnel: true, optional AWS PrivateLink) from broker to Wiz API. Cluster never accepts inbound. |
| GitHub | Org admin installs Wiz's published GitHub App (Wiz Code). | GitHub App installation ID. Wiz mints short-lived (1h) installation tokens via its globally-held private key. | Standard GitHub App pattern; no PAT/OAuth-token exchange. |
Note on sourcing. docs.wiz.io is gated behind app.wiz.io/login (returns HTTP 403 unauthenticated). The table above is reconstructed from partner-published walkthroughs (Cordant, Tom Walker on LinkedIn, Medium write-ups), the public wiz-sec/charts Helm repo, Google Cloud's published Wiz partner architecture, and Microsoft Learn's Wiz connector page. Exact CloudFormation parameter names, GCP federation audience strings, and GitHub App scope manifests are not in unauthenticated public material.
URLs:
- docs.cloud.google.com — Wiz partner architecture
- learn.microsoft.com — Wiz data connector
- cordant.au — Connecting Wiz to your Azure tenant
- github.com/wiz-sec/charts
- wiz.io/blog — Wiz Code unify VCS
2.2 Orca Security
Orca's onboarding mirrors Wiz's shape (federation in every cloud, vendor-authored bootstrap artifact, identifiers-only round-trip) but adds snapshot-create + share permissions to the customer-side role/identity in every cloud. This is forced by Orca's "SideScanning" architecture, where scanning runs against snapshots copied into Orca's own cloud tenant rather than against the customer's APIs directly.
Same sourcing caveat as Wiz.
docs.orcasecurity.iois login-walled. The AWS detail (external-ID,SecurityAudit, snapshot/KMS verbs, tag-scoped delete) is corroborated across the AWS Storage Blog and the publicorcasecurity/Orca_AWS_In-Account_Policiesrepo; the Azure/GCP role lists are reconstructed from partner pages + the standard pattern each cloud requires for snapshot-sharing. Treat specific role names as inferred.
| Cloud | Customer-side action | What is exchanged | Trust mechanism |
|---|---|---|---|
| AWS | Self-service CloudFormation template (single account) or StackSet against the AWS Organization. Multi-account auto-onboarding propagates to new accounts. | Role ARN + Orca-supplied External ID. No long-lived secret. | Cross-account sts:AssumeRole with ExternalId condition. Trust principal = Orca's production AWS account. Permissions = SecurityAudit managed policy + 6 Orca-managed policies covering ec2:CreateSnapshot/CopySnapshot, tag-scoped ec2:DeleteSnapshot (Orca=*), and KMS Decrypt/CreateGrant/... for CMK-encrypted volumes. |
| Azure | SaaS-deployment wizard triggers admin consent for Orca's multi-tenant Entra app + ARM/Bicep role-assignment script. Management-group-scoped onboarding covers all subscriptions. | Service principal in customer tenant. RBAC = built-in Reader + targeted readers (Key Vault, logs) + a custom role for Microsoft.Compute/disks/beginGetAccess/action and snapshots/write. | Multi-tenant Entra app + admin consent. |
| GCP | Self-service shell script or Terraform at org/project scope. | IAM binding: Orca's production service account gets roles/iam.serviceAccountTokenCreator on a customer-side SA. No JSON key exported. | Cross-project service-account impersonation (Google's current recommendation; close to WIF but uses a long-lived IAM binding rather than a federated identity pool). Custom role for compute.disks.createSnapshot/snapshots.setIamPolicy. |
Deviation from "pure read-only". Where classic CSPMs (e.g., API-only Wiz, pre-agentless Prisma) grant Reader/SecurityAudit, Orca additionally needs snapshot create + share (and on AWS, KMS grant) — necessary for SideScanning's snapshot-copy model. Tag-scoping (Orca=* on DeleteSnapshot) is how Orca narrows the destructive surface.
Note on sourcing. Same caveat as Wiz: docs.orcasecurity.io is login-walled. Cross-referenced against AWS Storage Blog, Orca's partner-page descriptions, the public orcasecurity/Orca_AWS_In-Account_Policies GitHub repo (snapshot/KMS verbs), and the SideScanning technical brief.
URLs:
- AWS Storage Blog — How Orca shares encrypted EBS snapshots
- Orca AWS partner page
- Orca Azure partner page
- Orca GCP partner page
- github.com/orcasecurity/Orca_AWS_In-Account_Policies
2.3 Datadog
Datadog is the laggard of the three on Azure and (until recently) GCP — both still expose a "customer creates app/SA and pastes secret/key into Datadog UI" path as the documented default. AWS is on par with Wiz/Orca. The key insight from Datadog's docs is that modern federation and legacy paste-a-secret coexist on the same product, gated by feature flags (secretless_auth_enabled for Azure WIF) or version markers (V1 GCP service-account JSON deprecated in favor of V2 WIF).
| Integration | Customer-side action | What is exchanged | Trust mechanism |
|---|---|---|---|
| AWS | CloudFormation Quick Create (default), manual IAM role, or Terraform datadog_integration_aws. | Role ARN + Datadog-generated External ID (Datadog mints it server-side, customer pastes it into AWS — opposite direction from Wiz/Orca). Access keys allowed only in GovCloud and China partitions (commercial AWS = role-only). | Cross-account sts:AssumeRole + ExternalId. |
| Azure | ARM template or manual app-registration walkthrough. Customer creates the app registration in their own tenant (not a Datadog-published multi-tenant app). | tenant_id + client_id + client_secret pasted into Datadog UI. WIF is in preview (secretless_auth_enabled flag). | Customer-owned single-tenant app + secret OR (preview) federated credential. |
| GCP | Terraform module or Cloud Shell script (recommended for WIF) or manual JSON-key upload (legacy). | V1: full service-account JSON key uploaded. V2: just client_email; WIF trust to Datadog's principal. V1 marked deprecated. | V1: SA key (legacy). V2: WIF. |
| ServiceNow | "Add New Instance" tile. Customer creates a ServiceNow OAuth Application Registry record + dedicated user with the right roles. | OAuth2 client_id + client_secret (recommended) OR HTTP Basic username/password (legacy). Pasted into Datadog UI; no public credentials-POST API for this integration. | OAuth client_credentials or basic. No federation option exists in ServiceNow. |
| GitHub | "Connect GitHub Account" button → redirect to GitHub App install. | GitHub App installation ID. No customer paste step into Datadog UI. Webhook URL paste from Datadog into GitHub side (one direction only). | GitHub App (same as Wiz). |
Programmatic credential intake. Datadog publishes versioned REST APIs for cloud integrations: POST /api/v2/integration/aws/accounts, POST /api/v1/integration/azure, POST /api/v2/integration/gcp/accounts. Mirrors the UI fields exactly — customers can register integrations via IaC pipeline. No equivalent for ServiceNow or GitHub (UI/redirect-only). The cloud-side APIs are the closest public precedent for a "tenant admin API" pattern: versioned REST, accept credentials over TLS, return server-generated trust artifacts (Datadog's external ID is the prime example).
URLs:
- docs.datadoghq.com/integrations/guide/aws-manual-setup/
- docs.datadoghq.com/integrations/azure/
- docs.datadoghq.com/integrations/google_cloud_platform/
- docs.datadoghq.com/integrations/servicenow/
- docs.datadoghq.com/integrations/github/
2.4 Cross-vendor convergence
A short cross-reference of where the three peers agree, disagree, and what their disagreements tell us:
| Surface | Wiz | Orca | Datadog | Convergence verdict |
|---|---|---|---|---|
| AWS | Cross-account role + external-ID | Cross-account role + external-ID | Cross-account role + external-ID (commercial); IAM access keys only in GovCloud/China | Strong convergence. Confirmed for Datadog (primary docs); consistent with partner-published Wiz/Orca walkthroughs (their own docs are login-walled — see sourcing notes in §2.1/§2.2). It is also the AWS-documented standard for third-party access, so the convergence is well-grounded even where vendor specifics are inferred. |
| Azure | Multi-tenant Entra app + admin consent | Multi-tenant Entra app + admin consent | Customer-owned app reg + paste secret (legacy); WIF in preview | Two camps. Modern (Wiz, Orca — inferred) vs. legacy (Datadog — confirmed). See note below on multi-tenant-app vs WIF. |
| GCP | Workload Identity Federation | Service-account impersonation (close to WIF) | WIF (V2 default); legacy SA-key JSON (V1 deprecated) | Trending to federation. All three avoid raw SA-key JSON for new integrations (Datadog confirmed; Wiz/Orca inferred). |
| GitHub | GitHub App | (not surveyed) | GitHub App | GitHub App is the dominant pattern (confirmed for Datadog; Wiz per blog material; Orca not surveyed). OAuth Apps and PATs are explicitly deprecated for new integrations per GitHub's own docs. |
| Kubernetes | Outbound broker (inverted direction) | (out of scope) | (out of scope) | Wiz's approach reflects that clusters are typically not reachable from the outside — connector model has to invert when scanning runtime-only data. |
| ServiceNow / generic SaaS | (not in core onboarding surface) | (not in core onboarding surface) | OAuth client_credentials, customer paste into UI | No federation option — every vendor degrades to customer-paste. Choose to either (a) accept paste, with strict UX rules, or (b) skip the integration class. |
| Atlassian (Jira / Confluence) | (not in core onboarding surface) | (not in core onboarding surface) | (mostly in developer-tool integrations as 3LO, not in the surveyed CSPM surface) | Currency flag: Atlassian Connect is deprecated (new Marketplace apps must be Forge; Connect support ends Dec 2026 — §3.8). The 2026 pattern is a Forge app with Forge Remote (Marketplace install + SV0-controlled backend). Existing-vendor Connect apps are mid-migration ecosystem-wide. PAT remains for Data Center. A Connect-first recommendation in 2026 is a red flag. |
Azure note — don't conflate two patterns. The modern Azure pattern is multi-tenant app + admin consent for reading the customer's Graph (§3.4). Entra Workload Identity Federation (§3.5) is a different thing — it only removes the vendor's own app secret. They compose; they are not alternatives.
What we learn. The federation patterns are the default expectation among modern customers. A new entrant that asks for an Azure client_secret today is doing what Datadog does, not what Wiz does — and Wiz's posture is the one customers point to in procurement. For AWS / Azure / GCP / GitHub, "federation-first" should be the default; for ServiceNow and other SaaS, paste is acceptable only if the UX is encrypted-direct-to-KV with no staff visibility.
3. Foundational federation patterns
Authoritative summaries from official cloud-provider docs. This section is the technical backbone the per-connector recommendation in §6 will refer back to.
3.1 AWS — Cross-account IAM role with sts:AssumeRole + sts:ExternalId
Trust model. Customer's AWS account trusts the vendor's AWS account only when the vendor presents an agreed-upon external ID, mitigating the confused-deputy problem.
Customer creates. An IAM role in their account whose trust policy names the vendor's AWS account as Principal and adds a Condition clause: "StringEquals": {"sts:ExternalId": "<vendor-assigned-tenant-id>"}. Permission policy is scoped to the read-only actions needed. Customer hands the role ARN back to the vendor.
Vendor side (once). A single AWS account holding the IAM principal that calls sts:AssumeRole.
Per-tenant. Generate a unique random external ID, store the {tenantId → roleArn, externalId} mapping, always pass ExternalId on AssumeRole.
Doc. AWS — External IDs for third-party access
Gotcha. The external ID is not a secret — AWS says explicitly "AWS does not treat the external ID as a secret … can be seen by anyone with permission to view the role." It must be generated by the vendor (not customer) and unique per AWS account; otherwise the confused-deputy protection collapses.
3.2 AWS — IAM Roles Anywhere
Trust model. AWS trusts X.509 certificates issued by a customer-controlled CA (the "trust anchor") that the vendor's external workload presents.
Customer creates. A trust anchor (reference to AWS Private CA or external CA cert), a profile (session policy + role list), and an IAM role whose trust policy trusts the rolesanywhere.amazonaws.com service principal, pinned to the trust anchor ARN via aws:SourceArn.
Vendor side. A workload with a valid X.509 cert + private key from a CA that the customer's trust anchor recognizes. Signs CreateSession requests using SigV4-with-X.509.
Doc. AWS — IAM Roles Anywhere intro
Gotcha. Requires PKI — vendor must run (or buy into) a CA whose cert chain the customer is willing to trust. Not appropriate when the customer doesn't already operate a CA. Trust boundary is at the AWS account level: any cert from any trust anchor in that account can assume any role unless trust policies add conditions. Niche pattern; not used by any surveyed peer.
3.3 AWS — OIDC federation (sts:AssumeRoleWithWebIdentity)
Trust model. Customer AWS account trusts a JWT issued by an external OIDC IdP (GitHub Actions, Entra, GCP, our own IdP).
Customer creates. An IAM OIDC identity provider resource (issuer URL + audience/client-ID), then an IAM role whose trust policy uses "Federated": "arn:aws:iam::ACCT:oidc-provider/<issuer>" with Action: sts:AssumeRoleWithWebIdentity and Condition on <issuer>:aud and <issuer>:sub to pin to a specific workload.
Vendor side (once). Operate the OIDC IdP — publish a discovery doc at /.well-known/openid-configuration with issuer, jwks_uri, id_token_signing_alg_values_supported (RS256/ES256 etc.), and sign JWTs per workload.
Per-tenant. Customer adds the OIDC provider + role; vendor stores role ARN.
Gotcha. Hard caps — JWKS limited to 100 RSA + 100 EC keys; 100 audiences max per provider; OIDC provider must be in the same account as the role. AWS falls back to thumbprint verification if it can't validate the JWKS TLS cert against trusted CAs, so a self-signed JWKS endpoint forces thumbprint rotation chores.
3.4 Azure / Entra — Multi-tenant app registration + admin consent
Trust model. Vendor publishes one app registration in their home tenant set to signInAudience = AzureADMultipleOrgs. When a customer admin consents, a service principal is materialized in their tenant — that SP is the trust object.
Customer-side action. Customer tenant admin clicks through https://login.microsoftonline.com/common/adminconsent?client_id=<app> (or installs from the Microsoft AppSource gallery). One button; creates the SP + a delegation recording consent.
Vendor side (once). Single multi-tenant app registration; code points at /common or /organizations endpoint and must validate iss against the per-tenant tid.
Per-tenant. Capture the tenant ID on first consent callback; store {tenantId → ...}.
Doc. Microsoft Entra — Convert app to multi-tenant
Gotcha. "App-only permissions always require a tenant administrator's consent" — and so do many delegated Graph permissions. If the customer tenant has disabled user consent (common in enterprise), every permission requires admin consent. Revocation is by deleting the enterprise application (service principal) in the customer's tenant — vendor has no API to force-remove it.
3.5 Azure / Entra — Workload Identity Federation
Trust model. An Entra app registration (or user-assigned managed identity) trusts JWTs from an external IdP (GitHub Actions, AWS-via-Cognito, GCP, Kubernetes, SPIFFE).
Setup. Add a federated identity credential to the app/MI specifying issuer, subject, audience. External workload calls the OAuth2 client-credentials endpoint with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer and the external IdP's JWT as client_assertion — no client_secret required.
Doc. Microsoft Entra — Workload Identity Federation
Gotcha. issuer, subject, audience are case-sensitive exact matches against the incoming JWT — silent failures are common. Tokens issued by Entra itself cannot be used as the federation assertion. External IdP JWKS capped at 100 signing keys.
Important distinction: Entra WIF is for our workloads (running outside Azure) to authenticate to Entra apps we own. It is not the pattern for reading a customer's Entra tenant — that's §3.4 (multi-tenant app + admin consent). The two patterns compose: our workload can use WIF to auth as our own multi-tenant app, then call Graph with a per-tenant token.
3.6 GCP — Workload Identity Federation
Trust model. A GCP project trusts an external IdP (AWS, Azure, OIDC, SAML, GitHub, GitLab, Okta) via a workload identity pool; external identities then impersonate a GCP service account.
Customer creates. A workload identity pool, a workload identity pool provider (OIDC/SAML/AWS with attribute mapping + optional CEL attributeCondition), a service account, and an IAM binding roles/iam.workloadIdentityUser granting the external principal (or set) the right to impersonate that SA.
Vendor side. External workload exchanges its IdP token at sts.googleapis.com/v1/token (grant urn:ietf:params:oauth:grant-type:token-exchange) for a federated access token, then optionally calls generateAccessToken on the target SA. Audience must be //iam.googleapis.com/projects/<NUMBER>/locations/global/workloadIdentityPools/<POOL>/providers/<PROVIDER> — note the project number, not ID.
Doc. GCP — Workload Identity Federation
Gotcha. Audience must use the project number, not the ID — common foot-gun. google.subject mapping is mandatory; some GCP APIs (e.g., legacy ones) still don't accept federated credentials and silently require SA JSON keys.
3.7 GitHub — App installation flow
Trust model. Org admin installs an App; GitHub mints short-lived per-installation tokens via the App's private key. No long-lived per-customer secret.
Customer-side action. Org owner clicks Install on the App's public install URL, picks All repositories or Only select repositories, reviews the requested fine-grained permissions, confirms.
Vendor side (once). Register the App, generating: App ID, RSA private key (PEM, download once), optional webhook secret, optional client ID/secret for user-to-server OAuth.
Per-installation. Receive installation_id (either via installation.created webhook payload or GET /app/installations); to act, sign a JWT (≤10 min lifetime) with the App private key, then POST /app/installations/{installation_id}/access_tokens — returns a token valid 1 hour, optionally narrowed via repositories/repository_ids (max 500) and a permissions subset.
Doc. GitHub — Generating an installation access token
Gotcha. The App private key is a global secret — leak it and every installation is compromised. Rotate via the App settings UI, which creates a second key while the first is still valid (only manual rotation, no per-installation key isolation). Rate limits scale with installation size (repos × org users), but a single misbehaving installation can still exhaust its quota.
3.8 Atlassian — Forge (with Forge Remote) is the current path; Connect is deprecated
Currency warning (verified 2026-05-19). Atlassian deprecated Atlassian Connect (announced 2025-03-17, three-phase plan). Phase 1: all new Marketplace apps must be built on Forge with no Connect modules. Phase 2 (2026-03-31): Connect apps can no longer be updated. Connect is supported until December 2026, then "use at your own risk" (no security patches). Recommending a brand-new Connect app in 2026 is not market-defensible — the Marketplace would reject it. The correct target is Forge.
Forge + Forge Remote (the 2026 pattern). Forge is Atlassian's managed app platform; admin installs from Marketplace, Atlassian mints scoped tokens to the app at runtime. The historical objection for a CSPM — "Forge runs inside Atlassian's cloud and restricts egress, so it can't ship bulk data to our backend" — is addressed by Forge Remote, which lets a Forge app call an SV0-controlled remote backend (declared in manifest.yml under permissions.external.fetch.backend) and supports data-residency realm pinning (region-pinned remote URLs). So we get the Forge install/trust model and keep our own backend.
Trust model. Admin installs the SV0 Forge app from Marketplace → Atlassian issues short-lived scoped tokens to the app; the Forge frontend calls our remote backend via Forge Remote, with our backend authenticating the Forge invocation token. No per-install shared secret to store; no global private key.
Doc. Atlassian — Connect end of support timeline · Forge Remote · Forge Remote data residency
Gotcha. Forge Remote is newer and the model is more constrained than Connect's free-form server — egress domains must be declared in the manifest, and the app's compute/fetch operations must be declared for data-residency scope. Migrating an existing Connect app to Forge is non-trivial (>1,000 Marketplace apps are mid-migration across the ecosystem). For SV0 this is a greenfield build, so we start on Forge directly and avoid the migration entirely.
3.9 Atlassian — alternatives (briefly)
For completeness; not recommended for our CSPM Cloud use case:
- Atlassian Connect — the prior dominant pattern (Marketplace app + per-install
sharedSecret+ HS256 JWTs signed with aqshclaim). Deprecated (see §3.8). Only relevant now as a transitional path for a pre-existing Connect app, with a hard Dec-2026 sunset. Do not start here. Theqsh-canonicalization gotcha (any query-string rewrite invalidates the JWT) and the lifecycle-webhook-as-rotation-control-plane risk both still apply if a transitional Connect app exists. - OAuth 2.0 3LO (three-legged) — user-delegated access bound to a specific human user's permissions. Refresh token rotates on every use; invalidated whenever the authorizing user changes password, leaves the org, or has permissions reduced. Connector silently breaks at a human-lifecycle event. Reserved for developer-tool integrations (PR-to-issue linking) where human attribution is desirable. Doc:
developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps. - OAuth 2.0 Client Credentials (2LO) — Atlassian Cloud does not expose a generic 2LO endpoint for third-party integrators.
- Personal API tokens — admin pastes
email + api_token; HTTP Basic against*.atlassian.net. Tied to a real human's permissions — when that admin leaves, the connector dies. Atlassian force-expired all pre-Dec-2024 tokens between March and May 2026. Acceptable only as a quick-start fallback. Doc:support.atlassian.com — manage API tokens.
3.10 Atlassian — Data Center / Server (self-hosted)
For self-hosted Atlassian instances (Data Center is now the only self-hosted SKU since Server EOL Feb 2024):
- Personal Access Tokens (Jira 8.14+, Confluence 7.9+) — bearer in
Authorization: Bearer <pat>, per-user, inherits permissions, admin-configurable expiry. Modern recommended path. Doc:confluence.atlassian.com — Using Personal Access Tokens. - OAuth 1.0a via Application Links — legacy "official" pattern; RSA-SHA1 signed, painful key exchange, still common at large enterprises.
- OAuth 2.0 — added in recent DC releases but adoption is patchy.
- Basic auth — still works, strongly discouraged.
Gotcha. Many enterprise customers are still on unsupported Server versions; a connector must probe capability rather than assume PAT support.
4. Customer-side credential-exchange UX (the "no email" rule)
For connectors where a secret is unavoidable (ServiceNow OAuth client_secret, Snowflake key-pair, Okta API token, custom SaaS), the open question is how the secret physically gets from the customer's IT team into our infrastructure. Email, Slack, Signal, "send me the values" — all leak the secret into channels we don't control and can't audit.
The industry-standard answer is the tenant admin portal pattern:
- Customer admin logs into our portal at
app.securityv0.com/admin/connections. - Picks a connector type (e.g., "ServiceNow OAuth").
- Pastes the secret into a form field that is masked on input and never displayed back.
- The form
POSTs over TLS directly to a/api/v1/admin/connector-credentialsendpoint that writes straight to Key Vault — Mongo only ever sees thecredentials_refpointer. - No SV0 staff sees the secret value at any point. The audit log records that a value was set and by whom, not the value itself.
This guarantee depends on a customer-authenticated admin doing the pasting — i.e. when the actor in step 1 is the customer's admin. During operator-mediated onboarding (today's design-partner reality, where SV0 operators create instances on the customer's behalf), there is no customer admin in the loop, so an operator may handle a secret directly and the no-staff-visibility property does not apply to that phase. Two ways to close the gap:
- (a) a customer-authenticated one-time credential-intake link the operator sends; or
- (b) restrict operator-mediated onboarding to federation connectors (no secret exchanged) and require the paste-flow for secret-bearing ones.
Federation connectors sidestep this entirely; it only bites for ServiceNow / Atlassian Data Center / generic SaaS.
Vendor precedent:
- Datadog's cloud APIs (
POST /api/v2/integration/aws/accounts,POST /api/v1/integration/azure,POST /api/v2/integration/gcp/accounts) are the closest public precedent for a versioned credentials-POST endpoint. Customers can register cloud integrations via IaC without touching the UI. Datadog'ssecretless_auth_enabledflag shows how to make the same endpoint accept either paste-a-secret or federation against the same schema. - Datadog's UI for Azure / ServiceNow is the canonical "customer pastes into masked form" UX — fields for
tenant_id,client_id,client_secret, server-side write to their secret store. - Wiz's CloudFormation/Cloud-Shell pattern is the alternative: skip the paste entirely by making the customer run a vendor-authored script that provisions the federation server-side. This is the gold standard for federation-capable integrations but doesn't help for ServiceNow et al.
Design questions for SecurityV0:
- Where in the platform UI does this form live? — Operations console (
/operations/instancesper ADR-027 Slice 2) is the natural fit; a customer-facing tenant-admin portal is a future option once we have customer admins (today operators add instances on customers' behalf). - Is the customer admin a WorkOS-authenticated user (Layer 2 per ADR-023)? Yes — but role-gating needs an additional
tenant_adminrole on top ofsuper_admin/member. - Does the
POSTgo through the platform API server (which then writes to KV) or directly to a KV proxy? — Through the API server, because we need to audit the operation (who-set-what-when) and the API server already has tenant-scoping middleware. The blast-radius argument for a KV proxy doesn't outweigh the cost of a new auth surface. - What about per-tenant rotation triggered by the customer? — A separate
PUTon the same endpoint, same UI flow, using staged rotation: write the new secret as a new KV version, health-check it (a probe call against the source system), promote it atomically, then revoke/delete the old version. Do not zero the old value before writing the new one — a malformed or under-scoped new secret would otherwise cause an outage with no rollback.
5. Gap analysis against ADR-027 today
ADR-027 §(b) defines a CredentialBroker interface with four providers: env, op (rejected at runtime), azure_keyvault, aws_secretsmanager. Every provider assumes the credential VALUE exists and the broker's job is to fetch it. The federation patterns in §3 bypass this assumption entirely for first-class clouds:
| Source system | ADR-027 today | Modern pattern | Net effect on broker |
|---|---|---|---|
| AWS | azure_keyvault provider stores AWS access keys per tenant | Cross-account assume-role + external-ID (§3.1) | credentials_ref becomes a config (account_id, role_arn, external_id) stored in Mongo; broker mints temp creds via sts:AssumeRole at scan time. No per-tenant secret in KV — but our platform still holds one AWS-caller credential (the identity that calls AssumeRole) unless we run OIDC federation. See §6.1. |
| Azure / Entra | KV stores AZURE_CLIENT_SECRET per tenant | Multi-tenant app + admin consent (§3.4) | credentials_ref becomes { tenant_id, sp_object_id, granted_scopes, consent_at, consenting_admin } — there is a per-tenant artifact (the consented service principal), it's just not a secret. Our single multi-tenant app's client secret is our own platform secret (one, in KV, not per-tenant). |
| GCP | (not in ADR-027's first slice) | Workload Identity Federation (§3.6) | credentials_ref becomes { project_id, service_account_email }. No SA key exchanged. |
| GitHub | (not in ADR-027) | GitHub App installation (§3.7) | credentials_ref becomes { installation_id }. Tokens minted per-call via our App's private key (one platform-wide secret). |
| ServiceNow | KV stores SERVICENOW_* per tenant | OAuth client_credentials with customer-provisioned client | Stays close to ADR-027's model. Customer pastes client_id/client_secret into admin portal → direct-to-KV. |
| Atlassian Cloud (Jira / Confluence) | (not in ADR-027) | Forge app + Forge Remote (§3.8); Connect deprecated | credentials_ref becomes { atlassian_site_id / installation }. Forge mints scoped tokens; our remote backend authenticates the Forge invocation. New provider variant atlassian_forge. (Connect's per-install sharedSecret model only if a transitional Connect app exists.) |
| Atlassian Data Center | (not in ADR-027) | Personal Access Token (§3.10) | Customer pastes PAT into admin portal → direct-to-KV. Stays close to ADR-027's model. |
| Snowflake / Okta / Workday / custom SaaS | KV per tenant | Same — usually no federation option | Same as ServiceNow. |
Implication. The broker interface from ADR-027 is right (vendor-agnostic, per-tenant scoped). What changes is that for AWS / Azure / GCP / GitHub, the broker's implementation is "mint a token from our trust relationship," not "fetch a stored secret." New broker provider variants — aws_assume_role, azure_multi_tenant, gcp_wif, github_app — sit alongside the existing env and azure_keyvault providers.
Isolation caveat for the federation providers. ADR-027's "tenantId derives the namespace" rule keeps env/KV providers safe but does not carry over to federation — there is no prefix; the dangerous value is the binding (role ARN, entra_tenant_id, installation_id). The isolation mechanism for federation is owned by 15-connector-runtime-architecture.md §4: in short, the binding is selected by the verified tenant (re-derived from the DB chain, not the job payload) and the external trust is scoped customer-side (their role trusts our account only with their external-ID; their consent grants only their tenant). Bottom line: "tenantId enforces isolation" is true for env/KV and misleading for federation — spec it explicitly per provider.
Concrete deltas to ADR-027:
- Add federation providers to the
CredentialBrokerinterface (§(b)). Theresolve()signature already returns aRecord<string, string>— the AWS provider returns{AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN}from STS; the GitHub provider returns an installation token; the Azure provider returns an OAuth access token. The connector subprocess contract is unchanged. - Extend
CredentialsRefto carry per-provider config:{ provider: "aws_assume_role", config: { role_arn, external_id, region } }. - ADR-027's tenant-isolation invariant (
tenantIdderives the lookup namespace, not the ref) still holds — for federation, "lookup" means "which STS role to assume" or "which Entra tenant to issue a token for," and the tenant-prefix-from-tenantId rule still applies. - The
azure_keyvaultprovider is not retired — it remains the storage for ServiceNow and other non-federated SaaS creds, and remains the platform's own-secret store.
What doesn't change in ADR-027:
- The Slice 1 plan (env-broker only, ship the interface) is still the right first slice. Federation providers come after the interface lands; they're additional implementations of the same interface.
- The pipeline-run root, deploy-gate, and operator-UX decisions are independent of credential model.
- The runtime architecture in
15-connector-runtime-architecture.md§3 (per-scan sequence) is unchanged — the broker call site atexecute_scanis the same.
6. Recommendation
A per-connector credential strategy, ordered by priority. Each item gets one option as primary recommendation; the alternatives are tracked in §7 (Next Action) so a decision-maker sees the path not taken.
6.1 AWS connector — cross-account assume-role + external-ID
- Connector-side is already done. The AWS connector already accepts
AWS_ROLE_ARN+AWS_EXTERNAL_IDand has anassume_role_intopath (sv0_aws/config.py:82-83,sv0_aws/cli/main.py:1367). The remaining work is platform-side: stop putting per-tenant AWS access keys in KV, store the role binding as config, and have the broker mint STS creds at scan time. Frame this as "wire up what the connector can already do," not "build assume-role from scratch." - Customer creates an IAM role in their account; trust policy names our AWS account + an SV0-generated external ID. Onboarding artifact: an SV0-published CloudFormation template (single account) and a StackSet (org-wide). UI surfaces the role ARN field + the SV0-generated external ID (read-only display).
- Two distinct bootstrap models — pick one, do not mix them (they need different customer-side trust policies and different STS APIs):
- (A) Cross-account
AssumeRole(recommended day one). SV0 holds one AWS principal (an IAM user or role) in our own AWS account. The customer's role trusts that account with the external-ID condition. We callsts:AssumeRole. This requires SV0 to hold one long-lived AWS credential — see "no per-tenant secret, but not zero secret" below. The customer-side trust policy is the standard third-party form (§3.1). - (B) OIDC federation, Entra → AWS (
AssumeRoleWithWebIdentity). SV0 holds no AWS credential; the Azure VM's Managed Identity gets an Entra-issued token and exchanges it directly viaAssumeRoleWithWebIdentity. This is not free or out-of-the-box: it requires (i) the customer to register an IAM OIDC identity provider in their account pointing at our Entra issuer URL, and (ii) us to prove the exact token shape (issuer,aud,subclaims) Entra emits is accepted by AWS's OIDC verification. The customer-side trust policy is different from (A). Treat (B) as "requires building and proving an OIDC bridge," a follow-up — not a day-one option.
- (A) Cross-account
- "No per-tenant secret" — but not "no secret." Model (A) still needs one AWS-caller credential (the IAM user/role key) on the platform side, in
kv-sv0-prod. The win over today is eliminating per-tenant AWS keys, not all AWS keys. That one platform credential needs a rotation schedule, blast-radius analysis (it can assume into every customer role), and an emergency-revocation procedure. Only model (B) achieves zero AWS secrets. - The real design choice is identity-bootstrap, not network. STS accepts a caller from anywhere, so Azure-hosting is no obstacle. But model (A) needs an AWS credential on the Azure VM, and model (B) needs the Entra→AWS OIDC bridge — pick based on that, not the network path.
- What's stored per tenant:
{ account_id, role_arn, external_id }in Mongoconnector_instances.credentials_ref. No per-tenant secret in KV.
6.2 Entra / Microsoft Graph connector — multi-tenant app + admin consent
- Connector-side rework is required — flag this. Today the Entra connector expects a per-tenant client secret (
entra-servicenow/config.py:25-27readsAZURE_TENANT_ID+AZURE_CLIENT_ID+AZURE_CLIENT_SECRET, i.e. a customer-owned single-tenant app). Moving to a multi-tenant app means the connector authenticates as our app against the customer's tenant ID — a different code path. This is not a config swap; it's the one connector recommendation that needs connector changes, not just platform wiring. - Publish a single SV0 multi-tenant app registration in our Entra tenant. Customer tenant admin clicks an SV0-provided URL → admin consents → service principal materialised in their tenant with the requested Graph scopes.
- Our platform code authenticates against the customer's tenant ID using OAuth2 client_credentials with our app's secret (one platform-wide secret in
kv-sv0-prod, NOT per-tenant). - What's stored per tenant — full consent metadata, not just the tenant ID:
{ entra_tenant_id, sp_object_id, granted_app_roles, consent_at, consenting_admin_upn, consent_status }. The bare{ entra_tenant_id }is credential-correct but authorization-incomplete — without the SP object ID and granted-scope set we can't detect drift (an admin narrowing our permissions), can't audit who consented, and can't tell "consented" from "revoked." None of this is a secret; it's authorization state. - Required Graph permissions must be pinned down before this is "adopted" (not just deferred): minimum scopes to extract
User,Application,ServicePrincipal,DirectoryAuditsreads. All application-permission, all*.Read.All— least-privilege is the read-only enforcement boundary (the connector code is not). - Customer revocation: they delete our enterprise app in their Azure portal (we have no API to force-remove it). We detect token-fetch failure → mark instance disconnected and surface it.
6.3 ServiceNow connector — OAuth client_credentials + tenant admin paste-into-portal
- No federation option exists in ServiceNow. Customer creates an OAuth Application Registry record in their instance + a dedicated user with our required roles, then pastes
instance_url,client_id,client_secretinto our operator UI form. - Form
POSTs to/api/v1/admin/connector-credentials→ writes tokv-sv0-produndertenants/{id}/connector/servicenow/*(per ADR-027 Slice 5 path shape). Mongo holds only thecredentials_refpointer. - Operator sees the customer pasted a value, never sees the value itself.
- What's stored per tenant: the credential values in KV (Slice 5 path) + a
credentials_refin Mongo.
6.4 GitHub connector — SV0-published GitHub App
- Register a single SV0 GitHub App with the minimum permissions our connector needs (
metadata: read,contents: read,members: read,administration: readas applicable). - Customer org admin clicks Install on our App's URL → picks repos → consents → GitHub fires
installation.createdwebhook to our/webhooks/githubendpoint. - We mint installation tokens per scan via our App's private key.
- What's stored per tenant:
{ installation_id }. Zero per-tenant secrets for this connector. - Single-point-of-failure caveat + containment plan. The App private key mints tokens for every installation across all customer orgs — one global secret, leak = total compromise of all GitHub access. The pattern is named in §3.7; the containment plan it requires: (a) keep the private key in
kv-sv0-prod/ HSM, never on disk; (b) split prod and staging into separate GitHub Apps with separate keys; (c) document a key-rotation cadence (GitHub lets a second key coexist with the first for zero-downtime rotation); (d) alert on anomalous token-mint volume; (e) keep App permissions minimal so a leaked token's blast radius is read-only. Atlassian Connect (§6.7) avoids this single global key by issuing a per-install shared secret — a relevant contrast, though Connect then makes the lifecycle webhook the control plane (§6.7 caveat).
6.5 GCP connector — Workload Identity Federation (when GCP enters scope)
- Not in current scope per ADR-027's
Priority Connectorstable (GCP is "future"). When it lands, the recommended pattern is GCP WIF (§3.6): customer creates a workload identity pool + provider trusting our identity (an Entra-issued token from our VM), grants impersonation on a customer-side SA. - Same per-tenant shape as Entra:
{ project_id, service_account_email }. Zero per-tenant secrets.
6.6 Other SaaS (Snowflake, Okta, Workday, etc.) — paste-into-portal
- Default to the same UX as ServiceNow (§6.3): customer pastes into the operator UI; direct-to-KV write; ref pointer in Mongo.
- For each integration as it enters scope, check whether the vendor offers OAuth client_credentials (preferred), OAuth user-delegated (acceptable with refresh), or only static API keys (worst). Document the choice in that connector's per-integration doc.
6.7 Atlassian (Jira + Confluence) — Forge app + Forge Remote for Cloud; PAT for Data Center
Cloud (the common case) — build on Forge, not Connect. Connect is deprecated (§3.8); a new Connect Marketplace app would be rejected and would hit a Dec-2026 sunset. The 2026-correct design:
- Publish an SV0 Forge app on the Marketplace. Atlassian site admin installs it; Atlassian mints short-lived scoped tokens to the app at runtime.
- Use Forge Remote so the app calls our SV0-controlled backend (declare egress in
manifest.ymlpermissions.external.fetch.backend; pin remote URLs by region for data residency). This keeps our own backend/storage while using the Forge install + trust model — the historical "Forge can't ship data out" objection is resolved by Forge Remote. - What's stored per tenant: the install/site identifier (
{ atlassian_site_id / cloud_id, installation }) in Mongo ascredentials_ref. No per-install shared secret to vault and no global private key — the Forge invocation token is what our backend verifies. - Required Marketplace scopes must be pinned down before "adopted" (least-privilege, read-only): minimum scopes to extract issue history, permission schemes, and audit events.
- If a transitional Connect app is unavoidable (e.g., to ship before the Forge build lands), the per-install
sharedSecretmodel from §3.9 applies, and the lifecycle webhook becomes the credential control plane and must be hardened: verify the lifecycle JWT (iss/aud/qsh), bindclientKey+baseUrlto an SV0-originated install, stage secret replacement (write-new/health-check/promote), and alert on unexpected reinstalls. Treat this as an explicitly time-boxed exception with the Dec-2026 sunset on the calendar.
Data Center (the enterprise self-hosted case):
- Customer admin creates a Personal Access Token in their Atlassian DC instance and pastes
{baseUrl, pat}into our operator UI (same form pattern as §6.3 ServiceNow; same staff-visibility caveat from §4). - Connector probes capability on first sync: if
Authorization: Bearerfails, fall back to OAuth 2.0 if the DC instance exposes it; OAuth 1.0a Application Links if not; document basic-auth as not supported. - What's stored per tenant:
{ baseUrl, deployment: "data_center" }in Mongocredentials_ref; PAT in KV (staged rotation per §4).
Anti-pattern (do not adopt):
- A new Connect app — deprecated; Marketplace will reject it; Dec-2026 sunset. Forge is the only forward path for Cloud.
- 3LO (user-delegated OAuth) — breaks when the authorizing admin's password changes / they leave the org. Use only for developer-tool integrations where human attribution is required (none of ours).
- Personal API tokens for Cloud — tied to one admin's lifecycle; Atlassian force-expired pre-Dec-2024 tokens in 2026. Quick-start fallback only.
6.8 Anti-recommendations
- Do not ask customers to email or Slack credentials to us. Tracked as a hard rule for onboarding; goes in the runbook.
- Do not store AWS access keys / Azure client_secrets / GCP SA JSON keys per tenant when a federation pattern is available. The provider has to be in the broker's
provider:enum but real customer onboarding should not select it. - Do not build a customer-facing self-service portal in the next 6 months. Operator-mediated onboarding (operator clicks "create instance" in our UI; customer admin separately runs CloudFormation / clicks Install / pastes secret) is sufficient for design partners. A self-service portal is a 1-year follow-up tied to product GTM, not an ADR-027 concern.
7. Next Action
Status: research-complete
Decision needed from: Ivan (CTO) — direction confirmation; CEO sign-off not required (technical architecture).
Options:
-
Adopt §6 as the connector-credential roadmap. Create one umbrella issue per connector for the federation migration; sequence them after ADR-027 Slice 1 lands. Concrete next moves:
- File issue: "ADR-028: connector identity model (federation-first per connector)" — captures §6's decisions formally.
- File issue: "AWS connector: switch to cross-account assume-role + external-ID" —
sv0-platform+sv0-connectors. - File issue: "Entra connector: publish multi-tenant Entra app for SV0" —
sv0-infrastructure(app registration),sv0-platform(consent callback handler). - File issue: "Tenant admin credential-paste API endpoint" —
sv0-platform. ServiceNow needs this on day one. - File issue: "GitHub connector: publish SV0 GitHub App" —
sv0-infrastructure,sv0-platform. - File issue: "Atlassian connector: SV0 Forge app + Forge Remote (Cloud) + PAT-paste (Data Center)" —
sv0-infrastructure(Marketplace Forge listing),sv0-platform(Forge Remote backend + PAT paste handler). Not Connect — deprecated, Dec-2026 sunset.
-
Adopt §6 but defer ADR-028 until first paying customer. Just file the AWS-assume-role issue now (cheapest, biggest blast-radius reduction); revisit Entra and GitHub when the first customer needs them. Risk: every connector that lands in the meantime defaults to ADR-027's secret-bundle path, which we'd later migrate.
-
Reject §6 and stay with ADR-027's secret-bundle model for all connectors. Defensible if (a) we expect ≤3 customers in the next 6 months and (b) we accept that the procurement conversation gets harder ("you store our AWS keys?"). Document as a deliberate trade-off.
Security-hardening items (from adversarial review) that the implementation ADR-028 must carry — not optional polish:
- Tenant isolation, secret-exposure, and KV-RBAC limits are owned by
15-connector-runtime-architecture.md§2a + §4 — carry them into ADR-028 verbatim: re-derive tenant from the DB chain (never the payload); the flat-env provider is dev/demo only (not production); shared-MI KV RBAC is a path convention, not per-tenant isolation; federation isolation rests on verified-binding selection + customer-side trust scoping. - Least-privilege customer-side scopes are the real read-only boundary, not connector code — lint the onboarding templates (CloudFormation / Bicep / App manifests) to stay read-only.
- Per-connector single-secret blast radius: the GitHub App private key and the platform AWS-caller credential are global secrets — each needs KV/HSM storage, rotation cadence, prod/staging split, and anomaly alerting.
- Staged rotation everywhere (write-new, health-check, promote, revoke-old) — never zero-then-write.
Market-parity items (from market-standards review) — table stakes for an enterprise buyer / CISO conversation, not stretch goals:
- Ship versioned, linted onboarding artifacts — don't just recommend them. Mature peers (Wiz/Orca/Prisma/Lacework/CrowdStrike) hand the customer a one-click/generated package. Release-gated deliverable list: AWS single-account CloudFormation + Organizations StackSet (org-wide auto-onboarding); Azure admin-consent URL + Bicep (app reg + Graph consent + RBAC + management-group scope); GCP Terraform/Cloud-Shell WIF bootstrap; GitHub Marketplace App install flow; Atlassian Forge Marketplace listing; ServiceNow paste-form. Each artifact CI-linted to fail if it requests write scopes outside an explicit remediation path.
- Per-connector minimum-permission manifests. "Connector code is read-only" is not a procurement answer; the customer-side permission ceiling is. Pin exact least-privilege scope sets per connector (Graph
*.Read.Allset, GitHubreadpermission set, Atlassian Forge scopes, ServiceNow roles, AWSSecurityAudit+ any read-only additions) before any connector is called "adopted," and lint the onboarding artifact against them. - Incident-response runbooks for the global secrets. The GitHub App private key and the platform AWS-caller credential are global minting authorities. Each needs a documented runbook: KV/HSM key protection posture, dual-key rotation cadence, prod/staging app separation, token-mint anomaly alerting, customer-notification clock, emergency mass-revocation drill (quarterly tabletop evidence for SOC 2). Lives in
docs/runbooks/, referenced from ADR-028. - SOC 2 / ISO control mapping. Map the credential model to SOC 2 CC6.1/CC6.6 and ISO 27001:2022 A.5.15/A.5.17/A.5.18/A.8.15 — secrets management, least privilege, separation of duties (who can read a tenant's secret), rotation/change control, access logging. The flat-env Slice 1 fails several of these; that's why it's non-production (see
15-connector-runtime-architecture.md§2a).
GitHub Issues: not yet created. Listed above under Option 1.
8. References
Vendor onboarding docs
- Datadog AWS
- Datadog Azure
- Datadog GCP
- Datadog ServiceNow
- Datadog GitHub
- Wiz partner page on Google Cloud
- Wiz data connector on Microsoft Learn
- Cordant — Connecting Wiz to your Azure tenant (third-party walkthrough)
- wiz-sec/charts (Kubernetes Helm)
- Orca AWS partner page
- Orca Azure partner page
- Orca GCP partner page
- AWS Storage Blog — How Orca shares encrypted EBS snapshots
- orcasecurity/Orca_AWS_In-Account_Policies
Foundational cloud-provider docs
- AWS — External IDs for third-party access
- AWS — IAM Roles Anywhere intro
- AWS — OIDC federation
- Microsoft Entra — Convert app to multi-tenant
- Microsoft Entra — Workload Identity Federation
- GCP — Workload Identity Federation
- GitHub — Generating an installation access token
- GitHub — Differences between GitHub Apps and OAuth Apps
- Atlassian — Connect end of support: timeline and next steps
- Atlassian — Adopting Forge from Connect
- Atlassian — Forge Remote
- Atlassian — Forge Remote data-residency realm pinning
- Atlassian — Understanding JWT for Connect apps (legacy)
- Atlassian — OAuth 2.0 (3LO) apps
- Atlassian — Manage API tokens for your Atlassian account
- Atlassian — Using Personal Access Tokens (Data Center)
Standards & frameworks
- RFC 9700 — OAuth 2.0 Security Best Current Practice
- AWS Well-Architected — SEC03-BP09 (share resources securely with a third party)
- Google Cloud — Workload Identity Federation is preferred over SA keys
- Microsoft Azure Well-Architected — security (managed identity / Key Vault, minimize secrets)
- SOC 2 Trust Services Criteria CC6.1 / CC6.6; ISO/IEC 27001:2022 Annex A A.5.15, A.5.17, A.5.18, A.8.15.
SecurityV0 internal
- ADR-022 — Azure compute landing zone
- ADR-023 — Authentication target architecture
- ADR-027 — Automated connector pipeline
05-connectors.md— Connector interface contract15-connector-runtime-architecture.md— Connector runtime architecture
9. Appendix — standards mapping
Per-connector mapping of the recommended pattern to the external standard / best-current-practice it satisfies. Buyer security questionnaires (CAIQ/SIG) and auditors expect this mapping; pin it before any connector is "adopted."
| Connector | Recommended pattern | Standard / BCP it satisfies | Key control to evidence |
|---|---|---|---|
| AWS | Cross-account role + external-ID (OIDC bridge later) | AWS Well-Architected SEC03-BP09; confused-deputy mitigation via sts:ExternalId; RFC 9700 (temporary, scoped creds) | External-ID generated by SV0 + unique per account; SecurityAudit read-only ceiling; STS session ≤1h |
| Azure / Entra | Multi-tenant app + admin consent | Azure WAF security (minimize secrets, prefer identity); OIDC issuer validation (tid/iss) | Validate iss against per-tenant tid on every token; *.Read.All least-privilege Graph set; consent metadata stored |
| GCP | Workload Identity Federation | Google Cloud "WIF preferred over SA keys"; RFC 8693 token exchange | No SA JSON key; WIF attribute-condition pins our identity; impersonation scoped to one SA |
| GitHub | GitHub App | GitHub's own "Apps over OAuth Apps/PATs"; short-lived scoped tokens | App private key in KV/HSM; 1h installation tokens; read permission set; prod/staging app split |
| Atlassian Cloud | Forge app + Forge Remote | Atlassian platform direction (Connect deprecated); data-residency realm pinning | Forge invocation token verified at our backend; egress declared in manifest; region-pinned remotes |
| ServiceNow | OAuth 2.0 client_credentials | RFC 6749 client_credentials; RFC 9700 (consider sender-constrained / mTLS where the instance supports it) | Customer-provisioned OAuth client; paste-direct-to-KV; staged rotation; dedicated read-only role |
| All | Tenant admin paste / federation | RFC 9700 issuer + state validation on any consent callback; NIST SP 800-204 service-to-service identity if the broker becomes distributed | OAuth state + issuer validation on callbacks; audit log on every credential resolve |
Gaps to close before "adopted" (not just deferred): explicit OAuth state/issuer validation on the Entra and Atlassian consent callbacks; a sender-constrained-token (mTLS) assessment for ServiceNow where the instance supports it; and a NIST SP 800-204-style service-to-service identity plan if/when the credential broker fans out beyond one worker (ties to ADR-027 §Plan-D).