# Authentication and tokens

Stream uses JWT (JSON Web Tokens) to authenticate users so they can open WebSocket connections and send API requests. When a user opens your app, they first pass through your own authentication system. After that, the Stream SDK is initialized and a client instance is created. The device then requests a Stream token from your server. Your server verifies the request and returns a valid token. Once the device receives this token, the user is authenticated, and the same token works for every Stream product in your app.

Authentication proves who a user is. Whether a user is authorized to perform certain actions is managed separately via a role based permissions system; see [Permissions and roles](https://getstream.io/docs/platform/permissions/).

<Admonition type="info">

The exception is anonymous and guest users, who require no authentication to log in. Both are covered in [Users](https://getstream.io/docs/platform/users/).

</Admonition>

## Generating Tokens

You can generate tokens on the server by creating a Server Client and then using the Create Token method.

If generating a token to use client-side, the token must include the userID claim in the token payload, whereas server tokens do not. When using the create token method, pass the user_id parameter to generate a client-side token.

<Tabs>

```js label="Node.js"
const userId = "john";
// validity is optional, in this case we set it to 1 day
const validity = 24 * 60 * 60;
client.generateUserToken({ user_id: userId, validity_in_seconds: validity });
```

```python label="Python"
# pip install getstream
from getstream import Stream

server_client = Stream(
  api_key="your_api_key", api_secret="your_api_secret"
)
token = server_client.create_token("john")
```

```ruby label="Ruby"
# gem install getstream-ruby
require 'getstream_ruby'
require 'jwt'

client = GetStreamRuby.manual(api_key: 'STREAM_KEY', api_secret: 'STREAM_SECRET')
token = JWT.encode({ user_id: 'john' }, 'STREAM_SECRET', 'HS256')
```

```php label="PHP"
use GetStream\ClientBuilder;

// Initialize client
$client = (new ClientBuilder())
    ->apiKey('your_api_key')
    ->apiSecret('your_api_secret')
    ->build();

// Generate user token
$userId = 'john';
// validity is optional, in this case we set it to 1 day
$validity = 24 * 60 * 60;
$token = $client->createUserToken($userId, [], $validity);
```

```go label="Go"
// github.com/GetStream/getstream-go/v4

serverClient, _ := getstream.NewClient(APIKey, APISecret)
token, _ := serverClient.CreateToken("john")
```

```csharp label="C#"
// dotnet add package getstream-net
using GetStream;

// Instantiate your Stream client using the API key and secret
// the secret is only used server side and gives you full access to the API.
var client = new StreamClient("your_api_key", "your_api_secret");

var token = client.CreateUserToken("john");
```

```java label="Java"
// For Gradle:
// dependencies {
//   implementation "io.getstream:stream-sdk-java:$stream_version"
// }

StreamSDKClient client = new StreamSDKClient("your_api_key", "your_api_secret");

String token = client.tokenBuilder().createToken("john");
```

</Tabs>

Video additionally supports call tokens, which grant roles on specific calls. See [Video authentication](https://getstream.io/video/docs/api/authentication/).

### Manually Generating Tokens

You can use the JWT generator on this page to generate a User Token JWT without needing to set up a server client. You can use this token for prototyping and debugging; usually by hardcoding this into your application or passing it as an environment value at initialization.

You will need the following values to generate a token:

- `User ID` : A unique string to identify a user.

- `API Secret` : You can find this value in the [Dashboard](https://getstream.io/signin/).

To generate a token, provide a `User ID` and your `API Secret` to the following generator:

<TokenGenerator></TokenGenerator>

For more information on how JWT works, please visit [https://jwt.io](https://jwt.io).

## Setting Automatic Token Expiration

By default, user tokens are valid indefinitely. You can set an expiration to tokens by passing it as the second parameter. The expiration should contain the number of seconds since Unix epoch (00:00:00 UTC on 1 January 1970).

<Tabs>

```js label="Node.js"
// creates a token that expires in 1 hour using moment.js
const timestamp = Number(moment().add("1h").format("X"));
const token1 = client.createToken("john", timestamp);

// the same can be done with plain javascript
const token2 = client.createToken(
  "john",
  Math.floor(Date.now() / 1000) + 60 * 60,
);
```

```python label="Python"
# creates a token valid for 1 hour
token = server_client.create_token("john", expiration=3600)
```

```ruby label="Ruby"
# creates a token valid for 1 hour
require 'jwt'

token = JWT.encode(
  { user_id: 'john', exp: Time.now.to_i + 3600 },
  api_secret,
  'HS256'
)
```

```php label="PHP"
// creates a token valid for 1 hour
$expiration = (new \DateTime())->getTimestamp() + 3600;
$token = $client->createUserToken("john", expiration: $expiration);
```

```go label="Go"
// creates a token valid for 1 hour
token, _ := client.CreateToken("john", getstream.WithExpiration(time.Hour))
```

```csharp label="C#"
// creates a token valid for 1 hour
var token = client.CreateUserToken("john", expiration: TimeSpan.FromHours(1));
```

```java label="Java"
// creates a token valid for 1 hour
String token = client.tokenBuilder().createToken("john", 3600);
```

</Tabs>

## Token Providers

A concept we will refer to throughout the docs is a Token Provider. At a high level, the Token Provider is an endpoint on your server that can perform the following sequence of tasks:

1. Receive information about a user from the front end.

2. Validate that user information with your own authentication system.

3. Provide a User-ID corresponding to that user to the server client's token creation method.

4. Return that token to the front end.

User Tokens can only be safely generated from a server. This means you will need to implement a Token Provider prior to deploying your application to production.

Stream client SDKs accept a token provider at initialization and call it to retrieve and renew tokens. The connection flow is documented with each product: [Chat](https://getstream.io/chat/docs/node/tokens-and-authentication/), [Video](https://getstream.io/video/docs/api/authentication/) and [Feeds](https://getstream.io/activity-feeds/docs/node/tokens-and-authentication/).

## Developer Tokens

For development applications, it is possible to disable token authentication and use client-side generated tokens or a manually generated static token. Disabling auth checks is not suitable for a production application and should only be done for proofs-of-concept and applications in the early development stage. To enable development tokens, you need to change your application configuration.

On the [Dashboard](https://getstream.io/signin/):

1. Select the App you want to enable developer tokens on and ensure it is in Development mode

2. Click the App name to open the app overview

3. Scroll to the _General_ section

4. Toggle _Disable Authentication Checks_

This disables the authentication check, but does not remove the requirement to send a token. Send either a client generated development token, or manually create one and hard code it into your application. Client SDKs include helpers to generate development tokens; see the client examples on the [Chat tokens page](https://getstream.io/chat/docs/node/tokens-and-authentication/).

## Manual Token Expiration

Token Revocation is a way to manually expire tokens for a single user or for many users by setting a `revoke_tokens_issued_before` time, and any tokens issued before this will be considered expired and will fail to authenticate. This can be reversed by setting the field to null.

### Token Revocation by User

You can revoke all tokens that belong to a certain user or a list of users.

<Tabs>

```js label="Node.js"
await client.revokeUserToken("user-id", revokeDate);
await client.revokeUsersToken(["user1-id", "user2-id"], revokeDate);
```

```python label="Python"
from getstream.models import UpdateUserPartialRequest

client.update_users_partial(users=[
    UpdateUserPartialRequest(id="user-id", set={"revoke_tokens_issued_before": revoke_date})
])
client.update_users_partial(users=[
    UpdateUserPartialRequest(id=uid, set={"revoke_tokens_issued_before": revoke_date})
    for uid in ["user1-id", "user2-id"]
])
```

```ruby label="Ruby"
Models = GetStream::Generated::Models

client.common.update_users_partial(
  Models::UpdateUsersPartialRequest.new(
    users: [Models::UpdateUserPartialRequest.new(
      id: 'user-id', set: { 'revoke_tokens_issued_before' => before.iso8601 }
    )]
  )
)
client.common.update_users_partial(
  Models::UpdateUsersPartialRequest.new(
    users: ['user1-id', 'user2-id'].map { |uid|
      Models::UpdateUserPartialRequest.new(
        id: uid, set: { 'revoke_tokens_issued_before' => before.iso8601 }
      )
    }
  )
)
# before should be a Time object
```

```php label="PHP"
use GetStream\GeneratedModels as Models;

$client->updateUsersPartial(new Models\UpdateUsersPartialRequest(
    users: [new Models\UpdateUserPartialRequest(
        id: "user-id",
        set: (object)["revoke_tokens_issued_before" => (new \DateTime())->format(\DateTime::ATOM)],
    )]
));

$client->updateUsersPartial(new Models\UpdateUsersPartialRequest(
    users: [
        new Models\UpdateUserPartialRequest(
            id: "user1-id",
            set: (object)["revoke_tokens_issued_before" => (new \DateTime())->format(\DateTime::ATOM)],
        ),
        new Models\UpdateUserPartialRequest(
            id: "user2-id",
            set: (object)["revoke_tokens_issued_before" => (new \DateTime())->format(\DateTime::ATOM)],
        ),
    ]
));
```

```go label="Go"
client.UpdateUsersPartial(ctx, &getstream.UpdateUsersPartialRequest{
	Users: []getstream.UpdateUserPartialRequest{
		{ID: "user-id", Set: map[string]any{"revoke_tokens_issued_before": revokeTime}},
	},
})
client.UpdateUsersPartial(ctx, &getstream.UpdateUsersPartialRequest{
	Users: []getstream.UpdateUserPartialRequest{
		{ID: "user1-id", Set: map[string]any{"revoke_tokens_issued_before": revokeTime}},
		{ID: "user2-id", Set: map[string]any{"revoke_tokens_issued_before": revokeTime}},
	},
})
```

```csharp label="C#"
// dotnet add package getstream-net
using GetStream;
using GetStream.Models;

var client = new StreamClient("your_api_key", "your_api_secret");

await client.UpdateUsersPartialAsync(new UpdateUsersPartialRequest
{
    Users = new List<UpdateUserPartialRequest>
    {
        new UpdateUserPartialRequest
        {
            ID = "<user-id>",
            Set = new Dictionary<string, object>
            {
                ["revoke_tokens_issued_before"] = DateTimeOffset.UtcNow.AddHours(-1)
            }
        }
    }
});

// Revoke tokens for multiple users
await client.UpdateUsersPartialAsync(new UpdateUsersPartialRequest
{
    Users = new List<UpdateUserPartialRequest>
    {
        new UpdateUserPartialRequest
        {
            ID = "user1-id",
            Set = new Dictionary<string, object>
            {
                ["revoke_tokens_issued_before"] = DateTimeOffset.UtcNow.AddHours(-1)
            }
        },
        new UpdateUserPartialRequest
        {
            ID = "user2-id",
            Set = new Dictionary<string, object>
            {
                ["revoke_tokens_issued_before"] = DateTimeOffset.UtcNow.AddHours(-1)
            }
        }
    }
});
```

```java label="Java"
client.updateUsersPartial(UpdateUsersPartialRequest.builder()
    .users(List.of(UpdateUserPartialRequest.builder()
        .id("<user-id>")
        .set(Map.of("revoke_tokens_issued_before", new Date()))
        .build()))
    .build()).execute();

client.updateUsersPartial(UpdateUsersPartialRequest.builder()
    .users(List.of(
        UpdateUserPartialRequest.builder()
            .id("<user1-id>")
            .set(Map.of("revoke_tokens_issued_before", new Date()))
            .build(),
        UpdateUserPartialRequest.builder()
            .id("<user2-id>")
            .set(Map.of("revoke_tokens_issued_before", new Date()))
            .build()))
    .build()).execute();
```

</Tabs>

Note: Your tokens must include the `iat` (issued at time) claim, which will be compared to the time in the `revoke_tokens_issued_before` field to determine whether the token is valid or expired. Tokens which have no `iat` will be considered invalid.

### Undoing the revoke

To undo user-level token revocation, you can simply set revocation date to `null`:

<Tabs>

```js label="Node.js"
await client.revokeUserToken("user-id", null);
await client.revokeUsersToken(["user1-id", "user2-id"], null);
```

```python label="Python"
from getstream.models import UpdateUserPartialRequest

client.update_users_partial(users=[
    UpdateUserPartialRequest(id="user-id", unset=["revoke_tokens_issued_before"])
])
client.update_users_partial(users=[
    UpdateUserPartialRequest(id=uid, unset=["revoke_tokens_issued_before"])
    for uid in ["user1-id", "user2-id"]
])
```

```ruby label="Ruby"
Models = GetStream::Generated::Models

client.common.update_users_partial(
  Models::UpdateUsersPartialRequest.new(
    users: [Models::UpdateUserPartialRequest.new(
      id: 'user-id', unset: ['revoke_tokens_issued_before']
    )]
  )
)
client.common.update_users_partial(
  Models::UpdateUsersPartialRequest.new(
    users: ['user1-id', 'user2-id'].map { |uid|
      Models::UpdateUserPartialRequest.new(
        id: uid, unset: ['revoke_tokens_issued_before']
      )
    }
  )
)
```

```php label="PHP"
use GetStream\GeneratedModels as Models;

$client->updateUsersPartial(new Models\UpdateUsersPartialRequest(
    users: [new Models\UpdateUserPartialRequest(
        id: "user-id",
        unset: ["revoke_tokens_issued_before"],
    )]
));

$client->updateUsersPartial(new Models\UpdateUsersPartialRequest(
    users: [
        new Models\UpdateUserPartialRequest(
            id: "user1-id",
            unset: ["revoke_tokens_issued_before"],
        ),
        new Models\UpdateUserPartialRequest(
            id: "user2-id",
            unset: ["revoke_tokens_issued_before"],
        ),
    ]
));
```

```go label="Go"
client.UpdateUsersPartial(ctx, &getstream.UpdateUsersPartialRequest{
	Users: []getstream.UpdateUserPartialRequest{
		{ID: "user-id", Unset: []string{"revoke_tokens_issued_before"}},
	},
})
client.UpdateUsersPartial(ctx, &getstream.UpdateUsersPartialRequest{
	Users: []getstream.UpdateUserPartialRequest{
		{ID: "user1-id", Unset: []string{"revoke_tokens_issued_before"}},
		{ID: "user2-id", Unset: []string{"revoke_tokens_issued_before"}},
	},
})
```

```csharp label="C#"
// dotnet add package getstream-net
using GetStream;
using GetStream.Models;

var client = new StreamClient("your_api_key", "your_api_secret");

await client.UpdateUsersPartialAsync(new UpdateUsersPartialRequest
{
    Users = new List<UpdateUserPartialRequest>
    {
        new UpdateUserPartialRequest
        {
            ID = "<user-id>",
            Unset = new List<string> { "revoke_tokens_issued_before" }
        }
    }
});

// Undo revoke for multiple users
await client.UpdateUsersPartialAsync(new UpdateUsersPartialRequest
{
    Users = new List<UpdateUserPartialRequest>
    {
        new UpdateUserPartialRequest
        {
            ID = "user1-id",
            Unset = new List<string> { "revoke_tokens_issued_before" }
        },
        new UpdateUserPartialRequest
        {
            ID = "user2-id",
            Unset = new List<string> { "revoke_tokens_issued_before" }
        }
    }
});
```

```java label="Java"
client.updateUsersPartial(UpdateUsersPartialRequest.builder()
    .users(List.of(UpdateUserPartialRequest.builder()
        .id("<user-id>")
        .unset(List.of("revoke_tokens_issued_before"))
        .build()))
    .build()).execute();

client.updateUsersPartial(UpdateUsersPartialRequest.builder()
    .users(List.of(
        UpdateUserPartialRequest.builder()
            .id("<user1-id>")
            .unset(List.of("revoke_tokens_issued_before"))
            .build(),
        UpdateUserPartialRequest.builder()
            .id("<user2-id>")
            .unset(List.of("revoke_tokens_issued_before"))
            .build()))
    .build()).execute();
```

</Tabs>

### Token Revocation by Application

It is possible to revoke tokens for all users of an application. This should be used with caution as it will expire every user's token, regardless of whether the token has an `iat` claim.

<Tabs>

```js label="Node.js"
await client.revokeTokens(revokeDate);
// you can pass Date or ISOstring as value here
```

```python label="Python"
client.update_app(revoke_tokens_issued_before=revoke_time)
# revoke_time is a datetime object
```

```ruby label="Ruby"
Models = GetStream::Generated::Models

client.common.update_app(
  Models::UpdateAppRequest.new(revoke_tokens_issued_before: before)
)
# before is a Time object
```

```php label="PHP"
use GetStream\GeneratedModels as Models;

$client->updateApp(new Models\UpdateAppRequest(
    revokeTokensIssuedBefore: new \DateTime("now"),
));
// revokeTokensIssuedBefore is a DateTime object
```

```go label="Go"
client.UpdateApp(ctx, &getstream.UpdateAppRequest{
	RevokeTokensIssuedBefore: &revokeTime,
})
// revokeTime is a time.Time object
```

```csharp label="C#"
// dotnet add package getstream-net
using GetStream;
using GetStream.Models;

var client = new StreamClient("your_api_key", "your_api_secret");

await client.UpdateAppAsync(new UpdateAppRequest
{
    RevokeTokensIssuedBefore = DateTimeOffset.UtcNow.AddHours(-1).UtcDateTime
});
```

```java label="Java"
// Revocation date must be at least 60 seconds in the past
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, -2);
client.updateApp(UpdateAppRequest.builder()
    .revokeTokensIssuedBefore(cal.getTime())
    .build()).execute();
```

</Tabs>

### Undoing the revoke

To undo app-level token revocation, you can simply set revocation date to `null`:

<Tabs>

```js label="Node.js"
await client.revokeTokens(null);
```

```python label="Python"
client.update_app(revoke_tokens_issued_before=None)
```

```ruby label="Ruby"
Models = GetStream::Generated::Models

client.common.update_app(
  Models::UpdateAppRequest.new(revoke_tokens_issued_before: nil)
)
```

```php label="PHP"
use GetStream\GeneratedModels as Models;

$client->updateApp(new Models\UpdateAppRequest(
    revokeTokensIssuedBefore: null,
));
```

```go label="Go"
client.UpdateApp(ctx, &getstream.UpdateAppRequest{
	RevokeTokensIssuedBefore: nil,
})
```

```csharp label="C#"
// dotnet add package getstream-net
using GetStream;
using GetStream.Models;

var client = new StreamClient("your_api_key", "your_api_secret");

await client.UpdateAppAsync(new UpdateAppRequest
{
    RevokeTokensIssuedBefore = null
});
```

```java label="Java"
client.updateApp(UpdateAppRequest.builder()
    .revokeTokensIssuedBefore(null)
    .build()).execute();
```

</Tabs>

### Adding iat claim to token

By default, user tokens generated through the createToken function do not contain information about time of issue. You can change that by passing the issue date as the third parameter while creating tokens. This is a security best practice, as it enables revoking tokens.

<Tabs>

```js label="Node.js"
client.createToken("user-id", expireTime, issuedAt);
// issuedAt should be unix timestamp
// issuedAt = Math.floor(Date.now() / 1000)
```

```python label="Python"
token = server_client.create_token("user-id", expiration=expiry_seconds)
# The token automatically includes the iat (issued at) claim
```

```ruby label="Ruby"
require 'jwt'

token = JWT.encode(
  { user_id: 'john', exp: exp_time, iat: issued_at },
  api_secret,
  'HS256'
)
# issued_at should be a unix timestamp
```

```php label="PHP"
$token = $client->createUserToken("user-id", expiration: $expiry, claims: ["iat" => $issuedAt]);
// issuedAt should be unix timestamp
```

```go label="Go"
client.CreateToken("user-id",
	getstream.WithExpiration(expiryTime),
)
// expiryTime is a time.Duration (e.g. 24 * time.Hour)
```

```csharp label="C#"
// creates a token valid for 2 hours
// The token automatically includes the iat (issued at) claim
var token = client.CreateUserToken("user1-id", expiration: TimeSpan.FromHours(2));
```

```java label="Java"
// creates a token valid for 1 hour
// The token automatically includes the iat (issued at) claim
String token = client.tokenBuilder().createToken("john", 3600);
```

</Tabs>


---

This page was last updated at 2026-08-07T13:10:43.698Z.

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