NETWORG internal — July 2025
6 services, shared Copilot account, scoped permissions per container
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.
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
- 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
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.
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_TOKENfrom the container, it expires in ≤8 hours (limits exposure at $0.04/premium request) - Token injected as
COPILOT_GITHUB_TOKENinto 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
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).
| 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.
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.
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/workflowsmodification) - Secrets (no access to repo/org secrets)
- Security events (no vulnerability management)
Three-layer access control:
- App permissions — what operations (set once at registration, we control)
- Installation scope — which repos (org admin installs app on specific repos)
- 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).
| 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.
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 reviewsvso.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 queuingvso.release_execute— no release managementvso.project_manage— no project adminvso.code_manage— no repo creation/deletionvso.security_manage— no permission changesvso.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.
Register an Atlassian OAuth 2.0 app in the Atlassian developer console.
Strictly limited scopes:
read:servicedesk-request— Read customer requests/ticketswrite: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
Each ACI container gets a system-assigned managed identity (auto-created with container, auto-deleted when destroyed).
Spawn form on dashboard (per-environment configuration):
- Dashboard lists the user's accessible Dataverse environments (via Power Platform Admin API)
- User toggles on/off which environments to grant
- For each enabled environment, user selects a security role from a dropdown (populated from that environment's available roles)
- Examples: "Basic User", "Custom Read-Only", "AgentBox Developer", "System Administrator" — user's choice per task
Provisioning flow:
- API creates ACI container with
--assign-identity(system-assigned managed identity) - Reads back the identity's Application (client) ID from the ACI resource
- For each selected environment: adds the managed identity as an application user via Power Platform Admin API
- Assigns the user-selected security role to the app user in that environment
Inside the container:
az login --identityauthenticates 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)
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 Dashboard —
Sites.Read.All(delegated) for site discovery/search - AgentBox API —
Sites.FullControl.All(application) to grant/revoke per-site permissions - AgentBox SharePoint Reader —
Sites.Selected(application) used by containers
Spawn form on dashboard (per-site configuration):
- Developer clicks "Add SharePoint sites" in the spawn form
- Dashboard searches sites using developer's delegated token
- Developer selects which sites to grant read access (checkboxes)
Provisioning flow:
- API grants the SharePoint Reader app
"read"role on each selected site viaPOST /sites/{siteId}/permissions - API stores permission IDs →
{ containerId → [{ siteId, permissionId }] } - Container gets Reader app credentials (client ID + certificate from Key Vault)
Inside the container:
- Pre-installed
sp-readCLI tool for document retrieval sp-read sites— list granted sitessp-read ls <site-id> [path]— browse document librariessp-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
| 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) |