Back to writing
Architecture · 8 min read

Building Identity into AI Applications from Day One

Identity is not a security review item you bolt on before launch; for AI applications, it is architecture that needs to run through the whole system from the first commit.

Almost every team I am talking to treats identity as a security review item, something you bolt on right before launch once legal and infosec start asking questions. For a normal CRUD app, you can mostly get away with that, badly, but you get away with it. For an AI application, this is backwards in a way that actually costs you real engineering time later, because agent identity is not a checklist item you add at the end, it is wiring that has to run through the whole thing from the first commit.

Why retrofitting identity is so much more expensive here

With a traditional app, bolting on proper authorization later mostly means adding middleware and updating some route handlers. Painful, but contained.

With an AI application, by the time someone notices the identity gap, you usually have: a growing list of tools the agent can call, each one written without a scope in mind because nobody was thinking about scopes when they wrote def send_email(to, subject, body):. A shared service account or a single API key wired into every integration, because that was the fastest way to get a demo working. No per-agent, per-session distinction in your logs, because logging was "add a print statement" not "log against an identity." Retrofitting at that point means touching every tool, reissuing every credential, and rebuilding your audit trail from nothing, all while the thing is already in production and agents are already calling those tools. That is a much bigger job than adding middleware, and it is also exactly the kind of migration that gets deprioritized forever because nothing is technically broken, it is just quietly unsafe.

The four things worth wiring in from day one

Four building blocks to wire in from day one

Give every agent instance its own identity. Not a shared service account "for now." Even in a prototype, issue a distinct client identity per agent or per deployment. It costs almost nothing at day one and it is the one piece that is genuinely painful to add after the fact, because everything downstream (tokens, scopes, audit logs) hangs off of it.

Map every tool to a scope before you write the tool, not after. When you are defining send_email or run_sql_query, decide right then what scope should gate it. This is a five minute conversation at design time and a multi-week migration once forty tools exist without one.

Design token issuance and expiry into the architecture, not into a later "security hardening" sprint. Short lived, scoped tokens (OAuth 2.0 client credentials or token exchange per RFC 8693) should be how the agent gets access from the start, even if the actual identity provider behind it is a stub in your prototype.

Build the audit trail against identity, not against generic app logs. Every tool call should be attributable to a specific agent identity and the scope it used to make that call. Bolting this on later means you have a gap in your history exactly during the period you probably most need to explain, which is early production, when things are still being figured out.

What happens if you skip this and retrofit later

The cost of retrofitting identity after the fact

I am not saying this to be dramatic, I am saying it because I am watching it happen basically every quarter with a different team. The MVP ships fast with one shared key because that is genuinely the right tradeoff for a two week prototype. Then the prototype works, more tools get added, more agents get spun up, all against the same shared credential because changing it now would mean touching working code. Eventually someone asks "which agent did this" during an incident review and the honest answer is "we cannot tell," and now the retrofit is not a nice-to-have, it is an incident follow-up item with a deadline.

A quick code example

Here is roughly what "no scope was designed in" looks like, which is the natural result of moving fast without thinking about identity first:

def send_email(to: str, subject: str, body: str):
    # works fine, called by whichever agent, however often, no scope check
    smtp_client.send(to, subject, body)

And here is what it looks like when the scope is part of the tool's definition from the moment it is written, not added later:

from dataclasses import dataclass
 
@dataclass
class Tool:
    name: str
    required_scope: str
    handler: callable
 
def send_email(to: str, subject: str, body: str):
    smtp_client.send(to, subject, body)
 
TOOL_REGISTRY = {
    "send_email": Tool(
        name="send_email",
        required_scope="email:send",
        handler=send_email,
    ),
}
 
def call_tool(tool_name: str, token, **kwargs):
    tool = TOOL_REGISTRY[tool_name]
    if tool.required_scope not in token.scopes:
        raise PermissionError(f"token missing scope: {tool.required_scope}")
    return tool.handler(**kwargs)

Nothing complicated about the second version, that is actually the point. It is barely more code. The difference is entirely about when you decided to think about it.

Standards worth reading

  • RFC 6749, The OAuth 2.0 Authorization Framework, for the base client identity and scope model.
  • RFC 8693, OAuth 2.0 Token Exchange, for how a per-agent identity gets a narrower, short lived token for a specific downstream call.
  • NIST SP 800-207, Zero Trust Architecture, for the general principle that identity and verification should be part of the architecture, not a perimeter you add once and trust forever after.

The takeaway

You do not need a fully built out identity provider on day one, a stub is fine for a prototype. What you actually need on day one is the shape: distinct identity per agent, a scope decided before the tool is written, tokens instead of shared secrets, and logs tied to identity instead of generic app output. Fill in the real infrastructure later if you must, but design the shape now, because the shape is the part that is expensive to change once real agents are running on top of it.


Akash Devdhar is a Senior Software Engineer specializing in enterprise identity, authentication, authorization, and AI infrastructure. He writes about building secure AI systems using OAuth, OIDC, RBAC, and modern identity architectures.