Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error: tokens can only be created server-side using the API Secret #665

Closed
emeka100 opened this issue Apr 19, 2021 · 23 comments
Closed

Error: tokens can only be created server-side using the API Secret #665

emeka100 opened this issue Apr 19, 2021 · 23 comments

Comments

@emeka100
Copy link

emeka100 commented Apr 19, 2021

I'm running an api server using nextjs api routes, which is basically a backend server: https://nextjs.org/docs/api-routes/introduction

However, I'm getting this error when initiating client.createToken()
Error: tokens can only be created server-side using the API Secret

Here's my code below :

`
import { StreamChat } from 'stream-chat'

const client = StreamChat.getInstance(process.env.STREAM_KEY, process.env.STREAM_SECRET)

export default function handler(req, res) {

try {
    const userId = req.query.userId

    console.log(userId)

    if (userId){

        const chatToken = client.createToken(userId)

        console.log(chatToken)

        res.status(200).json({ chatToken: chatToken })
    }

} catch(err){
    console.log(err)
}

}
`

@shodgetts
Copy link

This should be working fine. Are you sure that your .env file is being accessed properly? Can you try to console.log your app key and secret and make sure they're being found. If so, let me know, and we'll dig further.

@emeka100
Copy link
Author

Yes, the key and secret are called correctly. I found the issue and a temporary workaround.

The error is being thrown because the client.secret is undefined in the instance, even though it's passed with the getInstance() function.

Here's how I'm handling it:

let client = StreamChat.getInstance(process.env.STREAM_KEY, process.env.STREAM_SECRET)
if (!client.secret){
    client.secret=process.env.STREAM_SECRET
}

@ferhatelmas
Copy link
Contributor

ferhatelmas commented Apr 19, 2021

Next.js doesn't have process.env as standard, that might be the issue. We should handle it better /cc @mahboubii @vishalnarkhede

Docs:

Note: In order to keep server-only secrets safe, Next.js replaces process.env.* with the correct values at build time. This means that process.env is not a standard JavaScript object, so you’re not able to use object destructuring. Environment variables must be referenced as e.g. process.env.NEXT_PUBLIC_PUBLISHABLE_KEY, not const { NEXT_PUBLIC_PUBLISHABLE_KEY } = process.env.

src: https://nextjs.org/docs/basic-features/environment-variables

@mahboubii
Copy link
Contributor

@emeka100 Do you happen to call StreamChat.getInstance in any other part of your application without the secret? This is a common mistake since the getInstance function creates a new client only the first time it's called in your runtime, and subsequent calls with different parameters do not change the client options.

@mahboubii
Copy link
Contributor

Next.js doesn't have process.env as standard, that might be the issue. We should handle it better /cc @mahboubii @vishalnarkhede

Docs:

Note: In order to keep server-only secrets safe, Next.js replaces process.env.* with the correct values at build time. This means that process.env is not a standard JavaScript object, so you’re not able to use object destructuring. Environment variables must be referenced as e.g. process.env.NEXT_PUBLIC_PUBLISHABLE_KEY, not const { NEXT_PUBLIC_PUBLISHABLE_KEY } = process.env.

src: https://nextjs.org/docs/basic-features/environment-variables

In this SDK the process.env is only used for testing env, we shouldn't have any issue supporting Next.js

@kyriacos
Copy link

i am getting exactly the same issue when used with the api routes. Tried commenting out all the client-side code just in case something affects it but it seems that the instance still has an undefined secret. Even if i hard code the value. Using the next-based example.

Also if getInstance only creates 1 instance for all the runtime, then if we have an instance initialized inside an api/route would the same instance then becomes available client-side? Which would then have the key?

Thanks!

@kyriacos
Copy link

Replying back with an answer to the problem. Just use new StreamChat() to avoid the issue. Worked fine for me. If running in a serverless environment it wouldn't be a problem since each function would load up an instance as far as i am aware.

Hope this helps @emeka100

@mahboubii
Copy link
Contributor

@kyriacos You are right. Alternatively, you can simply use new StreamChat if you want to have different instances with different key/secrets

@nhannah nhannah closed this as completed Jul 14, 2021
@rohankushwah123
Copy link

I am still getting error on this

@shodgetts
Copy link

Hi Rohan, your issue is not related to this. Can you please follow up on the tickets from support@getstream.io. GitHub issues are reserved for bug reports and feature requests/improvements. Your issue is not a bug, just implementation.

@pascuflow
Copy link

I am having the same error event after using new StreamChat.

    const logger = await datadogLogger(req.url, 'nextjs')

    try {
        const userId = cookieParser(res.getHeaders())['userId'].value
        console.log(process.env.STREAM_API_KEY)
        console.log(process.env.STREAM_SECRET)
        const key = process.env.STREAM_API_KEY
        const secret = process.env.STREAM_SECRET
        const serverClient = new StreamChat.getInstance(key, secret);
        console.log(serverClient)
        const token = serverClient.createToken(userId);
        res.send({user_token: token, user_id: userId})

    } catch (error) {
        logger.error(error, 'Stream token provider error')
        return res.status(500).send("Server error, 'Stream token provider error'");
    }
}
u8hbw7rrskqq ----> KEY
23abyqgqvh78v4zcv73qeganvpumvheujw4fx5gr44jxg6xwv4r99fmhjnmf7npw ---> Secret
StreamChat {
  _user: undefined,
  activeChannels: {},
  anonymous: false,
  axiosInstance: [Function: wrap] {
    request: [Function: wrap],
    getUri: [Function: wrap],
    delete: [Function: wrap],
    get: [Function: wrap],
    head: [Function: wrap],
    options: [Function: wrap],
    post: [Function: wrap],
    put: [Function: wrap],
    patch: [Function: wrap],
    defaults: {
      transitional: [Object],
      adapter: [Function: httpAdapter],
      transformRequest: [Array],
      transformResponse: [Array],
      timeout: 3000,
      xsrfCookieName: 'XSRF-TOKEN',
      xsrfHeaderName: 'X-XSRF-TOKEN',
      maxContentLength: -1,
      maxBodyLength: -1,
      validateStatus: [Function: validateStatus],
      headers: [Object],
      withCredentials: false,
      warmUp: false,
      recoverStateOnReconnect: true,
      httpsAgent: [Agent]
    },
    interceptors: { request: [InterceptorManager], response: [InterceptorManager] },
    create: [Function: create]
  },
  baseURL: 'https://chat.stream-io-api.com',
  browser: false,
  cleaningIntervalRef: undefined,
  clientID: undefined,
  configs: {},
  key: 'u8hbw7sfskqq',
  listeners: {},
  logger: [Function],
  recoverStateOnReconnect: true,
  mutedChannels: [],
  mutedUsers: [],
  node: true,
  options: {
    timeout: 3000,
    withCredentials: false,
    warmUp: false,
    recoverStateOnReconnect: true,
    httpsAgent: Agent {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      defaultPort: 443,
      protocol: 'https:',
      options: [Object],
      requests: {},
      sockets: {},
      freeSockets: {},
      keepAliveMsecs: 3000,
      keepAlive: true,
      maxSockets: Infinity,
      maxFreeSockets: 256,
      maxTotalSockets: Infinity,
      totalSocketCount: 0,
      scheduling: 'fifo',
      maxCachedSessions: 100,
      _sessionCache: [Object],
      [Symbol(kCapture)]: false
    }
  },
  secret: undefined,
  setUserPromise: null,
  state: ClientState { users: {}, userChannelReferences: {} },
  tokenManager: TokenManager {
    loadTokenPromise: null,
    type: 'static',
    secret: undefined,
    token: undefined,
    tokenProvider: undefined,
    user: undefined,
    setTokenOrProvider: [Function],
    reset: [Function],
    validateToken: [Function],
    tokenReady: [Function],
    loadToken: [Function],
    getToken: [Function],
    isStatic: [Function]
  },
  user: undefined,
  userAgent: undefined,
  userID: undefined,
  wsBaseURL: 'wss://chat.stream-io-api.com',
  wsConnection: null,
  wsFallback: undefined,
  wsPromise: null,
  consecutiveFailures: 0,
  insightMetrics: InsightMetrics {
    connectionStartTimestamp: null,
    wsConsecutiveFailures: 0,
    wsTotalFailures: 0,
    instanceClientId: '59adf822-bdeb-4262-8204-036c16afbbfb'
  },
  defaultWSTimeoutWithFallback: 6000,
  defaultWSTimeout: 15000,
  _getConnectionID: [Function],
  _hasConnectionID: [Function],
  connectUser: [Function],
  setUser: [Function],
  _setToken: [Function],
  closeConnection: [Function],
  openConnection: [Function],
  _setupConnection: [Function],
  _normalizeDate: [Function],
  disconnectUser: [Function],
  disconnect: [Function],
  connectAnonymousUser: [Function],
  setAnonymousUser: [Function],
  doAxiosRequest: [Function],
  dispatchEvent: [Function],
  handleEvent: [Function],
  _updateMemberWatcherReferences: [Function],
  _updateUserReferences: [Function],
  _updateUserMessageReferences: [Function],
  _deleteUserMessageReference: [Function],
  _handleUserEvent: [Function],
  _callClientListeners: [Function],
  recoverState: [Function],
  getChannelByMembers: [Function],
  getChannelById: [Function],
  updateUsers: [Function: upsertUsers],
  updateUser: [Function: upsertUser],
  markAllRead: [Function: markChannelsRead],
  _isUsingServerAuth: [Function],
  _buildWSPayload: [Function]
}

@rumfiske
Copy link

Anyone got a fix for this? I have the same problem..

@ferhatelmas
Copy link
Contributor

pascuflow

- const serverClient = new StreamChat.getInstance(key, secret);
+ const serverClient = StreamChat.getInstance(key, secret);

@pascuflow
Copy link

@ferhatelmas That did not fix the issue for me, its a deeper problem with how the stream-chat module works, if you look at the node-modules you see:

  • Get a client instance
  • This function always returns the same Client instance to avoid issues raised by multiple Client and WS connections
  • After the first call, the client configuration will not change if the key or options parameters change

You have to make sure that the first call to StreamChat.getInstance happens on the server, in my case I had to make sure all frontend component references to StreamChat.getInstance(key) do a pre render call to the server first to create the initial instance.

@ferhatelmas
Copy link
Contributor

Good to hear you were able to solve the issue.

Your explanation sounds correct to me. It's just a convenience to prevent multiple connection from the same client (on sloppy implementations such as creating an instance in each render). In advanced usage, instance could be created by yourself (new StreamChat()).

@abins6342
Copy link

const stream = require('getstream');
const bcrypt = require('bcrypt');
const StreamChat = require('stream-chat').StreamChat;
const crypto = require('crypto');

require('dotenv').config();

const api_key = process.env.STREAM_API_KEY;
const api_secret = process.env.STREAM_API_SECRET;
const app_id = process.env.STREAM_APP_ID;

const signup = async (req, res) => {
try {

    const { fullName, username, password, phoneNumber } = req.body;

    const userId = crypto.randomBytes(16).toString('hex');

    const serverClient = stream.connect(api_key, api_secret, app_id);

    const hashedPassword = await bcrypt.hash(password, 10);

    const token = serverClient.createUserToken(userId);

    res.status(200).json({ token, fullName, username, userId, hashedPassword, phoneNumber });
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

const login = async (req, res) => {
try {

    const { username, password } = req.body;
    
    const serverClient = stream.connect(api_key, api_secret, app_id);
    const client = StreamChat.getInstance(api_key, api_secret);
    if (!client.api_secret){
        client.api_secret=process.env.STREAM_API_SECRET
    }

    const { users } = await client.queryUsers({ name: username });

    if(!users.length) return res.status(400).json({ message: 'User not found' });

    const success = await bcrypt.compare(password, users[0].hashedPassword);

    const token = serverClient.createUserToken(users[0].id);

    if(success) {
        res.status(200).json({ token, fullName: users[0].fullName, username, userId: users[0].id});
    } else {
        res.status(500).json({ message: 'Incorrect password' });
    }
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

module.exports = { signup, login }

Can anyone solve the error

@zamorai
Copy link

zamorai commented Dec 6, 2022

Also have the issue, any solution yet?

@izakfilmalter
Copy link

Good to hear you were able to solve the issue.

Your explanation sounds correct to me. It's just a convenience to prevent multiple connection from the same client (on sloppy implementations such as creating an instance in each render). In advanced usage, instance could be created by yourself (new StreamChat()).

This worked for me.

@FreddieSethi
Copy link

ERROR Error: tokens can only be created server-side using the API Secret
why does this come up when I try to create a token

@Jatinvaish
Copy link

const stream = require('getstream'); const bcrypt = require('bcrypt'); const StreamChat = require('stream-chat').StreamChat; const crypto = require('crypto');

require('dotenv').config();

const api_key = process.env.STREAM_API_KEY; const api_secret = process.env.STREAM_API_SECRET; const app_id = process.env.STREAM_APP_ID;

const signup = async (req, res) => { try {

    const { fullName, username, password, phoneNumber } = req.body;

    const userId = crypto.randomBytes(16).toString('hex');

    const serverClient = stream.connect(api_key, api_secret, app_id);

    const hashedPassword = await bcrypt.hash(password, 10);

    const token = serverClient.createUserToken(userId);

    res.status(200).json({ token, fullName, username, userId, hashedPassword, phoneNumber });
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

const login = async (req, res) => { try {

    const { username, password } = req.body;
    
    const serverClient = stream.connect(api_key, api_secret, app_id);
    const client = StreamChat.getInstance(api_key, api_secret);
    if (!client.api_secret){
        client.api_secret=process.env.STREAM_API_SECRET
    }

    const { users } = await client.queryUsers({ name: username });

    if(!users.length) return res.status(400).json({ message: 'User not found' });

    const success = await bcrypt.compare(password, users[0].hashedPassword);

    const token = serverClient.createUserToken(users[0].id);

    if(success) {
        res.status(200).json({ token, fullName: users[0].fullName, username, userId: users[0].id});
    } else {
        res.status(500).json({ message: 'Incorrect password' });
    }
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

module.exports = { signup, login }

Can anyone solve the error

is you solved this error?

@Jatinvaish
Copy link

const stream = require('getstream'); const bcrypt = require('bcrypt'); const StreamChat = require('stream-chat').StreamChat; const crypto = require('crypto');

require('dotenv').config();

const api_key = process.env.STREAM_API_KEY; const api_secret = process.env.STREAM_API_SECRET; const app_id = process.env.STREAM_APP_ID;

const signup = async (req, res) => { try {

    const { fullName, username, password, phoneNumber } = req.body;

    const userId = crypto.randomBytes(16).toString('hex');

    const serverClient = stream.connect(api_key, api_secret, app_id);

    const hashedPassword = await bcrypt.hash(password, 10);

    const token = serverClient.createUserToken(userId);

    res.status(200).json({ token, fullName, username, userId, hashedPassword, phoneNumber });
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

const login = async (req, res) => { try {

    const { username, password } = req.body;
    
    const serverClient = stream.connect(api_key, api_secret, app_id);
    const client = StreamChat.getInstance(api_key, api_secret);
    if (!client.api_secret){
        client.api_secret=process.env.STREAM_API_SECRET
    }

    const { users } = await client.queryUsers({ name: username });

    if(!users.length) return res.status(400).json({ message: 'User not found' });

    const success = await bcrypt.compare(password, users[0].hashedPassword);

    const token = serverClient.createUserToken(users[0].id);

    if(success) {
        res.status(200).json({ token, fullName: users[0].fullName, username, userId: users[0].id});
    } else {
        res.status(500).json({ message: 'Incorrect password' });
    }
} catch (error) {
    console.log(error);

    res.status(500).json({ message: error });
}

};

module.exports = { signup, login }

Can anyone solve the error

i have this error when i try to create a user :
connection.ts:157 Uncaught (in promise) Error: {"code":5,"StatusCode":401,"message":"WS failed with code 5 and reason - Connect failed with error: "Token signature is invalid"","isWSFailure":false}

@BoldPlut0
Copy link

Hi , Where you able to solve this error:

i have the same error:

--------ERROR-----------
ERROR [Error: {"code":5,"StatusCode":401,"message":"WS failed with code 5 and reason - Connect failed with error: "Token signature is invalid"","isWSFailure":false}]

@shadowdom24
Copy link

@BoldPlut0 @Jatinvaish

This is working fine for me now.

// package.json
...
"dependencies": {
    "getstream": "^8.1.5",
    "next": "13.4.9",
    "react": "18.2.0",
    "react-dom": "18.2.0"
  }
...
const streamClient = connect(
 process.env.STREAM_API_KEY as string,
 process.env.STREAM_SECRET_KEY as string,
 undefined,
 {  browser: false,  location: "us-east"} // <-- added option for browser mode false
 )

const userToken = streamClient.createUserToken(userId)

res.status(200).json({ data: userToken })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests