# Authentication

agentOS uses the same authentication system as [Rivet Actors](/actors/docs/authentication): clients send credentials as connection params, and you validate them server-side.

- Clients pass credentials in `params` when they connect.
- Validate them on the server in `onBeforeConnect` (throw to reject the connection), or extract user data into connection state with `createConnState` (read it in actions via `c.conn.state`).
- You can declare the credential shape with `agentOS(...)` to document what you accept, but the client's `params` is `unknown` and is not checked against it. The real check is your hook, not the types.
- AgentOS uses ordinary Rivet actor connection hooks, so authentication runs before the connection reaches an action.

## Example

The server declares the credential shape and validates it in `onBeforeConnect` (throw to reject); the client passes credentials as `params`.

examples/authentication/server.ts:

```ts
import pi from "@agentos-software/pi";
import { agentOS, setup } from "@rivet-dev/agentos";

// The credential shape clients pass when they connect. This documents the
// connection params; the client's params are typed as unknown, so the real
// check is the onBeforeConnect hook below.
interface ConnParams {
	authToken: string;
}

// Validate credentials server-side. onBeforeConnect receives the connection
// params and rejects the connection by throwing. Wired via the underlying Rivet
// Actor; see Actor Authentication for the full hook signatures.
export function onBeforeConnect(_c: unknown, params: ConnParams): void {
	if (typeof params?.authToken !== "string" || params.authToken.length === 0) {
		throw new Error("missing or invalid authToken");
	}
	// verify the token (JWT signature, lookup, ...) here
}

const vm = agentOS({
	software: [pi],
	onBeforeConnect,
});

export const registry = setup({ use: { vm } });
registry.start();
```

examples/authentication/client.ts:

```ts
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";

const client = createClient<typeof registry>({
	endpoint: "http://localhost:6420",
});

// Pass credentials when connecting. They are forwarded as the connection
// params for your server-side validation hooks to check. `params` is typed as
// unknown, so the shape is not checked against the actor's ConnParams here.
const agent = client.vm.getOrCreate("my-agent", {
	params: { authToken: "my-jwt-token" },
});

// Actions on the handle run against the authenticated connection.
// `openSession` resolves with no value; the caller keeps the chosen session ID.
await agent.sessions.open({
	agent: "claude",
	env: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY! },
});
await agent.sessions.prompt({
	content: [{ type: "text", text: "List the files in the working directory." }],
});
```

See [Actor Authentication](/actors/docs/authentication) for JWT validation, role-based access control, external auth providers, and token caching.
