APIs & Integrations

skimura
Top Contributor | Platinum Partner
Top Contributor | Platinum Partner

The access token expires even though I just got it.

SOLVE

I use hubspot public app.

 

1. Get hubspot `access token` from refresh token.

 (hubspotClient.oauth.tokensApi.createToken)

2. `hubspotClient.crm.tickets.associationsApi.getAll` → OK (300ms after)

3. `hubspotClient.crm.deals.basicApi.getById` → NG (500ms after)

 

error response:

// 2023-08-29T00:52:32.490Z

401:Error:HTTP-Code: 401
Message: An error occurred.
Body: 
{
    "status": "error",
    "message": "The OAuth token used to make this call expired 20 minute(s) ago.",
    "correlationId": "2fa2e50f-0fa8-43a6-b4c7-1bebc50e896c",
    "category": "EXPIRED_AUTHENTICATION",
    "context": {
        "expire time": [
            "2023-08-29T00:31:49.484Z"
        ]
    }
}

 It means `token expired`.

 

But, the time token got is `2023-08-29T00:52:31.912Z`.

It only 500ms after.

// 2023-08-29T00:52:31.912Z
TokenResponseIF {
  accessToken: '...',
  refreshToken: '...',
  expiresIn: 1800,
  tokenType: 'bearer'
}

 

It doesn't occur when run only once.

Occurs when running continuously for testing.

(example: 100 at 1 minute intervals)

 

Please let me know if you have any ideas to solve.

1 Accepted solution
skimura
Solution
Top Contributor | Platinum Partner
Top Contributor | Platinum Partner

The access token expires even though I just got it.

SOLVE

This problem is resolved.

 

I changed code, to without use hubspot API SDK.

I use `axios` for API.

 

The source code looks like this.

*This code is simplified.

 

...
  private static readonly HUBSPOT_API_BASE_URL = 'https://api.hubapi.com';

  #retryCount = 0;

    this.#axios = axios.create({
      baseURL: HUBSPOT_API_BASE_URL,
    });

    this.#axios.interceptors.request.use(async (request) => {
      request.headers.Authorization = `Bearer ${this.#accessToken}`;

      return request;
    });
    // retry
    this.#axios.interceptors.response.use(
      (response: AxiosResponse) => {
        this.#retryCount = 0;

        return response;
      },
      async (error: AxiosError) => {
        switch(error.response?.status) {
          case 401:
            this.debugLogAccessToken();

            if (this.isErrorCausedByAccessTokenExpired(error)) {
              // instance property: this is not best way.
              this.#retryCount++;
              if (this.#retryCount < this.MAX_RETRY_COUNT) {
                await this.refreshAccessToken();

                if (error.config) {
                  return this.#axios.request(error.config);
                }
              } else {
                console.log('retries exceeded');
                this.#retryCount = 0;

                throw error;
              }
            } else {
              throw error;
            }
            break;
          default:
            break;
        }
        return Promise.reject(error);
      }
    );
...
  static async getAccessTokenByRefreshToken(
      config: HubspotConfig,
  ) : Promise<string>
  {
    const formData: { [k in string]: string } = {
        grant_type: 'refresh_token',
        client_id: config.client_id,
        client_secret: config.client_secret,
        refresh_token: config.refresh_token,
    };
    const params = new URLSearchParams();
    Object.keys(formData).forEach(k => {
      params.append(k, formData[k]);
    })

    const response = await axios.post<TokenResponseIfSnake>(
      HUBSPOT_API_BASE_URL + '/oauth/v1/token',
      params,
    )

    console.log('token: ' + JSON.stringify(response.data));

    return response.data.access_token;
  }
...
  private isErrorCausedByAccessTokenExpired(error: any): boolean {
    if (!this.isAxiosError<ApiError>(error)) {
      return false;
    }

    const code = error.response?.status;
    const status = error.response?.data.status;
    const category = error.response?.data.category;

    // token expired
    return code === 401
     && status === 'error'
     && category === 'EXPIRED_AUTHENTICATION';
  }
...
    const url = '/crm/v3/objects/deals/';
    const getParams = `${id}?archived=false&properties=` + properties;

    try {
      const response = await this.#axios.get<SimplePublicObjectWithAssociations>(
        url + getParams,
      )

      return new DealResponse(response.data.properties);
    } catch (error: unknown) {
        ...
    }
...

 

 

I thought, Hubspot API SDK isn't stored refreshed access token.

 

Anyway this problem is resolved.

 

View solution in original post

0 Upvotes
5 Replies 5
skimura
Solution
Top Contributor | Platinum Partner
Top Contributor | Platinum Partner

The access token expires even though I just got it.

SOLVE

This problem is resolved.

 

I changed code, to without use hubspot API SDK.

I use `axios` for API.

 

The source code looks like this.

*This code is simplified.

 

...
  private static readonly HUBSPOT_API_BASE_URL = 'https://api.hubapi.com';

  #retryCount = 0;

    this.#axios = axios.create({
      baseURL: HUBSPOT_API_BASE_URL,
    });

    this.#axios.interceptors.request.use(async (request) => {
      request.headers.Authorization = `Bearer ${this.#accessToken}`;

      return request;
    });
    // retry
    this.#axios.interceptors.response.use(
      (response: AxiosResponse) => {
        this.#retryCount = 0;

        return response;
      },
      async (error: AxiosError) => {
        switch(error.response?.status) {
          case 401:
            this.debugLogAccessToken();

            if (this.isErrorCausedByAccessTokenExpired(error)) {
              // instance property: this is not best way.
              this.#retryCount++;
              if (this.#retryCount < this.MAX_RETRY_COUNT) {
                await this.refreshAccessToken();

                if (error.config) {
                  return this.#axios.request(error.config);
                }
              } else {
                console.log('retries exceeded');
                this.#retryCount = 0;

                throw error;
              }
            } else {
              throw error;
            }
            break;
          default:
            break;
        }
        return Promise.reject(error);
      }
    );
...
  static async getAccessTokenByRefreshToken(
      config: HubspotConfig,
  ) : Promise<string>
  {
    const formData: { [k in string]: string } = {
        grant_type: 'refresh_token',
        client_id: config.client_id,
        client_secret: config.client_secret,
        refresh_token: config.refresh_token,
    };
    const params = new URLSearchParams();
    Object.keys(formData).forEach(k => {
      params.append(k, formData[k]);
    })

    const response = await axios.post<TokenResponseIfSnake>(
      HUBSPOT_API_BASE_URL + '/oauth/v1/token',
      params,
    )

    console.log('token: ' + JSON.stringify(response.data));

    return response.data.access_token;
  }
...
  private isErrorCausedByAccessTokenExpired(error: any): boolean {
    if (!this.isAxiosError<ApiError>(error)) {
      return false;
    }

    const code = error.response?.status;
    const status = error.response?.data.status;
    const category = error.response?.data.category;

    // token expired
    return code === 401
     && status === 'error'
     && category === 'EXPIRED_AUTHENTICATION';
  }
...
    const url = '/crm/v3/objects/deals/';
    const getParams = `${id}?archived=false&properties=` + properties;

    try {
      const response = await this.#axios.get<SimplePublicObjectWithAssociations>(
        url + getParams,
      )

      return new DealResponse(response.data.properties);
    } catch (error: unknown) {
        ...
    }
...

 

 

I thought, Hubspot API SDK isn't stored refreshed access token.

 

Anyway this problem is resolved.

 

0 Upvotes
himanshurauthan
Thought Leader | Elite Partner
Thought Leader | Elite Partner

The access token expires even though I just got it.

SOLVE

Hello @skimura 

HubSpot Oauth APIs are robust and well-tested since all public apps are using them.

 

According to the Information you have shared, I believe that after using the refresh token to get the new access token, you might still be using the old access token for authentication and probably there would be a logical error in your code.

Thanks

Digital Marketing & Inbound Expert In Growth Hacking Technology
0 Upvotes
skimura
Top Contributor | Platinum Partner
Top Contributor | Platinum Partner

The access token expires even though I just got it.

SOLVE

@himanshurauthan 

 

Thank you for reply.

 

>you might still be using the old access token for authentication and probably there would be a logical error in your code.

 

The "old access token" does not exist

because the access token is acquired at the beginning of the process.

 

I have checked the access token at the time of the error.

It was the same access token as the first access token.

 

const tokenResponse = await this.#hubspotClient.oauth.tokensApi.createToken(
  'refresh_token',
  undefined,
  undefined,
  this.#hubspotConfig.client_id,
  this.#hubspotConfig.client_secret,
  this.#hubspotConfig.refresh_token,
);
// log output: access token
console.log(tokenResponse);
...

try {
  const response = await this.#hubspotClient.crm.deals.basicApi.getById(
    id, 
    properties
  );
  ...
  
} catch (error: unknown) {
  // log output: access token
  console.error(this.#hubspotClient.config.accessToken);

  throw error;
}

 

 Thanks.

0 Upvotes
himanshurauthan
Thought Leader | Elite Partner
Thought Leader | Elite Partner

The access token expires even though I just got it.

SOLVE
hubspotClient.oauth.tokensApi
"receiving the response"
.create('refresh_token', undefined, undefined, YOUR_CLIENT_ID, YOUR_CLIENT_SECRET, YOUR_REFRESH_TOKEN)
.then((results) => {
console.log(results)

// this assigns the accessToken to the client, so your client is ready
// to use
hubspotClient.setAccessToken(results.accessToken)

return hubspotClient.crm.companies.basicApi.getPage()
})

 

The HubSpot Node Lib shares this example, in this case we are setting the accessToken on the API after receiving, Are you also updating the token received in the api after your first await in the snippet you have shared ?

Thanks

Digital Marketing & Inbound Expert In Growth Hacking Technology
0 Upvotes
skimura
Top Contributor | Platinum Partner
Top Contributor | Platinum Partner

The access token expires even though I just got it.

SOLVE

@himanshurauthan 

 

>Are you also updating the token received in the api after your first await in the snippet you have shared ?

Yes.

 

The source code looks like this.

*This code is simplified.

import { Client } from '@hubspot/api-client';

const hubspotClient = new Client();
const tokenResponse = await hubspotClient.oauth.tokensApi.createToken(
  'refresh_token',
  undefined,
  undefined,
  this.#hubspotConfig.client_id,
  this.#hubspotConfig.client_secret,
  this.#hubspotConfig.refresh_token,
);

console.log(tokenResponse);

this.#hubspotClient = new Client({ accessToken: accessToken, numberOfApiCallRetries: this.MAX_RETRY_COUNT });

...

try {
  const response = await this.#hubspotClient.crm.deals.basicApi.getById(
    id, 
    properties
  );
  ...

} catch (error: unknown) {
  if (this.isErrorCausedByAccessTokenExpired(error)) {
    await this.refreshAccessToken();

    // DO retry
    ...
  } else {
    throw error;
  }
}
...

private async refreshAccessToken() {
  const tokenResponse = await this.#hubspotClient.oauth.tokensApi.createToken(
    'refresh_token',
    undefined,
    undefined,
    this.#hubspotConfig.client_id,
    this.#hubspotConfig.client_secret,
    this.#hubspotConfig.refresh_token,
  );

  this.#hubspotClient.setAccessToken(tokenResponse.accessToken);
  // I tried the following but the result was the same.

  // this.#hubspotClient = new Client({
  //   accessToken: tokenResponse.accessToken,
  //   numberOfApiCallRetries: this.MAX_RETRY_COUNT
  // });
}

 

 

Thanks.

0 Upvotes