Skip to content
theAuth

Give an AI agent its own credential with theAuth for Go

An agent that borrows a person's session can do everything that person can. This guide gives the agent its own identity, a scope it cannot exceed, and a delegation you can revoke without logging anyone out.

  • Practitioner
  • 3 min read
On this page

Before you start

  • A Go HTTP server you can add a module to.
  • A secrets manager or environment that can hold a 32 byte encryption key.
  • Users who sign in through theAuth, so the agent has someone to act for.

Two tokens, two questions

The agent gets two kinds of token. One answers who the agent is: it authenticates as itself with client credentials, and the token's subject is the agent. The other answers who it is acting for: it exchanges a user's token for a delegated one, where the user is the subject and the agent is named as the actor. Your API can then allow, log and revoke each on its own.

Set it up

  1. Step 1Add the module

    theAuth runs inside your process as an http.Handler and keeps users, sessions and tokens in your own database.

    bash
    go get github.com/glincker/theauth-go/v2
  2. Step 2Turn on the authorization server and agent identity

    Agent identity needs the authorization server, which needs a 32 byte EncryptionKey. Declare each API the agent may call as a protected resource with its scopes. The memory store is for trying it out; use SQLite, Postgres or MySQL for real data.

    auth.go
    a, err := theauth.New(theauth.Config{
    	Storage:       memory.New(),
    	BaseURL:       "https://as.example.com",
    	EncryptionKey: loadEncryptionKey(), // 32 bytes from your secrets manager
    	AuthorizationServer: &theauth.AuthorizationServerConfig{
    		Issuer: "https://as.example.com",
    		Resources: []theauth.ProtectedResource{
    			{Identifier: "https://api.example.com", Scopes: []string{"read", "write"}},
    		},
    	},
    	AgentIdentity: &theauth.AgentConfig{},
    })
    if err != nil {
    	log.Fatal(err)
    }
    defer a.Close()
  3. Step 3Register the agent for one user

    RegisterAgent creates the agent owned by a signed in user. Because Resource is set, the same call records a delegation grant from that user to the agent, limited to the scope you pass.

    go
    reg, err := a.RegisterAgent(ctx, theauth.RegisterAgentInput{
    	OwnerID:  user.ID,
    	Name:     "report-bot",
    	Scope:    []string{"read"},
    	Resource: "https://api.example.com",
    })
    if err != nil {
    	return err
    }
    // reg.Secret.ClientID and reg.Secret.Secret are the agent's credential.
  4. Step 4Let the agent authenticate as itself

    The agent uses the client credentials grant. The access token it gets has the agent as its subject, so your API knows a machine is calling.

    go
    self, err := a.ClientCredentialsToken(ctx, theauth.TokenRequest{
    	GrantType:    "client_credentials",
    	ClientID:     reg.Secret.ClientID,
    	ClientSecret: reg.Secret.Secret,
    	Resource:     "https://api.example.com",
    	Scope:        []string{"read"},
    })
  5. Step 5Act for the user through token exchange

    To act for the user, the agent trades the user's access token for a delegated one (RFC 8693). The result names the user as the subject and the agent as the actor, and it never carries more scope than the grant allows.

    go
    delegated, err := a.ExchangeToken(ctx, theauth.TokenExchangeRequest{
    	ClientID:         reg.Secret.ClientID,
    	ClientSecret:     reg.Secret.Secret,
    	SubjectToken:     userAccessToken,
    	SubjectTokenType: "urn:ietf:params:oauth:token-type:access_token",
    	Resource:         "https://api.example.com",
    	Scope:            []string{"read"},
    })
  6. Step 6Revoke access when the job is done

    Use RevokeDelegation to end the grant for one user, SuspendAgent to pause the agent, or RevokeAgent to retire it. Resource servers see the change on their next token introspection, and the user's own session is untouched.

Handle the secret once

  • Keep the encryption key out of source control and out of logs. Load it at start up.
  • Give each agent the smallest scope that does its job. A reporting agent needs read, not write.
  • Register one agent per job, so revoking one does not stop the others.
Keep going
Next

Take it further with theAuth

The product page covers what it does, how to install it and where the docs live.