Skip to content

Latest commit

 

History

History
318 lines (239 loc) · 17.1 KB

File metadata and controls

318 lines (239 loc) · 17.1 KB

AgentBox — Identity & Auth Design

NETWORG internal — July 2025

6 services, shared Copilot account, scoped permissions per container

Overview

All users authenticate via Microsoft Entra ID (networg.com tenant only). The AI agent gets scoped access to 6 services — each with the minimum permissions required and explicit user consent at spawn time.

Key security principle: The AI agent impersonates the developer for GitHub, ADO, and JSM (scoped OAuth — user's own identity, limited scopes). For Dataverse, it uses a per-container managed identity with a user-selected security role. For SharePoint, a shared reader app gets per-site read-only grants. For Copilot CLI, users with personal subscriptions use their own token; others use a shared Copilot account (Microsoft/GitHub approved) with premium request billing. The AI's Dataverse and SharePoint permissions are decoupled from the user's own permissions.


Token Flow on the Dashboard

User signs in → Entra ID session
  ↓
"Spawn AgentBox" — spawn form appears:
  - AgentBox name
  - Four OAuth flows triggered (user authorizes once each):
    GitHub App OAuth     → ghu_* token (git + gh CLI, possibly Copilot)
    Azure DevOps OAuth   → ADO token (vso.code_write vso.work_write vso.build)
    Atlassian OAuth 3LO  → Jira token (JSM read + internal notes)
    SharePoint           → site selection (delegated Sites.Read.All for discovery)
  - Dataverse section (no OAuth — managed identity):
    → Select environments (toggle on/off)
    → Select security role per environment (dropdown)
  - SharePoint section:
    → Search/browse sites
    → Select sites to grant read access (checkboxes)
  ↓
API checks Copilot entitlement:
  GET /orgs/NETWORG/members/{username}/copilot
  → 200 (has own seat): COPILOT_GITHUB_TOKEN = user's ghu_* token
  → 404 (no seat):      COPILOT_GITHUB_TOKEN = shared account token (from Key Vault)
  ↓
API creates ACI container:
  --assign-identity (system-assigned managed identity)
  GH_TOKEN=ghu_user               → git + gh CLI (always user's own identity)
  COPILOT_GITHUB_TOKEN=ghu_xxx    → Copilot CLI (own or shared, per above)
  AZURE_DEVOPS_EXT_PAT=xxx        → az devops commands (scoped as user)
  JIRA_TOKEN=xxx                  → JSM API calls (read tickets, post internal notes)
  SHAREPOINT_SITES=xxx            → site IDs for sp-read CLI
  SP_CLIENT_ID + SP_CERT          → SharePoint Reader app credentials
  ↓
API post-provision:
  → Read managed identity Application ID from ACI resource
  → For each selected Dataverse environment:
    → Add managed identity as application user
    → Assign the user-selected security role
  → For each selected SharePoint site:
    → Grant SharePoint Reader app "read" role via POST /sites/{id}/permissions
    → Store permission IDs for cleanup
  ↓
Container starts → az login --identity → Dataverse tokens acquired automatically

1. Entra ID — Dashboard/API Authentication

  • Container Apps Easy Auth (networg.com tenant only)
  • Cookie-based SSO for dashboard, Bearer token for API
  • "AgentBox Admins" Entra ID group for fleet-wide access
  • Per-container authorization: owner, shared access list, or admin

2. GitHub Identity & Copilot Licensing

Identity Linking (Entra ID ↔ GitHub)

AgentBox reuses the existing github-organization-management identity infrastructure:

  • Entra ID directory extension attribute stores each user's GitHub numeric user ID (e.g., extension_<appid>_githubId = "12345678")
  • Users link their GitHub account once via the existing org management UI (Entra ID login → GitHub OAuth → attribute written)
  • No separate identity database — Entra ID is the single source of truth
  • At spawn time: read extension attribute → resolve GitHub username via GET /users/{id} → check Copilot entitlement

Users who haven't linked their GitHub account are redirected to the org management linking UI before they can spawn.

Shared Copilot Account (Approved)

Microsoft/GitHub experimental approval allows NETWORG to use a single shared GitHub account for Copilot CLI sessions in AgentBox. Premium requests are paid for — all users get access to premium models regardless of their personal subscription status.

  • Dedicated GitHub account (e.g., networg-agentbox-copilot) with Copilot Business seat
  • Short-lived OAuth tokens (8hr): API holds the shared account's refresh token in Key Vault, mints fresh access tokens per container spawn
  • If a user extracts COPILOT_GITHUB_TOKEN from the container, it expires in ≤8 hours (limits exposure at $0.04/premium request)
  • Token injected as COPILOT_GITHUB_TOKEN into containers for users without personal subscriptions
  • Shared token has minimal GitHub permissions — only enough for Copilot authentication, NOT repo access
  • All repository access is through the user's own GH_TOKEN
  • Initial setup: admin logs into the shared account once via GitHub OAuth on the dashboard → refresh token stored in Key Vault → API auto-refreshes thereafter

Copilot License Detection

The GitHub API provides per-user license checks:

GET /orgs/NETWORG/members/{username}/copilot
→ 200 = has personal Copilot seat (Pro+, Business, etc.)
→ 404 = no personal seat → use shared account for Copilot

Required permission: organization_copilot_seat_management: read on the GitHub App. Added to the existing github-organization-management GitHub App (already has org-level access).

Two User Classes

Has personal subscription (Pro+, Copilot Business seat) No personal subscription
Copilot CLI ✅ Own token — COPILOT_GITHUB_TOKEN=ghu_user ✅ Shared account — COPILOT_GITHUB_TOKEN=ghu_shared
Premium models ✅ via own subscription ✅ via shared account (premium requests paid by NETWORG)
Git operations Own identity (clone, commit, push, PR) Own identity (clone, commit, push, PR)
GH_TOKEN ghu_user (full: Copilot + git + gh) ghu_user (git + gh CLI only)
Cost to NETWORG $0 extra (existing subscription) Premium request usage on shared account

No automatic seat assignment. No free tier fallback. No seat provisioning API calls. All users get premium models through one of the two paths.

Spawn Flow Decision Tree

Entra ID login → read githubId extension attribute
  │
  ├── Not linked? → redirect to github-org-management linking UI
  │
  ├── Linked → resolve username → check Copilot entitlement
  │
  ├── Has personal Copilot seat (200) ─────────────────────┐
  │   → Standard OAuth flow → ghu_* token                  │
  │   → Container:                                         │
  │     GH_TOKEN=ghu_user (Copilot + git + gh)            │
  │     COPILOT_GITHUB_TOKEN=ghu_user                     │
  │   → Full Copilot CLI via own subscription ✅           │
  │                                                        │
  └── No personal seat (404) ──────────────────────────────┐
      → Split identity configuration:                      │
      → Container:                                         │
        COPILOT_GITHUB_TOKEN=ghu_shared (from Key Vault)  │
        GH_TOKEN=ghu_user (git + gh CLI)                  │
        git config user.name "Real Name"                  │
        git config user.email "dev@networg.com"           │
      → Full Copilot CLI via shared account ✅             │

Token priority in Copilot CLI: COPILOT_GITHUB_TOKEN > GH_TOKEN > GITHUB_TOKEN > keychain. Git credential helper uses GH_TOKEN — completely independent from Copilot auth.

Users with personal subscriptions avoid consuming the shared account's premium request budget.

GitHub App OAuth

Register NETWORG AgentBox GitHub App on github.com. Callback URL: https://agentbox.networg.com/auth/github/callback

Fine-grained permissions (set at app registration):

  • Contents: Read & Write (code access, git clone/push)
  • Pull requests: Read & Write (create/manage PRs)
  • Issues: Read & Write (create/edit issues, post comments)
  • Actions: Read (check workflow run results and logs)
  • Copilot (Copilot CLI authentication)

NOT requested (AI cannot do any of these):

  • Administration (no repo settings, no team management)
  • Actions: Write (no triggering or re-running workflows)
  • Environments / Deployments (no deploy access)
  • Workflows (no .github/workflows modification)
  • Secrets (no access to repo/org secrets)
  • Security events (no vulnerability management)

Three-layer access control:

  1. App permissions — what operations (set once at registration, we control)
  2. Installation scope — which repos (org admin installs app on specific repos)
  3. User intersection — token only works for repos the user AND app can both access

User access token (ghu_*) expires in 8 hours, refreshable via ghr_* refresh token. Token acts as the user (their GitHub identity, their repo access) — zero extra cost.

Repo scoping: org admin manages which repos the app is installed on. Per-spawn repo selection can be added later via repository_id (single repo) or container-level credential helper (multi-repo).

Integration with github-organization-management

Component How AgentBox Uses It
Entra ID extension attribute Read githubId at spawn → resolve username → check license
Account linking UI Redirect unlinked users before they can spawn
GitHub App Add organization_copilot_seat_management: read permission
Sync process Ensures org membership is current; AgentBox requires users be org members

See github-identity-investigation.md for the full investigation.


3. Azure DevOps OAuth

Register app at Azure DevOps OAuth registration. Scoped token acting as the user but with strictly limited permissions.

Requested scopes (and ONLY these):

  • vso.code_write — Read/write code, create/manage PRs and code reviews
  • vso.work_write — Read/write work items (create, edit, comment)
  • vso.build — Read build/pipeline run results and logs

NOT requested (AI cannot do any of these):

  • vso.build_execute — no pipeline execution or queuing
  • vso.release_execute — no release management
  • vso.project_manage — no project admin
  • vso.code_manage — no repo creation/deletion
  • vso.security_manage — no permission changes
  • vso.serviceendpoint_manage — no service connection access

Token acts as the user (inherits their project access — no extra license). But can only exercise the scoped permissions. Developer who has access to Project-A sees Project-A; one who doesn't, doesn't.

Redirect: https://app.vssps.visualstudio.com/oauth2/authorize?scope=vso.code_write vso.work_write vso.build

Project-level scoping considered and deferred: ADO OAuth/PAT scopes are organization-wide, not project-scoped. Native project isolation requires service principals/managed identities at $6/mo per identity. Accepted trade-off: the AI has the user's org-wide project visibility, constrained by OAuth scopes (code, work items, build logs — no execution). Container-level URL enforcement can be added later if needed.


4. Jira Service Management (Atlassian OAuth 2.0 3LO)

Register an Atlassian OAuth 2.0 app in the Atlassian developer console.

Strictly limited scopes:

  • read:servicedesk-request — Read customer requests/tickets
  • write:servicedesk-request — Post comments (internal notes) to tickets

NOT requested (AI cannot do any of these):

  • Transition tickets, manage service desks, manage customers/organizations
  • Access Confluence, Bitbucket, or any other Atlassian product

Token acts as the user (their JSM project access, their existing license). AI can read tickets and post internal notes ("public": false) — nothing else.

OAuth redirect: https://auth.atlassian.com/authorize?scope=read:servicedesk-request write:servicedesk-request


5. Dataverse / Power Platform (Per-Container Managed Identity)

Each ACI container gets a system-assigned managed identity (auto-created with container, auto-deleted when destroyed).

Spawn form on dashboard (per-environment configuration):

  1. Dashboard lists the user's accessible Dataverse environments (via Power Platform Admin API)
  2. User toggles on/off which environments to grant
  3. For each enabled environment, user selects a security role from a dropdown (populated from that environment's available roles)
  4. Examples: "Basic User", "Custom Read-Only", "AgentBox Developer", "System Administrator" — user's choice per task

Provisioning flow:

  1. API creates ACI container with --assign-identity (system-assigned managed identity)
  2. Reads back the identity's Application (client) ID from the ACI resource
  3. For each selected environment: adds the managed identity as an application user via Power Platform Admin API
  4. Assigns the user-selected security role to the app user in that environment

Inside the container:

  • az login --identity authenticates as the managed identity (no secrets needed)
  • Acquire Dataverse token: az account get-access-token --resource https://{org}.crm4.dynamics.com
  • Agent calls Dataverse Web API with Bearer token

Security model:

  • What operations: defined by the security role the user selected at spawn time (not the user's own role)
  • Which environments: only those explicitly enabled on the spawn form
  • Different AgentBoxes can have different roles in the same environment (read-only for exploration vs editor for changes)

Auto-cleanup: container deleted → managed identity auto-deleted → TTL cleanup function removes orphaned app users from Dataverse environments

Audit trail: each container has its own identity — Dataverse audit logs show exactly which AgentBox instance performed each operation

Zero extra licenses — application users in Dataverse are unlicensed

Local runs: managed identity is ACI-only; for local Docker, the API backend can vend Dataverse tokens using a shared service principal as a fallback (with a default restricted role)


6. SharePoint Online (Sites.Selected + Per-Site Grants)

Uses Microsoft Graph's Sites.Selected application permission with the Site Permissions API. See sharepoint-investigation.md for full investigation.

Three app registrations involved:

  • AgentBox DashboardSites.Read.All (delegated) for site discovery/search
  • AgentBox APISites.FullControl.All (application) to grant/revoke per-site permissions
  • AgentBox SharePoint ReaderSites.Selected (application) used by containers

Spawn form on dashboard (per-site configuration):

  1. Developer clicks "Add SharePoint sites" in the spawn form
  2. Dashboard searches sites using developer's delegated token
  3. Developer selects which sites to grant read access (checkboxes)

Provisioning flow:

  1. API grants the SharePoint Reader app "read" role on each selected site via POST /sites/{siteId}/permissions
  2. API stores permission IDs → { containerId → [{ siteId, permissionId }] }
  3. Container gets Reader app credentials (client ID + certificate from Key Vault)

Inside the container:

  • Pre-installed sp-read CLI tool for document retrieval
  • sp-read sites — list granted sites
  • sp-read ls <site-id> [path] — browse document libraries
  • sp-read download <site-id> <path> — download files (supports PDF conversion for .docx/.pptx/.xlsx)
  • sp-read search <query> — search across granted sites

Security model:

  • Container can ONLY read sites explicitly selected at spawn time
  • Read-only: no write, no delete, no share, no modify
  • AI cannot discover or access any other SharePoint sites

Auto-cleanup: container destroyed → API calls DELETE /sites/{siteId}/permissions/{permId} for each grant

Zero extra licenses — Graph API access included in existing M365 licenses


Summary: What the AI CAN and CANNOT Do

Service CAN CANNOT
GitHub Clone/push code, create/manage PRs, create/edit issues, read actions logs, use Copilot CLI Manage repos, trigger workflows, modify CI/CD, access secrets, manage teams
Azure DevOps Read/write code & PRs, read/write work items, read build logs Execute pipelines, manage releases, admin projects, manage permissions
JSM Read tickets, post internal notes Transition tickets, manage service desks, access Confluence/Bitbucket
Dataverse Operations defined by user-selected security role per environment Exceed assigned role, access unselected environments
SharePoint Read documents from selected sites only Write/delete/share, access unselected sites, discover other sites
Azure Use managed identity for Dataverse tokens, az CLI (scoped) Access other Azure resources (unless MI has additional role assignments)