[#1183] RestApi authentication in a stateless environment

Migrated from Redmine #1183 | Author: Olaf Siebert
Status: Feedback | Priority: High, I’m very impatient | Created: 2024-02-25


We currently developing a public facing website that requires access to restApi using serverless functions. Requesting a token is simple enough but storing the token and refreshing it when the token has expired is a different matter. Any suggestion how to manage authentication in a stateless environment.

Dmytro Bondarev wrote:

Hi,
Even if you use serverless functions you should have any kind of key-value storage or cache or any DB to store access/refresh token. This is how most APIs works.

Olaf Siebert wrote:

This project, is hosted on Netlify, which offers the ability to set environment variables. However, it’s important to note that these variables are static and cannot be dynamically updated. This poses a challenge when dealing with API tokens, which we typically obtain from an admin dashboard for the APIs we integrate with. Ideally, for user-specific interactions, storing tokens in the browser would be a practical approach, leveraging mechanisms like cookies or localStorage, especially in scenarios involving individual user accounts and logins. However, this strategy doesn’t align well with our current needs, where a single API token is used application-wide rather than being user-specific.

Upon further consideration, using a database solely for managing this API token feels like an overextension of resources. Moreover, relying on admin credentials within serverless functions, despite their restricted access, introduces potential security concerns. It’s paramount to maintain strict control over sensitive information, adhering to the principle of least privilege and exploring more secure methods of token management.

Additionally, while the available public API offers a solution, it lacks certain functionalities present in the restApi, such as the ability to request payment links. This limitation necessitates a more nuanced approach to ensure both secure and functional integration with the necessary APIs.

Olaf Siebert wrote:

I added a database to store the token but on refresh I am getting 401 error (Unauthorized).

const data = {
company: process.env.SIMPLEBOOKME_COMPANY_LOGIN,
token: token.token,
refresh_token: token.refreshToken
}

This is the data data send to the endpoint. Quite in convenient the refresh token. Any ideas why this is not working???

Olaf Siebert wrote:

Ok, seems there is bug with your refresh token. I can refresh the token before 30 min expiration but anytime past, it will not refresh the token. I thought the refresh token aren’t expiring?

Dmytro Bondarev wrote:

Hi, please provide http request you are making to refresh token.

Olaf Siebert wrote:

export async function getRefreshToken(token: Token) {

  try {

    if(!token.refresh_token && !token.token) {
      throw new Error('Token and refresh token are required');
    }

    const data = {
      company: process.env.SIMPLEBOOKME_COMPANY_LOGIN,
      refresh_token: token.refresh_token
    }

    const response = await fetch(`https://user-api-v2.simplybook.me/admin/auth/refresh-token`, {
      method: 'POST',
      body: JSON.stringify(data),
      headers: {
        'Content-Type': 'application/json',
      }
    });
    const refresh = await response.json();
    
    return refresh

  } catch (error: any) {
    console.error('Error refreshing token', error.response.data);
    return null;
  }
}

Right now, if I get an undefined, I login again but I worried this will cause a too many logins error.

Dmytro Bondarev wrote:

Please provide response from API and your company login.

Olaf Siebert wrote:

If try to refresh the token after 30min, I get unauthorised.

Olaf Siebert wrote:

Company login: myelementalbeing

Dmytro Bondarev wrote:

Per our logs you are trying to use refresh token multiple times.
You should use refresh token only once. Second time you will get error.

Olaf Siebert wrote:

Ok, thanks, seems I am doing something wrong when I refresh. I have a look.