# pip install stream-chat
import stream_chat
server_client = stream_chat.StreamChat(
api_key="{{ api_key }}", api_secret="{{ api_secret }}"
)
token = server_client.create_token("john")
Tokens & Authentication
Authentication vs Authorization
Stream uses JWT (JSON Web Tokens) to authenticate chat users, enabling them to login. Knowing whether a user is authorized to perform certain actions is managed separately via a role based permissions system.
The exception is Anonymous and Guest users who require no authentication to login.
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:
Receive information about a user from the front end.
Validate that user information against your system.
Provide a User-ID corresponding to that user to the server client’s token creation method.
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. To conduct development before implementing a Token Provider, you will need to disable token authentication.
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, where as server tokens do not. When using the create token method, pass the user_ID parameter to generate a client-side token.
# gem install stream-chat-ruby
require 'stream-chat'
server_client = StreamChat::Client.new(api_key='STREAM_KEY', api_secret='STREAM_SECRET')
server_client.create_token('john')
// composer require get-stream/stream-chat
$server_client = new GetStream\StreamChat\Client("STREAM_API_KEY", "STREAM_API_SECRET");
$token = $server_client->createToken("john");
// github.com/GetStream/stream-chat-go/v3
serverClient, _ := stream.NewClient(APIKey, APISecret)
token := serverClient.CreateToken("john", expiredAt, issuedAt)
// nuget install stream-chat-net
using StreamChat.Clients;
// Instantiate your Stream client factory using the API key and secret
// the secret is only used server side and gives you full access to the API.
var factory = new StreamClientFactory("{{ api_key }}", "{{ api_secret }}");
var userClient = factory.GetUserClient();
var token = userClient.CreateToken("john");
// Define values.
const api_key = "{{ api_key }}";
const api_secret = "{{ api_secret }}";
const user_id = "john";
// Initialize a Server Client
const serverClient = StreamChat.getInstance(api_key, api_secret);
// Create User Token
const token = serverClient.createToken(user_id);
// You need to have io.getstream.chat.apiKey and io.getstream.chat.secretKey
// properties available, or STREAM_KEY and STREAM_SECRET environmental variables.
// For more info: https://github.com/GetStream/stream-chat-java
var token = User.createToken("john", null, null);
// User Tokens must be generated server-side, check server-side SDKs for examples
// For Testing/Development - read below how you can enable "Developer Tokens" and skip token generation for development purposes
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).
// 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,
);
# creates a token valid for 1 hour
token = chat_client.create_token(
'john',
exp=datetime.datetime.utcnow() + datetime.timedelta(hours=1)
)
# creates a token valid for 1 hour
client.create_token('john', exp: Time.now.to_i + 3600)
// creates a token valid for 1 hour
$expiration = (new DateTime())->getTimestamp() + 3600;
$token = $client->createToken("john", $expiration);
var calendar = new GregorianCalendar();
calendar.add(Calendar.MINUTE, 60);
var token = User.createToken("john", calendar.getTime(), null);
// creates a token valid for 1 hour
token := client.CreateToken("john", time.Now().UTC().Add(time.Hour))
// at the moment we don't have a Swift client for server side usage
// You can use our user token generator here during development: https://getstream.io/chat/docs/token_generator/?language=swift
// creates a token valid for 1 hour
var token = userClient.CreateToken("john", DateTimeOffset.UtcNow.AddHours(1));
// user tokens must be generated server-side, check other languages for examples
// user tokens must be generated server-side, check other languages for examples
// User Tokens must be generated server-side, check server-side SDKs for examples
// For Testing/Development - read below how you can enable "Developer Tokens" and skip token generation for development purposes
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 certain user or list of users
await client.revokeUserToken("user-id", revokeDate);
await client.revokeUsersToken(["user1-id", "user2-id"], revokeDate);
client.revoke_user_token("user-id", revokeDate)
client.revoke_users_token(["user1-id", "user2-id"], revokeDate)
client.RevokeUserToken("user-id", &revokeTime)
client.RevokeUsersToken([]string{"user1-id", "user2-id"}, &revokeTime)
$client->revokeUserToken("user-id", $revokeTime);
$client->revokeUsersToken(["user1-id", "user2-id"], $revokeTime);
client.revoke_user_token("user-id", before)
client.revoke_users_token(["user1-id", "user2-id"], before)
# before should be a datetime object
var issuedBefore = DateTimeOffset.UtcNow.AddHours(-1);
await userClient.RevokeUserTokenAsync("<user-id">", issuedBefore);
await userClient.RevokeManyUserTokensAsync(new[] { user1.Id, user2.Id }, issuedBefore);
var calendar = new GregorianCalendar();
User.revokeToken("<user-id>", calendar.getTime()).request();
User.revokeTokens(List.of("<user1-id>", "<user2-id>"), calendar.getTime()).request();
// This is a server-side feature
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 valid.
Undoing the revoke
To undo user-level token revocation, you can simply set revocation date to null
:
await client.revokeUserToken("user-id", null);
await client.revokeUsersToken(["user1-id", "user2-id"], null);
client.revoke_user_token("user-id", None)
client.revoke_users_token(["user1-id", "user2-id"], None)
client.RevokeUserToken("user-id", nil)
client.RevokeUsersToken([]string{"user1-id", "user2-id"}, nil)
$client->revokeUserToken("user-id", null);
$client->revokeUsersToken(["user1-id", "user2-id"], null);
client.revoke_user_token("user-id", nil)
client.revoke_users_token(["user1-id", "user2-id"], nil)
await userClient.RevokeUserTokenAsync("<user-id">", null);
await userClient.RevokeManyUserTokensAsync(new[] { user1.Id, user2.Id }, null);
User.revokeToken("<user-id>", null).request();
User.revokeTokens(List.of("<user1-id>", "<user2-id>"), null).request();
// This is a server-side feature
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
await client.revokeTokens(revokeDate);
// you can pass Date or ISOstring as value here
client.revoke_tokens(revokeTime)
# revokeTime is a datetime object
client.RevokeTokens(&revokeTime)
// revokeTime is a time.Time object
$client->revokeTokens($revokeTime)
// revokeTime is a DateTime object
client.revoke_tokens(before)
#before is a datetime object
await appClient.RevokeTokensAsync(DateTimeOffset.UtcNow.AddHours(-1));
var calendar = new GregorianCalendar();
App.update().revokeTokensIssuedBefore(calendar.getTime()).request();
// This is a server-side feature
Undoing the revoke
To undo app-level token revocation, you can simply set revocation date to null
:
await client.revokeTokens(null);
client.revoke_tokens(None)
client.RevokeTokens(nil)
$client->revokeTokens(null);
client.revoke_tokens(nil)
await appClient.RevokeTokensAsync(null);
App.update().revokeTokensIssuedBefore(null).request();
// This is a server-side feature
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
client.createToken("user-id", expireTime, issuedAt);
// issuedAt should be unix timestamp
// issuedAt = Math.floor(Date.now() / 1000)
client.create_token(self, user_id, exp=expiryTime, iat=issuedAt)
#issueTime should be a Unix timestamp
client.CreateToken("user-id", expiryTime, issuedAt)
// issuedAt should be unix timestamp
// issuedAt := time.Now().Unix()
$client->createToken("user-id", $expiry, $issuedAt);
// issuedAt should be unix timestamp
client.create_token('john', exp: expTime, iat: issuedAt)
# issuedAt should be a unix timestamp
var token = userClient.CreateToken(user1.Id,
expiration: DateTimeOffset.UtcNow.AddHours(2),
issuedAt: DateTimeOffset.UtcNow);
var expiry = new GregorianCalendar();
expiry.add(Calendar.MINUTE, 60);
var issuedAt = new GregorianCalendar();
var token = User.createToken("john", expiry.getTime(), issuedAt.getTime());
// This is a server-side feature
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:
Select the App you want to enable developer tokens on
Click Appname to enter the Chat Overview
Scroll to the Authenticationsection
Toggle Disable Auth Checks
Click Save
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.
val user = User(
id = "bender",
name = "Bender",
image = "https://bit.ly/321RmWb",
)
val token = client.devToken(user.id)
client.connectUser(user, token).enqueue { /* ... */ }
await client.connectUser(
{
id: "john",
name: "John Doe",
image: "https://getstream.io/random_svg/?name=John",
},
client.devToken("john"),
);
User user = new User();
user.setId("bender");
user.setName("Bender");
user.setImage("https://bit.ly/321RmWb");
String token = client.devToken(user.getId());
client.connectUser(user, token).enqueue(result -> { /* ... */ });
import StreamChat
client.connectUser(userInfo: .init(id: "john-doe"), token: .development(userId: "john-doe"))
final user = User(id: "john", extraData: {
"name": "John Doe",
"image": "https://getstream.io/random_svg/?name=John",
});
await client.setUser(
user,
client.devToken("john"),
);
const FUser User{TEXT("john")};
const FString Token = Client->DevToken(User.Id);
Client->ConnectUser(
User,
Token,
[](const FOwnUser& UserRef)
{
// Connection established
});
var userName = "The Amazing Tom";
var userId = StreamChatClient.SanitizeUserId(userName); // Remove disallowed characters
var userToken = StreamChatClient.CreateDeveloperAuthToken(userId);
var credentials = new AuthCredentials("API_KEY", userId, userToken);
// Create chat client
var client = StreamChatClient.CreateDefaultClient();
// Connect user
var localUserData = await client.ConnectUserAsync(credentials);
The above code used the connectUser call. The connectUser call is the most convenient option when your app has authenticated users. Alternatively, you can use setGuestUser if you want to allow users to chat with a guest account or the connectAnonymousUser if you want to allow anonymous users to watch the chat.
Manually Generating Tokens
You can generate a token manually using the JWT generator.
If you need to test your app before you have a Token Provider and you don’t want to enable developer tokens, you can hardcode tokens from this generator into your app for testing.
How to Refresh Expired Tokens
If you’re using tokens with an expiration date you’ll want to update tokens as soon as a token exception occurs. Our React, RN, iOS, Android and Flutter libraries have built-in support for this.
Here is the regular flow to handle tokens with expiration with a token provider:
Chat is initialized using the API Key and the token provider
The Chat client will use the token provider to fetch the token when
connectUser
is calledWhen the token expires, the API will return a specific Authentication error code
The client will pause API requests and use the token provider to obtain a fresh token
The token provider returns a new token (ie. from your backend)
Chat client replaces the old token with the new one and use it for all waiting and future API calls
A token provider is a function or class that you implement and that is responsible for requesting a new token from your own login infrastructure.
The most common token provider implementation does an HTTP call to your backend with the ID of the user as well as a valid session id or secret needed to authenticate them.
val user = User(
id = "bender",
name = "Bender",
image = "https://bit.ly/321RmWb",
)
val tokenProvider = object : TokenProvider {
// Make a request to your backend to generate a valid token for the user
override fun loadToken(): String = yourTokenService.getToken(user)
}
client.connectUser(user, tokenProvider).enqueue { /* ... */ }
User user = new User();
user.setId("bender");
user.setName("Bender");
user.setImage("https://bit.ly/321RmWb");
TokenProvider tokenProvider = new TokenProvider() {
@NotNull
@Override
public String loadToken() {
return yourTokenService.getToken(user);
}
};
client.connectUser(user, tokenProvider).enqueue(result -> { /* ... */ });
// Completion handler based connect method
chatClient.connectUser(
userInfo: .init(id: userID),
tokenProvider: { providerResult in
loadChatToken(completion: providerResult)
},
completion: { error in
if let error = error {
print("Connection failed with: \(error)")
} else {
// User successfully connected
}
}
)
// or alternatively, using the async-await method
let connectedUser = try await chatClient.connectUser(
userInfo: .init(id: userID),
tokenProvider: { providerResult in
loadChatToken(completion: providerResult)
}
)
func loadChatToken(completion: @escaping (Result<Token, Error>) -> Void) {
NetworkingLayer.getChatToken() { token in
do {
let token = try Token(rawValue: token)
completion(.success(token))
} catch {
completion(.failure(error))
}
}
}
const client = new StreamChat("api_key");
const userID = "vishal";
client.connectUser({ id: userID }, async () => {
// make a request to your own backend to get the token
const response = await httpBackend.post("/chat-token/", { userID });
return response.token;
});
public async Task ConnectWithTokenProvider()
{
// If your backend exposes a simple endpoint to get token from a url you can use our predefined token provider and provide delegate for URL construction
var tokenProvider = StreamChatClient.CreateDefaultTokenProvider(userId
=> new Uri($"https:your-awesome-page.com/api/get_token?userId={userId}"));
await Client.ConnectUserAsync("api-key", "local-user-id", tokenProvider);
// For more advanced cases you can implement the ITokenProvider
var yourTokenProvider = new YourTokenProvider();
await Client.ConnectUserAsync("api-key", "local-user-id", yourTokenProvider);
}
// You can write your own implementation of the ITokenProvider
public class YourTokenProvider : ITokenProvider
{
public async Task<string> GetTokenAsync(string userId)
{
// Your logic to get the auth token from your backend and generated with Stream backend SDK
var token = "";
return token;
}
}