# Auth

> **Note:** This page covers every SDK. Each sample is labelled with its language and where it runs: the client (your app, signed in as the user) or the server (with your API secret).

Every request to Stream Agents is authenticated in one of two ways:

| Side        | Runs in                               | Holds                    | Can reach                     |
| ----------- | ------------------------------------- | ------------------------ | ----------------------------- |
| Server side | Your server, workers, scripts         | API key and API secret   | Everything in your Stream app |
| Client side | The browser, Android, iOS and Flutter | API key and a user token | That user's own sessions      |

The API key is public. Your server uses the secret to mint a token for each signed-in user, and the app connects with the key and that token.

>
> **Danger:** The secret is not public: never ship it in an app or a web page.
>

## Server side

On your server, set your key and secret in the environment:

```sh
export STREAM_API_KEY=your_api_key
export STREAM_API_SECRET=your_api_secret
```

The client reads them, so it needs no arguments:

**Node.js (server)**

```ts label="JavaScript"
import { Client } from "@stream-io/stream-agents";

const client = new Client();
```

**Python (server)**

```python label="Python"
from vision_agents.plugins import stream

client = stream.Client()
```

**Ruby (server)**

```ruby label="Ruby"
require "getstream/vision_agents"

client = GetStream::VisionAgents::Client.new
```

**PHP (server)**

```php label="PHP"
use GetStream\VisionAgents\Client;

$client = new Client();
```

**Go (server)**

```go label="Go"
api, err := client.New(stream.Backend{})
if err != nil {
	log.Fatal(err)
}
```

**.NET (server)**

```csharp label="C#"
using GetStream.VisionAgents;

using var client = new VisionAgentsClient();
```

**Rust (server)**

```rust label="Rust"
use vision_agents::Client;

let client = Client::from_env()?;
```

The SDK signs a short-lived server token with the secret on every request. Your server can configure and sync agents, and read and manage every session in your Stream app.

## Client side

An app holds only the public API key. Once the user has signed in, hand the SDK a token your server minted for them:

**JavaScript (client)**

```ts label="JavaScript"
import { Client } from "@stream-io/stream-agents";

const client = new Client({ apiKey: "your_api_key" });
await client.setUser("jlahey", () =>
  fetch("/api/agent-token").then((response) => response.text()),
);
```

**Kotlin (client)**

```kotlin label="Kotlin"
val agents = StreamAgents(apiKey = "your_api_key")
agents.setUser(User(id = "jlahey")) { yourBackend.agentToken() }
```

**Swift (client)**

```swift label="Swift"
let agents = StreamAgents(apiKey: "your_api_key")
agents.setUser(User(id: "jlahey")) { try await yourBackend.agentToken() }
```

**Dart (client)**

```dart label="Dart"
final agents = VisionAgents(
  apiKey: 'your_api_key',
  userId: 'jlahey',
  token: () => http.read(Uri.parse('https://your-app.example.com/api/agent-token')),
);
```

The token can be a string, or a function that fetches one from your server. The Kotlin and Swift SDKs ask the function once and keep the token, then ask again when the router refuses it with a 401 and send the request once more. The JavaScript SDK does the same in releases after 0.3.1, where it also asks again shortly before the token's `exp` and when a session's socket is refused; 0.3.1 and earlier ask the function before every request.

When the user signs out, call `clearUser()`: the SDK forgets them and their token, and asks for a user again before its next request.

A client only reaches its signed-in user's own sessions. It can open, watch, ask, search, fork, rewind, rename, close and delete them. Configuring agents, syncing, and reading call records are server-side only. Each page lists what a client may call, such as [Sessions](https://getstream.io/agents/docs/sessions/#client-vs-server).

### Guests

A guest lets somebody talk to an agent before they sign up. The app asks for one with the public API key alone, and acts as it with `setUser(guest)`, which takes the token the guest was minted with. In JavaScript:

**JavaScript (client)**

```ts label="JavaScript"
import { Client } from "@stream-io/stream-agents";

const client = new Client({ apiKey: "your_api_key" });

// Remembered in a cookie, else localStorage, so a reload is the same guest with the same
// conversations. An expired guest is refreshed rather than replaced.
const guest = await client.guestUser({ name: "Guest" });
await client.setUser(guest);

const session = await client.agent("support").sessions.create();
```

`guestUser({ fresh: true })` starts over as a new guest, for a "not me" button. When the guest signs up or signs in, your server moves their conversations onto the account, and the app forgets the guest and sets the real user:

**Node.js (server)**

```ts label="JavaScript"
// On your backend, with STREAM_API_KEY and STREAM_API_SECRET in the environment,
// once it has authenticated the account the guest turned out to be.
import { Client } from "@stream-io/stream-agents";

const server = new Client();
await server.claimGuestUser(guestId, "jlahey");
```

In JavaScript, `client.forgetGuest()` clears the remembered guest before `setUser` names the account. Claiming is server-side only: a client able to claim could take anybody's conversations by guessing a guest id. A Stream app that has turned guests off refuses `guestUser` with a 403, from a client and from your server alike. A router that admits no anonymous callers refuses only the client: `guestUser` throws a `ConfigurationError`, and your server mints the guest with `guestUser` and hands it to the client for `setUser`.

## Generating a token

For testing, mint one with the CLI:

```sh
getstream token jlahey --ttl 2h
```

In production, your server mints the token after it has signed the user in, and returns it to the app. A token is a JWT signed with your API secret (HS256), naming the user in `user_id`.

>
> **Warning:** A token must carry an expiry: a token without `exp` is refused.
>

Stream's server SDKs mint one for you:

**Node.js (server)**

```ts label="JavaScript"
import { StreamClient } from "@stream-io/node-sdk";

const stream = new StreamClient(
  process.env.STREAM_API_KEY!,
  process.env.STREAM_API_SECRET!,
);

const token = stream.generateUserToken({
  user_id: "jlahey",
  validity_in_seconds: 3600,
});
```

**Python (server)**

```python label="Python"
from getstream import Stream

stream = Stream(api_key=os.environ["STREAM_API_KEY"], api_secret=os.environ["STREAM_API_SECRET"])

token = stream.create_token("jlahey", expiration=3600)
```

**Go (server)**

```go label="Go"
stream, err := getstream.NewClient(os.Getenv("STREAM_API_KEY"), os.Getenv("STREAM_API_SECRET"))
if err != nil {
	log.Fatal(err)
}

token, err := stream.CreateToken("jlahey", getstream.WithExpiration(time.Hour))
```

The same token signs that user in to Stream Chat and Video, so one endpoint on your server serves all three.

---

For the most recent version of this documentation, visit [https://getstream.io/agents/docs/auth/](https://getstream.io/agents/docs/auth/).