
Guide
Agentic Ops
Access Control
User authentication: a guide for developers in the era of autonomous AI agents
Guillaume Rigal
Authentication looks solved. Then an agent signs in with a user's credentials, and you find out what your setup actually assumed.
User authentication has been settled for a long time. A person types something, your system verifies it, and the session that follows belongs to that person. Every decision downstream was made on that assumption: how long a session lasts, what gets written to the log, who can cut access off and how fast.
AI agents break the assumption, and they are already in production. An agent connected to a backoffice signs in, calls the API, reads records, and increasingly changes them. Ask the obvious questions and the answers get uncomfortable quickly. Whose credentials is it using? If they belong to a user, does it get everything that user can do, on every record they can reach, at machine speed? When that credential leaks, who can cancel it, and how long does it take?
Nothing there is exotic. It is the same authentication you already have, asked about a caller it was never designed for.
So this guide is that re-examination, in two halves. The basics first: authentication versus authorization, how a login works today, and which of SSO, SAML, OIDC, SCIM and MFA covers what. Skim it if you build this regularly, but it is worth a look, because most teams have one gap in there and it tends to be the same one. Then the agent half: how an agent authenticates, how to think about the credential you hand it, when it needs an identity of its own, and what your log has to record once software is acting for somebody.
What is the difference between authentication and authorization?
Authentication establishes who is calling. Authorization decides what they may do. Authentication happens once, at the start of a session, and produces an identity. Authorization happens on every single request, using that identity.
If you are here, this is probably the table you wanted.
Authentication | Authorization | |
|---|---|---|
The question | Who are you? | What are you allowed to do? |
When | Once, at the start of a session | Every request |
Produces | An identity, usually carried in a token | A yes or a no |
Fails as | 401 Unauthorized, which is misnamed and means unauthenticated | 403 Forbidden |
Owned by | Your identity provider, or your login code | Your permission model |
The two fail differently, and teams debug the wrong one constantly. If somebody can sign in but cannot see what they expect, that is authorization, and reading the login flow will not help.
The agent question above is this confusion at scale. "The agent authenticates as the user" answers only the first column. What the agent may then do is a permission model, covered in our guide to user roles and permissions. Everything below is authentication.
How does user authentication actually work?
The three steps have not changed: present a credential, verify it, receive proof of the verification so you do not repeat it on every request. What has changed is how much of that you write, and the answer is almost none. Every method that became standard in the last few years moves the risky part elsewhere:
OAuth 2.0 and OpenID Connect turned "let this application act for this user" into a protocol rather than a per-integration design decision.
Hosted identity providers took password storage, reset flows, breach detection and MFA enrollment off your roadmap.
Passkeys and WebAuthn removed the shared secret. No password to steal, and the credential is bound to your domain, so it will not fire on a convincing copy of your login page.
Magic links and one-time codes removed the password from the user's side, not from yours. A usability improvement, not a security one.
What none of them decides is the part that determines what your logs are worth: how long a credential stays valid, and how you cancel it.
Most systems now issue a short-lived access token plus a longer-lived refresh token, and the split exists for one reason. A signed token cannot be withdrawn once issued, so a short access token caps how long a stolen one is useful and the refresh token is the thing you can actually cancel. Expiry is not a nuisance setting. In a token system it is most of the revocation you have. Choose it deliberately, and shorter for anything non-interactive.
Which of SSO, SAML, OIDC, SCIM and MFA do you actually need?
Teams buy one of these and assume they bought the others. That gap is why leavers keep their access.
Stands for | What it does | The job it covers | |
|---|---|---|---|
SSO | Single sign-on | One login across many applications | Convenience, and one place to enforce policy |
SAML | Security Assertion Markup Language | The older SSO protocol, XML-based, still standard in enterprise | How the login is transported |
OIDC | OpenID Connect | The newer one, JSON-based, sits on top of OAuth 2.0 | How the login is transported, and who the user is |
SCIM | System for Cross-domain Identity Management | Pushes user creation, updates and deactivation from your identity provider into each application | Lifecycle. This is what removes a leaver everywhere |
MFA | Multi-factor authentication, called 2FA when there are exactly two factors | Requires a second proof beyond the password | Making a stolen password insufficient |
SSO without SCIM is the gap almost everybody has. SSO means a leaver can no longer sign in through the identity provider. It does not mean their account is gone, their roles are revoked, or the API key they created two years ago stopped working. Closing that needs SCIM, or an offboarding process somebody runs. The wider version of that problem, joiners and movers as well as leavers, is in our guide to permission management.
The MFA factors are not equivalent either. SMS is phishable and SIM-swappable. Only a hardware key or passkey survives a convincing phishing page, because the credential will not present itself to the wrong domain.
How does an AI agent authenticate?
Everything above assumed a person at a keyboard. This is where that stops being true, and where the practices that worked for people need revisiting rather than replacing.
An agent authenticates in one of two shapes, and they are not interchangeable.
Delegated: the agent acts as a user. The user signs in, authorizes the agent, and it receives a token carrying their identity. Every request it makes is that user's request. This is what OAuth was designed for, and it is the right shape whenever a person asked for the specific work.
Standing: the agent acts as itself. The agent holds a credential representing itself and holds exactly the role you attached to that identity: a defined set of operations on a defined set of records, decided when you created the account rather than inherited from anybody. This is the right shape when nothing human triggered the run, and what that role should look like when an agent rather than a person holds it is covered in our guide to role-based access control.
The difference is governance. A delegated token dies when the user's access is revoked, so somebody is already maintaining it without meaning to. A standing credential is maintained by nobody unless you assign an owner.
Treat the agent as an adversary, and the questions get easier
The sharpest position we have heard comes from security teams in regulated fintech, and it is worth borrowing whatever you are building. They treat an AI agent as an adversary by default. Not because it is expected to misbehave, but because it is software holding a credential: anything you hand it is assumed leakable, and anything leaked is assumed replayed to pull data out.
The assumption is useful because it replaces an unanswerable question with three answerable ones.
Ask | Why it decides the risk |
|---|---|
How narrow is the identity behind the credential? | A stolen token is worth exactly what the identity can reach. Narrowing the identity is the only control that caps the damage rather than shortening it |
How long does the credential live? | Lifetime is the difference between an incident and a window. An agent making rapid calls does not need an hour |
How fast can it be revoked, and by whom? | If revocation requires an admin, a ticket and a deploy, it will not happen on the day it matters |
Two settings follow directly. Shorten the lifetime, since a non-interactive caller is not inconvenienced by refreshing. And declare which clients you accept, because an OAuth flow authorizes any registered client by default: a token from a client you never registered is rejected before it reaches your data.
It does not buy you a credential that is safe by construction. The realistic goal is a small blast radius and fast revocation, not an unstealable token.
How this works in Forest. Both shapes exist, and the test above decides which one you are in.
Delegated | Standing | |
|---|---|---|
The agent signs in as | A user, by OAuth or an application token | A service account, with credentials of its own |
It can reach | What that user can reach, no more and no less | What its own role and scopes allow |
The log names | That user | The service account, under its own name |
Revoked by | The user who issued the token, with no admin in the loop | An administrator, by disabling the account or rotating its credentials |
Use it when | A person asked for this specific action | The run is scheduled or event-driven, with nobody behind it |
An assistant running on an operations analyst's machine is the left column. It signs in with that analyst's account, inherits their role and scopes, and its calls appear under their name. A nightly reconciliation job is the right column, and no person's credentials should be anywhere near it.
Narrowing a delegated agent below what its user can reach uses the mechanism you already use for people: point it at a separate account with a narrower role. Neither column has a special agent path, which is the part worth taking away. The permission model you already review is the one governing the agent.
How does a service account work for AI and automation?
A service account is an identity that belongs to software rather than to a person. It has credentials, it holds a role, it appears in your logs under its own name, and nobody signs in as it.
It exists for attribution. In an audited system every action has to be traceable to an entity, human or not, and "the system did it" is not an answer a regulator accepts when a scheduled job moves money at three in the morning. The entity needs a name, and the name has to outlive whoever configured it.
Four things make an account a service account rather than a shared credential:
A named owner. A person is accountable for it. The account is not theirs and nobody logs in as it.
Its own grant, built from the operations the automation performs, rather than copied from a person's role.
Its own line in the log, under its own name.
A review date, because nothing else will ever prompt anybody to look at it. Service accounts belong in the same access review as people, and they are the item most often left out of it.
For instance, in Forest a service account is a first-class identity on the same permission model as any human user: its own role, its own scopes, its own line in the activity log. The practical consequence is that it gets reviewed where people get reviewed, rather than living in a spreadsheet of integrations that nobody owns and nobody opens.
Why a shared API key fails an audit. A key pasted into three services and a password manager is not an identity. It is a password with no owner. That creates four problems, and in a regulated business they compound:
No attribution. The log says the key acted. It cannot say which service, which person, or which of the four people who hold it.
No revocation. Cancelling it breaks everything that shares it, and nobody knows the full list, so it does not get cancelled.
No scope. Keys are almost always issued broad, because narrowing one means finding out what uses it.
No rotation. For the same reason. A credential nobody dares rotate is a permanent credential.
One identity per caller removes all four at once. It costs nothing at the moment you create an integration and is expensive eighteen months later.
What should your log record when an agent acts?
Four fields. The middle two are the ones teams leave out and regret.
Field | What goes in it |
|---|---|
Who is accountable | The user, on a delegated call. The service account, otherwise |
What made the call | The assistant, the scheduled job, the interface, a direct API call |
Which application held the token | Delegated calls only |
What changed | The record, the field, the value before, the value after |
Two examples, so it is clear what lands where.
A support analyst asks their assistant to issue a refund. Accountable identity: the analyst. Origin: the assistant. Record only the assistant and the log says a robot issued a refund. Record only the analyst and the refund looks like she clicked it herself, so six months later nobody can tell an integration was involved.
A nightly job re-screens the sanctions list. Accountable identity: the service account. There is no second name, and that is correct. The origin still matters, because a call from the reconciliation service and a call from somebody's laptop using that service's key should not look the same in the log.
That is the whole specification. It is tedious to build, easy to defer, and urgent on the day somebody asks for a year of evidence. It is also not your product.
Which is the argument for putting a backoffice on infrastructure that already does it. Forest sits on top of the databases and data sources you already have, gives every user and every service account an identity on one permission model, and records every operation and action against the record, field by field. Forest's MCP Server exposes that same model to AI clients, so an agent arrives already inside the permissions and already in the audit trail rather than as a new integration to govern separately. It is the layer regulated fintechs including Qonto and Swan run their operations on.
Wiring an agent to your data takes a day. Building the identity, permission and audit layer that makes it safe to do so takes a quarter. Forest gives you that layer already built.
Frequently asked questions about user authentication
Should an AI agent have its own login?
Only if nothing human triggered the work. If a user asked for a specific action, the agent should carry that user's identity, so their permissions and their name apply. If the run is scheduled or event-driven with nobody behind it, it needs a standing identity of its own. A customer filling in a form on your website does not count as a human user: they hold no permissions in your system, so there is nothing to inherit, and that run needs a service account.
Is OAuth authentication or authorization?
Authorization. OAuth grants an application permission to act on somebody's behalf. OpenID Connect is the layer on top that adds authentication, telling you who the person is.
What about agents that belong to another company?
Same starting point. A partner's agent, or a tool one of your own teams bought, still needs an identity you issued and a grant you control. That case is covered in permissions beyond your own team.
Authentication is where accountability starts
Everything downstream of the login inherits what the login established. A permission model cannot scope what it cannot identify, and an audit trail cannot name what was never distinguished.
None of the work differentiates your product, which is the argument for not building it. What an identity is permitted to do, once you know who it is, is in our guide to user roles and permissions.
More questions about user authentication
How should you store passwords?
Hash them with a slow, memory-hard algorithm built for the purpose: Argon2id, scrypt or bcrypt. Never a general-purpose hash like SHA-256, which is fast, and fast is the wrong property here. Every password gets its own random salt, stored alongside the hash.
If you use a hosted identity provider you are not storing passwords at all, which remains the right answer for most teams.
Is biometric authentication worth implementing?
Not directly, and you almost certainly should not try. The biometric check happens on the user's device and never reaches your server: a fingerprint or a face unlocks a passkey held in the device's secure hardware, and what you receive is a cryptographic signature.
So the practical way to offer biometrics is to support passkeys and WebAuthn, which gives you phishing resistance in the same move. Handling raw biometric data yourself brings storage, consent and regulatory obligations that are not worth taking on.
How do you protect against brute force attacks?
Rate limit by account and by source address, and keep the two independent. An attacker spraying one common password across ten thousand accounts never trips a per-account limit.
Prefer increasing delays after failed attempts over a hard lockout, since a lockout is itself a denial-of-service vector: anyone who knows a colleague's email can lock them out. Then watch the pattern rather than the count. Many accounts, one source, one password is credential stuffing, and it looks nothing like somebody forgetting their password.
What makes a good password policy today?
Shorter than the rules most teams still enforce. Current guidance has moved away from forced composition rules and scheduled expiry, because both push people toward predictable patterns: a capital at the front, a digit and an exclamation mark at the end, incremented every quarter.
What replaces them is length as the main lever, a check of new passwords against known breached lists, and a forced change only when there is evidence of compromise rather than on a calendar.
