Part 1 of 2. Continue with Keeping an AI Agent Within Its Authority.
Connecting an AI assistant to your email, calendar, or booking account lets it work with information you would otherwise have to look up yourself. Allowing it to make changes means deciding how much authority to give it. A request to arrange a trip might include a spending limit and permission to keep working while you are away. Those conditions need to govern the actions the application takes on your behalf.
Authentication and authorization give us a starting point: establish an identity, then check its permissions. For a task carried out on someone’s behalf, we also need a record of what that person approved. The application may already have access to several bookings, while the current request concerns just one. If it hands the work to a background agent or obtains fresh credentials later, the approved limits still need to apply. The user must also have a way to withdraw that approval.
To see how these requirements fit together, we’ll follow a single booking change through a fictional Travel App. Alice asks for a different departure and sets a budget. We’ll establish her right to change the booking, the application’s permission to represent her, and the approval needed for this particular job. The example connects OAuth and OpenID Connect to the application rules that define that authority. It assumes familiarity with applications calling services, but no particular identity product or permission engine.
1. Alice gives the Travel App a bounded job
Alice opens the Travel App and asks its AI assistant to move booking ABC123 to tomorrow morning. The route and passengers should stay the same, and the change may cost no more than CHF 500 extra. If no suitable fare is available, the app should keep looking while she gets on with her day.
A human assistant arriving at a travel office might bring a written authorization, a Vollmacht, allowing them to arrange the change within Alice’s limits.
The employee would check Alice’s identity and rights to the booking, the assistant’s authority to represent her, and whether the proposed change falls within the written limits.
Checking the document’s signature helps establish who issued it. Its contents specify what the assistant may do. The employee still needs to identify the assistant presenting it.
In software, these checks have familiar names. Authentication checks evidence supporting an identity. Authorization determines whether access is allowed. Delegation lets a representative exercise authority on another’s behalf. Enforcement is what makes those decisions effective: the employee performs an allowed change and refuses an unapproved one.
This comparison describes the checks at the counter; it makes no claim about the legal requirements of a Vollmacht. In our system, Alice is the user and the Travel App is her representative. The Booking Service accepts the app’s requests and submits permitted changes to the airline’s API.
The Travel App’s language model can propose a CHF 700 upgrade. The Booking Service must reject it because Alice’s limit is CHF 500.
The request involves three permission questions:
What may Alice do? Change bookings she owns.
What may the Travel App do for Alice? Act as her approved booking representative.
What did Alice authorize for this task? Change
ABC123once, within the confirmed itinerary and spending limits.
The first two can be broader than the third. Alice might own ten bookings and use the Travel App for all of them. That gives us no reason to change the other nine today.
Figure 1. The Booking Service checks the proposed change against Alice’s rights, the representative’s rights, and the approved task.
Alice’s instruction still needs clarification and confirmation. Before recording that approval, the application must establish which account she is using.
2. Sign-in gives the Travel App access
The Travel App sends Alice’s browser to an identity service. This is the system responsible for authenticating accounts and issuing access tokens in our example. Alice signs in there, perhaps with a password or passkey. The Travel App does not receive her password.
The identity service checks evidence bound to the account: a previously registered password, for example, or a cryptographic response produced with a registered passkey. Supplying the name “Alice” alone cannot authenticate the account.[1]
The app needs to learn who signed in and obtain access for its calls to the Booking Service.
OAuth provides the framework for obtaining access. OpenID Connect, or OIDC, adds the sign-in layer. Our identity service performs both roles: it is the OAuth authorization server and the OpenID Provider.[2][3]
The handover
Before Alice arrives, the operators register the Travel App, configure its browser return address, and establish which issuer the services trust. A client ID identifies the registration and can be public. The accepted issuer and return addresses are fixed by configuration; a request cannot introduce arbitrary replacements.
Our example uses a server-side application. The Travel App server handles the tokens; Alice’s browser maintains a session with the Travel App. The sign-in follows an authorization-code flow:
The Travel App directs the browser to the identity service, identifying the application and requesting access.
The identity service authenticates Alice and applies its access-grant policy. It may ask for consent or recognize an existing authorization where appropriate.
It returns the browser to the Travel App with a short-lived authorization code.
The Travel App server redeems that code directly at the identity service and receives the tokens after the required checks succeed.
The ID token tells the Travel App who authenticated. The access token goes to the Booking Service with API requests. The Booking Service requires that access token; it cannot accept the ID token in its place.[3]
The code handover must be protected. In particular, PKCE (Proof Key for Code Exchange) ties code redemption to a fresh secret created for that authorization attempt, so stealing the code alone should not be sufficient to redeem it. Redirect validation, application authentication where required, and checks binding the response to the correct login attempt also matter. A maintained protocol implementation should perform these checks together.[4][5]
Alice’s browser now has a session with the Travel App. The access token stays on the Travel App server in this design. Alice still needs to approve the rebooking task.
Figure 2. The browser mediates the code handover. Tokens return directly to the Travel App server, which uses the API access token when calling the Booking Service.
What the access token means
Our access token is opaque. The Booking Service looks it up at the issuer to learn what it represents.
It is also a bearer token: possession is enough to present it, without separate proof of control of a cryptographic key. A stolen token may therefore be usable by the thief. Bearer tokens need protection in transit and storage.[6]
The two properties are independent:
Opaque describes how the recipient interprets the token.
Bearer describes what is required to use it.
A JWT, or JSON Web Token, carries claims: statements such as an identifier or expiry time. A signed JWT access token is a representation that a recipient can often validate locally. It can still be a bearer token. A valid signature alone is insufficient; issuer, audience, lifetime, and token purpose must also be acceptable. Signing does not hide the contents.[7]
The Travel App presents its opaque bearer token in the request:
Authorization: Bearer <access-token>
The Booking Service asks the configured issuer about the token through introspection, authenticating its own lookup. The result lets it establish whether the token is active and obtain the information needed to evaluate its use.[8]
For our first request, the relevant meaning is:
Subject: Alice’s account Whose access the token represents.
Audience: Booking Service Which protected recipient may accept it.
Scope:
bookings.writeThe granted category of access.Active and within its lifetime The credential meets the applicable validity checks.
The labels illustrate our configuration; bookings.write is an application-defined scope. The service checks the intended recipient and operation as well as the token’s activity. Because this is a bearer token, these checks cannot establish that Alice personally sent the request.
With Alice’s identity established, the Booking Service can check her relationship to ABC123 in the application’s permission records.
3. Relationships determine Alice’s rights
The Booking Service consults a permission service, which stores relationships between people and resources and evaluates rules defining what those relationships permit.
For Alice, the first fact is simple:
Fact: Alice owns booking ABC123.
Rule: A booking’s owner may rebook it.
Both are necessary. “Owner” is a relationship name whose consequences come from our policy. A different application could allow an owner to view a booking while requiring approval for changes.
This is relationship-based access control, or ReBAC. A relation names a kind of connection, such as owner or member. A relationship is a particular instance: Alice owns this booking. Rules combine these connections to determine permission.
Google’s Zanzibar paper describes a large-scale system built around relationship tuples and configured authorization rules. It covers relationships involving groups of users and the consistency problems that arise when permissions change. Zanzibar implements this approach at Google’s scale. The booking rules here are our own application policy.[9]
Connecting identity to the permission model
The Booking Service obtains Alice’s identity from validated token information and maps it to its internal user object, user:u123. It does not accept a request field saying user=Alice as proof.
The mapping must preserve the identity’s namespace. OIDC uses the combination of issuer and subject to identify a user. Two issuers can both use u123 for different people. Our example has one configured issuer, but the application still owns the mapping into its permission model.[3]
The permission question has three parts:
Subject: user:u123
Permission: rebook
Resource: booking:ABC123
The subject is the identity being checked; the resource is the object being protected. The service chooses rebook because that is the permission required by the operation. The Travel App cannot select an easier permission or substitute another user.
Alice owns ABC123, so the ownership rule allows her to rebook it.
Following a second relationship
Company bookings need an additional rule. Suppose a booking belongs to Acme, whose travel managers may change it even when someone else owns it.
The rule becomes: a subject may rebook if they own the booking or manage travel for the organization to which it belongs.
Figure 3. Ownership and the organization’s travel-manager relationship satisfy different branches of the same rule. An arbitrary connection in the graph grants nothing by itself.
Alice owns the booking. Rule evaluation: The owner branch succeeds. Result: Allow.
Bob does not own it, but is Acme’s travel manager; the booking belongs to Acme. Rule evaluation: The organization’s travel-manager branch succeeds. Result: Allow.
Bob is an ordinary Acme member, with neither ownership nor the travel-manager relationship. Rule evaluation: Neither branch succeeds. Result: Deny.
The Booking Service asks whether the subject may rebook in each case. The rule specifies which relationships count: ownership or travel management for the booking’s organization. Ordinary Acme membership satisfies neither branch.
Roles still have a place. “Travel manager” is a role scoped to an organization, represented here through a relationship. Other restrictions depend on attributes, such as the proposed price. The final rebooking decision will need both relationship checks and conditions on the quote.[10]
Maintaining relationships and enforcing decisions
In our application, the booking-creation workflow records ownership. An authorized administrative workflow assigns travel managers. A transfer must update the relevant relationship. The permission service evaluates the data maintained by those workflows; it cannot repair stale ownership by checking Alice’s token again.
The service’s own interface is protected too. Permission to query a relationship model does not imply permission to alter it, and the Travel App’s model has no tool for assigning itself rights.
Once the permission service returns an answer, the Booking Service must obey it. This makes the Booking Service the enforcement point. It owns the code path capable of submitting a change to the airline. A check in the Travel App’s interface might improve the user experience, but cannot replace the check at that protected operation.[10]
Alice’s ownership allows her to rebook ABC123. The Booking Service must also establish whether the Travel App may act for her and what limits she approved for this request.
4. Alice approves a representative and a task
Alice can own a booking without having authorized every application to change it. The Travel App needs its own relationship granting it permission to represent her.
A principal is an identifiable person or software entity that can hold rights. A service account represents a non-human principal. A workload identity identifies running software; depending on the design, several instances can share it. For this example, the Travel App has the service identity service:travel-app.
When Alice first enrolled in the Travel App, before today’s request, an authenticated account-management flow let her enable the Travel App as a booking representative. That flow checked the application’s enrollment policy and stored the relationship. The Travel App’s language model could not create it.
Our standing policy now asks:
May Alice rebook
ABC123? And mayservice:travel-appact for Alice on booking matters?
Both checks must concern the same Alice. Permission to represent Bob cannot be combined with Alice’s ownership of a booking. The second check protects Alice’s user object: a person can be the subject of one permission question and the resource of another.
How the service learns which representative is acting
Our identity service supports service tokens and explicit actor information through OAuth token exchange. This lets the Booking Service identify the representative from validated credentials. A caller-supplied actor=travel-app field would not prove that identity.
The Travel App first authenticates to the identity service with a signed JWT assertion. It signs the assertion with its private key, and the identity service validates it using the configured public key. The assertion is evidence for client authentication.[11]
The Travel App uses that authentication in a client-credentials grant to request a service token. Our issuer’s configuration maps the Travel App’s registered client to its service identity and permits issuance for the intended exchange use. After authenticating the client, the identity service evaluates the token request and issues a service token if policy permits. This token is separate from the assertion used to authenticate.[2][11]
The Travel App can then request token exchange, presenting a token representing Alice and its service token as the actor token. In our chosen deployment, the issuer validates both and permits this combination under its exchange policy. It returns access intended for the Booking Service with Alice as the represented subject and the Travel App as the current actor.[12]
The Booking Service obtains both identities through authenticated introspection of the opaque token. The issuer must expose subject and actor information on this path; support for token exchange or JWT actor claims alone is insufficient.
Identifying Alice as the subject and the Travel App as the actor adds explicit actor attribution to the delegation already involved in ordinary OAuth access on a user’s behalf. In impersonation, the acting party is treated as the represented party within the authorized context.[12]
Someone who steals this bearer token can present its actor information too. Sender-constrained tokens add proof tied to a separate key; actor attribution and proof of the present sender solve different problems. Our running example retains bearer tokens and their corresponding protection requirements.[13]
Turning Alice’s instruction into an approved task
The standing relationship allows the Travel App to represent Alice on booking matters. Her CHF 500 limit belongs in a task grant, a protected record of the authority she approves for this job. We define this record in the application; OAuth does not specify its contents.
The Travel App proposes an interpretation of Alice’s instruction. The application asks her to confirm the departure window, expiry, and permitted specialist. For this example, she confirms the following record on 26 September 2026:
Task grant
g42Represented user Alice (
user:u123)Initial representative
service:travel-appBooking and operation Rebook
ABC123Route and passengers Preserve the current booking’s recorded route and passengers.
Departure 27 September 2026, 06:00–12:00, Europe/Zurich
Maximum additional charge CHF 500, including fees
Expiry 26 September 2026, 20:00, Europe/Zurich
Permitted specialist Fare Finder, which can search and request a change within these limits
Execution allowance One completed rebooking across this task and its child tasks
Alice sees the proposed 06:00–12:00 window and confirms it. The model’s interpretation alone would not authorize those values. Dates and identifiers throughout this example are fictional.
Alice confirms through a dedicated application form, separate from the model’s tools. The Travel App server validates the session and the form’s anti-forgery protection, then forwards the confirmation through an authenticated server connection to the Booking Service’s approval endpoint. The endpoint accepts confirmations only from that application path, binds Alice’s validated identity to the exact record version she saw, checks her rights and the delegation policy, and stores the approved version as g42. The model’s tools cannot submit confirmations, edit approved limits, or write permission relationships.
This approval covers a bounded range of fares. A different business policy could require Alice to confirm the exact final itinerary and price. Either way, approval must be tied to the significant details that the service will enforce.[14]
When the Travel App later supplies g42, the Booking Service loads the stored record and compares its user, permitted actor, booking, operation, and limits with the validated request. The reference locates the record; the service still checks the caller’s authority to use it.
Alice has confirmed one rebooking within a defined departure window and spending limit. The Booking Service holds the approved record, and the Travel App has access identifying Alice and the software acting for her. The search can now proceed under that authority.
In Keeping an AI Agent Within Its Authority, Fare Finder takes over the search. We’ll follow the approved limits through that handover and the wait for a quote, then see what the service checks before changing the booking.
Technical notes
Protocol details behind the sign-in
The browser-mediated code handover is the front channel; direct token redemption is the back channel. A session cookie identifies the browser’s interaction with the Travel App in our design. It does not need to expose the Booking Service’s access token. For code flows, PKCE, redirect checks, client authentication where applicable, and appropriate state/OIDC nonce handling must be implemented and validated together. These bind protocol messages to the intended interaction; they do not decide Alice’s booking permission.[4][5]
JWT specifies a token format. Purpose and security depend on the claims, validation, and surrounding protocol. Similarly, an OAuth client registration and an application service account need a deliberate mapping; equal-looking identifiers do not establish equivalence. For stronger sender proof, DPoP and certificate-bound tokens using mutual TLS are distinct mechanisms to investigate. Neither follows merely from including an actor name.[7][11][13]
Sources and further reading
These sources support the protocol and security concepts discussed in this part. The services, permission rules, task grants, and airline capabilities are explicit choices in the fictional example.
NIST, Digital Identity Guidelines: Authentication and Authenticator Management. SP 800-63B-4. Evidence of authenticator control, account binding, and cryptographic authentication.
IETF, The OAuth 2.0 Authorization Framework. RFC 6749, particularly sections 1, 4.1, 4.4, and 6. Read with the later security guidance in reference 4.
OpenID Foundation, OpenID Connect Core 1.0, errata set 2. Specification. ID tokens, identity claims, and issuer-and-subject identity mapping. Issuer policy must permit the API access used in this example.
IETF, Best Current Practice for OAuth 2.0 Security. RFC 9700. Security requirements around authorization flows and token use.
IETF, Proof Key for Code Exchange. RFC 7636. The code-verifier and code-challenge mechanism.
IETF, Bearer Token Usage. RFC 6750. Bearer semantics, request presentation, and token protection.
IETF, JSON Web Token and JWT Best Current Practices. RFC 7519 and RFC 8725. Token formats and validation requirements.
IETF, OAuth 2.0 Token Introspection. RFC 7662. Protected lookups, response fields, and caching considerations.
Pang et al., Zanzibar: Google’s Consistent, Global Authorization System. USENIX ATC 2019 paper. Relationship tuples, configured rules, and consistency across authorization changes.
OWASP, Authorization Cheat Sheet. Guidance. Relationship and attribute-based access control, enforcement, and deny-by-default behavior.
IETF, JWT Profile for OAuth 2.0 Client Authentication and Authorization Grants. RFC 7523. Client assertions and the distinction between authentication and grants.
IETF, OAuth 2.0 Token Exchange. RFC 8693, especially sections 2 and 4.1. Subject and actor tokens, actor claims, and lifecycle limitations.
IETF, Sender-constrained access. DPoP, RFC 9449 and mutual TLS and certificate-bound access tokens, RFC 8705.
OWASP, Transaction Authorization Cheat Sheet. Guidance. Binding authorization to significant transaction details and final execution.




