Advanced authentication

This page provides further information about how to authenticate TalkJS sessions and requests. It assumes that you have experience setting up basic authentication.

Long-life authentication setups

If you generate tokens that expire after a period of time, the chat will eventually stop working if the user keeps the page open long enough. For example, the tokens generated in the basic authentication guide expired after 24 hours. The best way to fix this is by automatically generating a new token ('refreshing' it) before the old one expires.

Refreshable tokens

This is the recommended method for long-life authentication. TalkJS sessions using refreshable tokens can keep working forever, and are even more secure than basic authentication. However, this is the most complex authentication method.

Adding support for refreshable tokens requires 3 changes:

  • On your backend, add an endpoint that generates tokens
  • On the frontend, write a function that requests a token from that endpoint
  • Pass that function to TalkJS when creating a session

First create a new endpoint on your backend that calls your token generation code and returns the token in the response. This is not a special proprietary TalkJS endpoint, it's a normal endpoint like any other in your backend. Copy an existing endpoint if you have one.

We'll assume your endpoint is GET https://<YOUR_DOMAIN>/talkjs/auth, and returns the token as text. However, you could instead use a POST request, a different URL, or return the token inside a JSON object. You will pass TalkJS a function that calls your endpoint, so the specific endpoint configuration doesn't matter.

Make sure your endpoint rejects unauthorized requests. If a user is banned from your service or not logged in yet, your endpoint should refuse to generate a token. Otherwise, malicious users could exploit it to impersonate others and evade bans.

Test your solution by sending a request to your new endpoint with a tool like Postman. It should respond with a token when the request is valid.

Next, we will write a function on the frontend that TalkJS can call whenever the current token is about to expire. This function should send a request to your endpoint and return the token as a string.

Using our example from before, we could write:

1async function fetchNewToken() {
2 const response = await fetch('https://<YOUR_DOMAIN>/talkjs/auth');
3 const token = await response.text();
4 return token;
5}

Like before, there's nothing TalkJS-specific in this code. Copy an existing API call if you have one. We have used fetch in this example but you could use a library to send the HTTP request. Use whatever method you're comfortable with.

Finally, pass that function when creating the TalkJS session:

1const talkSession = new Talk.Session({
2 appId: '<APP_ID>',
3 me: me,
4 tokenFetcher: fetchNewToken,
5});

TalkJS will call tokenFetcher on startup to get the initial token, and will call it again every time the current token is about to expire.

Test the solution. If TalkJS loads without errors, then you are done. You don't have to worry about sessions expiring after 24 hours.

If TalkJS stops working once you provide a tokenFetcher, check the developer console for any helpful warnings or errors. Check the network tab to see whether the request to your endpoint is working. If you're still stuck, send us a message via the chat on this page. Our developers will be happy to help.

Optional: Now that you can refresh tokens, you can use a short expiry to improve security without hurting the user experience. For example, to generate tokens lasting 1 hour:

1// Uses `jsonwebtoken`: https://www.npmjs.com/package/jsonwebtoken
2import jwt from 'jsonwebtoken';
3
4const encoded_jwt = jwt.sign({ tokenType: 'user' }, '<SECRET_KEY>', {
5 issuer: '<APP_ID>',
6 subject: '<USER_ID>',
7 expiresIn: '1h',
8});
9console.log(encoded_jwt);

Each time a token expires, it will trigger a request to your server. If your tokens expire too quickly, this can add up to a considerable amount of load. Therefore we don't recommend using tokens that expire in less than 10 minutes.

Tip: If you are using server-side rendering, try providing both token and tokenFetcher when creating the session. This skips the initial tokenFetcher request and slightly improves startup speed.

Long-life token

Making your token last longer is a much simpler but less secure alternative to refreshable tokens. For example, rather than generating tokens that expire after 24 hours, you can generate tokens that last 7 days:

1// Uses `jsonwebtoken`: https://www.npmjs.com/package/jsonwebtoken
2import jwt from 'jsonwebtoken';
3
4const encoded_jwt = jwt.sign({ tokenType: 'user' }, '<SECRET_KEY>', {
5 issuer: '<APP_ID>',
6 subject: '<USER_ID>',
7 expiresIn: '7d',
8});
9console.log(encoded_jwt);

This allows each TalkJS session to last 7 days instead of just 24 hours. Chat sessions will still expire eventually, but it is much less likely for users to keep the page open for an entire week.

Think carefully before increasing the expiry of your tokens. Tokens lasting a week are much less secure because it means that any leaked or stolen tokens can be exploited by malicious users for much longer.

Non-expiring token

As an extreme alternative to long-life tokens, it is possible to generate tokens that never expire, lasting forever.

This is not recommended. If a user is banned from your service, they can keep using their old token to access the TalkJS API forever. If a token is leaked or stolen, there is no way to revoke it, and it will stay valid forever. The only solution would be to rotate your secret key, causing downtime for legitimate users.

However, this is the simplest way to make sure that TalkJS chats keep working forever, if security is not a priority. To generate a non-expiring token:

1// Uses `jsonwebtoken`: https://www.npmjs.com/package/jsonwebtoken
2import jwt from 'jsonwebtoken';
3
4const encoded_jwt = jwt.sign({ tokenType: 'user' }, '<SECRET_KEY>', {
5 issuer: '<APP_ID>',
6 subject: '<USER_ID>',
7});
8console.log(encoded_jwt);

Tip: Consider using a token with a month-long expiry instead. It is extremely unlikely that a user will keep a tab open for an entire month before trying to use the TalkJS chat. However, it means that if a malicious user discovers a token you leaked multiple years ago, they won't be able to use it.

Signature-based verification (Legacy)

Legacy signature-based verification is equivalent to using a non-expiring token. We no longer recommend this method because the signature is a custom, non-standard token. New customers should use one of the other methods on this page.

The signature is a token created on your server by calculating HMAC-SHA256 hash of the current user ID, signed with your TalkJS secret key. For example:

1import crypto from 'crypto';
2
3const userId = '<USER_ID>';
4const secretKey = '<SECRET_KEY>';
5
6const signature = crypto
7 .createHmac('sha256', secretKey)
8 .update(userId)
9 .digest('hex');
10console.log(signature);

You pass this signature when creating the TalkJS session, similar to how you pass a token. For example, in PHP, you might do:

1const talkSession = new Talk.Session({
2 appId: "<APP_ID>",
3 me: me,
4 signature: "<?= $signature ?>",
5});

Token reference

All TalkJS tokens are JSON Web Tokens (JWTs). These are industry-standard authentication tokens which grant access to a specific resource. In the context of TalkJS, they grant access to a specific user's data, or the data related to a specific app ID.

When TalkJS receives a JWT from a client, it checks that the token has been cryptographically signed using your secret key. This means that nobody can grant access to your data without access to that secret key.

JWTs contain three sections: header, payload, and signature. We will discuss the specific requirements for each section. This helpful introduction is a great place to start if you want to learn more about what JWTs are and what each section does.

TalkJS JWTs must incude "alg": "HS256" in the header. This specifies that the HMAC-SHA256 algorithm will be used to sign the JWT. HS256 is the only algorithm supported by TalkJS.

TalkJS JWTs may include "typ": "JWT" in the header. Some JWT libraries will add this to explicitly specify that the token is a JWT. This is optional and has no effect. We assume that all tokens are JWTs.

Payload

TalkJS JWTs must include "iss": "<APP_ID>" in the payload, replaced with your app ID. This "issuer" claim indicates who generated the token. Since you sign the JWT with that app ID's secret key, you cannot forge the issuer.

TalkJS JWTs may include an expiry claim (exp) in the payload. This is a numeric UNIX epoch second timestamp, indicating when the token expires. For example, "exp": 1718018815. Requests using the token after that date will be rejected with status 401.

TalkJS JWTs must include a tokenType claim. This indicates whether the JWT grants access to a specific user's data ("user-scoped"), or to the entire app's data ("app-scoped").

Most of the time, you will be generating user-scoped tokens. These are used to authenticate a TalkJS Session and show a chat UI.

User-scoped JWTs must include "tokenType": "user". This indicates that the token is user-scoped.

User-scoped JWTs must include "sub": "<USER_ID>". This indicates which user's data the token grants access to. The user does not have to exist yet.

User-scoped JWTS must last at least 3 minutes before expiring.

For admin tasks, like exporting a list of every message in your app, you will need to generate an app-scoped token. The REST API requires app-scoped tokens.

App-scoped JWTs must include "tokenType": "app". This indicates that the token is app-scoped.

Signature

TalkJS JWTs must be signed using your secret key. Specifically, you must use the secret key associated with the app ID in the payload's iss claim.

You can find your secret key on the TalkJS dashboard settings page. It will start with sk_test or sk_live.

TalkJS JWT examples

Example 1: A user-scoped token for user ID EXAMPLE_USER in app ID EXAMPLE_APP. Never expires. Signed with the secret key sk_live_example:

1{ // Header
2 "alg": "HS256"
3}
4{ // Payload
5 "tokenType": "user",
6 "iss": "EXAMPLE_APP",
7 "sub": "EXAMPLE_USER"
8}
eyJhbGciOiJIUzI1NiJ9.eyJ0b2tlblR5cGUiOiJ1c2VyIiwiaXNzIjoiRVhBTVBMRV9BUFAiLCJzdWIiOiJFWEFNUExFX1VTRVIifQ.L2xKxkn0mpK46PKP_S384N0mT1Flog38NAaaiy3nG-I(inspect)

Example 2: An app-scoped token for app ID EXAMPLE_APP. Expired at the start of the year 2020 (UNIX epoch time 1577836800). Includes optional typ header. Signed with the secret key sk_live_example:

1{ // Header
2 "alg": "HS256",
3 "typ": "JWT"
4}
5{ // Payload
6 "tokenType": "app",
7 "iss": "EXAMPLE_APP",
8 "exp": 1577836800
9}
eyJhbGciOiJIUzI1NiJ9.eyJ0b2tlblR5cGUiOiJhcHAiLCJpc3MiOiJFWEFNUExFX0FQUCIsImV4cCI6MTU3NzgzNjgwMH0.hj_GxQcfSgaQ2auPeZdKI5-LWfpb54Z7UFARVr_kUEU(inspect)

Contact us

Our developers are happy to answer any questions you have. We're here to help, whether your question is:

  • Why isn't my token being accepted?
  • Which authentication type would be best for my use case?
  • Can you explain this part of the token reference?
  • Anything else

Use the popup in the bottom right corner of this page to get in touch.