Skip to content

Feat/azure multitenant onboarding - #92

Open
amansingh39 wants to merge 9 commits into
authsec-stagingfrom
feat/azure-multitenant-onboarding
Open

amansingh39 wants to merge 9 commits into
authsec-stagingfrom
feat/azure-multitenant-onboarding

Conversation

@amansingh39

@amansingh39 amansingh39 commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Description

Changes

Testing

  • Unit tests pass (go test -short ./tests/unit/)
  • go vet ./... clean
  • Manual testing (describe below)
  • Add label run-integration to this PR to trigger integration tests in CI (requires a live DB)

Checklist

  • Code follows the project's style guidelines
  • Self-reviewed the diff
  • No secrets or credentials committed
  • Updated documentation if needed
  • Added/updated tests for new behaviour

amansingh39 and others added 9 commits October 7, 2026 10:46
One AuthSec multi-tenant app registration, and the customer only sees
Microsoft's own screens: admin consent creates AuthSec's service
principal and grants its Graph application permissions; a delegated
sign-in (PKCE, no refresh token) gives the administrator's ARM token,
parked in Redis for at most 15 minutes. Connect then checks the service
principal and its Graph permissions (granted, then usable), checks the
signed-in user may bind the tenant, creates Reader on each selected
subscription WITH THE ADMINISTRATOR'S TOKEN -- AuthSec's principal never
holds roleAssignments/write -- and proves each one with AuthSec's own
app-only token, polling while Azure applies it.

A tenant is one cloud_connector row (scope_kind 'tenant', migration 042)
and each subscription one row under it. Verify re-proves either without
the customer; Revoke stops using a tenant and its subscriptions. No
credential is stored: auth_ref is "azure-app:<client id>" and the secret
stays in the deployment environment.

Resource Manager and app-only tokens go through the official Azure SDK
(azcore, azidentity, armsubscriptions, armauthorization), Graph through
the Microsoft Graph SDK. Discovery is not part of this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… their access

Tenant and subscription discovery passes: Entra objects, Microsoft Entra role assignments and definitions (built-in, custom, scoped), app and delegated permissions, Azure RBAC, Key Vault, federation, sign-ins and activity, Foundry, workloads. Agents from Microsoft Entra Agent ID, the Microsoft 365 agent registry, Foundry agent applications and project agents, Azure Bot Service, Logic Apps agent loops and the Teams app catalog, plus inference, each listed once and closed when its source no longer lists it. Scheduled rescans, custom-app onboarding with credentials in Vault, and projection into the shared iga_* graph behind IGA_GRAPH_PROJECTION.
Azure migrations renumbered after staging's (042-044 -> 138-140); the duplicate sortedKeys helper dropped in favour of staging's.
138-140 now leave a constraint that already admits their value as it is, so on a database that ran them under their old numbers (042-044), or whose bootstrap already lists every value, they never narrow it under rows a later source wrote.
The Azure projection states each row's scope in provider_attrs as Kubernetes and GitHub rows do (the tenant for Entra objects; the subscription, with its resource group or region, for resources and workloads), so /api/iga/v1/inventory lists provider azure with scope, native id and as_of from the Azure discovery run. /authsec/discovery/connections lists Azure tenants and subscriptions, with their latest run, coverage and a live graph state.
The shared connections list gives an Azure tenant as the connection and counts its subscriptions in its scope summary instead of listing each one. Every Azure inventory row is scoped to its tenant, with a resource's or workload's subscription as its sub-scope and its subscription id and resource group in its attrs, so filtering by the tenant connection includes them.
Own app: the client secret is stored only after every read it enables is proven, and destroyed on revoke or when the tenant moves to the AuthSec app. Its failures are the customer's (400, credential_invalid, unknown tenant, app not in tenant, named missing permissions), never AuthSec's deployment. Warnings for Graph permissions that can change data, Azure roles beyond Reader, and no readable subscription. A tenant switching between apps moves its other subscriptions to the new credentials. Verify checks revoked first; a passing Microsoft failure no longer stops scheduled discovery; discovery fails closed when the own app's credentials cannot be read; unexpected errors no longer return internals.
Staging took migrations 138-140 (access policy delete, cloud scan schedule); the Azure migrations move to 141-143, with their references.
… pass the tenancy guardrail

Every Azure upsert overwrites a row only from a pass at least as new as the one that wrote it, and a pass that loses its lease stops. AWS's Connector, ForRun, verify and revoke act only on AWS connectors, so an Azure connector on an AWS route is not found and is never queued there. Reconnecting a tenant revokes the subscriptions not selected. The Azure projection runs under row-level security for its workspace (tenancy.InWorkspace); every Azure raw statement is bound to its workspace and carries a TENANT-EXEMPT reason, and the sign-in origin update now binds workspace_id; the guardrail is back at its baseline (72). Database tests for the fence, the AWS routes and deselection.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant