grant_consent.py¶
Create or update the delegated consent grant that lets the Agent Identity
acquire tokens as the Agent User. This is the generalised CLI form of the
consent logic embedded in
create_entra_agent_ids.py.
Part of the auth-and-certs command reference.
Purpose¶
Writes an oauth2PermissionGrant on Microsoft Graph so the Agent Identity
service principal can mint delegated tokens carrying the requested scopes,
acting on behalf of the Agent User. The grant is created with
consentType: "Principal", meaning consent is recorded for the single Agent
User principal only — it is not tenant-wide admin consent.
These are delegated scopes (oauth2PermissionGrants), not application
permissions. Application permissions are app-role assignments
(appRoleAssignments) and behave differently: they grant the service principal
standalone access with no signed-in user. Agent Identity service principals are
created by extending Microsoft.Graph.AgentIdentity and have no associated app
registration, so there is no requiredResourceAccess manifest and the
admin-consent URL flow does not apply — the grant is written directly.
See Token flows → Consent records for the grant shape and how Hop 3 consumes it.
Requirements¶
.entrabot-state.jsonmust containAGENT_OBJECT_ID(the Agent Identity service-principal object ID) andAGENT_USER_ID(the Agent User object ID). Runsetup.shfirst if either is missing.- The Provisioner app must already exist with its certificate available for
cert-auth — the private key lives in the OS keystore on macOS/Linux, or in
an ACL-locked file under
%LOCALAPPDATA%\entrabot\on Windows. This command uses the existing-only token helper and never bootstraps or repairs the Provisioner app. - The Provisioner app needs
DelegatedPermissionGrant.ReadWrite.All(to write the grant) plus directory read access to resolve the resource service principal.
Usage¶
# Grant specific scopes against Microsoft Graph (the default resource)
python scripts/grant_consent.py --scopes "Chat.Create,Mail.Read"
# Grant against a different resource by app ID (e.g. Azure Storage)
python scripts/grant_consent.py \
--scopes "user_impersonation" \
--resource-app-id "e406a681-f3d4-42a8-90b6-c2b029497af1"
Options¶
--scopes(required) — comma-separated delegated scope names, e.g."Chat.Create,Mail.Read". Surrounding whitespace is trimmed and empty entries are dropped.--resource-app-id(optional) — the app ID of the resource to consent against. Defaults to Microsoft Graph (00000003-0000-0000-c000-000000000000).
Effects¶
- Mints a Provisioner Graph token; the certificate private key is read in memory only — from the OS keystore on macOS/Linux, or from the Windows file-backed store.
- Resolves the resource app ID to its service-principal object ID via
$filter=appId eq '<app-id>'. - Lists existing grants filtered by
clientId(Agent Identity) andprincipalId(Agent User), then selects the grant whoseresourceIdmatches the resolved resource. - Reconciles scopes idempotently:
- All requested scopes already present → prints
[skip], makes no change. - Some scopes missing →
PATCHthe grant with the union of existing and requested scopes. Only missing scopes are added; existing scopes are never removed. - No grant exists →
POSTa new grant withconsentType: "Principal", the sorted requested scopes, and astartTimeof now (UTC). ThePOSTretries up to four times (15s, 30s, 45s backoff) when Graph reports the principal is not yet propagated.
Exit behavior¶
0— grant created, updated, or already satisfied ([skip]).1— Provisioner token failure, missingAGENT_OBJECT_ID/AGENT_USER_ID, resource service principal could not be resolved, no valid scopes provided, or a GraphPATCH/POSTfailure (including exhausted propagation retries). Diagnostics are printed to stdout.2— argument-parsing error (for example,--scopesomitted entirely).
Security¶
The Provisioner token and certificate material never touch disk or logs. The grant only widens delegated access for the specific Agent User principal; review requested scopes before granting.
Common failures¶
- Missing state —
AGENT_OBJECT_ID/AGENT_USER_IDnot in.entrabot-state.json; runsetup.sh. - Propagation — running immediately after provisioning may hit "Principal was not found"; the built-in retries usually cover it.
- Insufficient Provisioner permissions — a
403on the write means the Provisioner lacksDelegatedPermissionGrant.ReadWrite.All.
Related commands¶
grant_files_consent.py— ensures the Agent User's grant includes the Files/Sites scope set.revoke_consent.py— inverse operation.provisioner-token.py— mint a Graph token for manual inspection.show_permissions.py— list the current delegated grants.create_entra_agent_ids.py— the provisioning script this generalises.