Airtable OAuth: "invalid_grant" error when fetching access token

294 1
Showing results for 
Search instead for 
Did you mean: 
4 - Data Explorer
4 - Data Explorer

I’m attempting to integrate Airtable OAuth into an application of mine but the Airtable endpoints to fetch access tokens keep giving an “invalid_grant” error.


I’ve already looked at this post and haven’t been able to solve my problem with it:


Step one: The user clicks on a link and gets redirected to Airtable OAuth. Here’s how I generate the link:


const state = crypto.randomBytes(100).toString('base64url');


// prevents others from impersonating you

const codeVerifier = crypto.randomBytes(96).toString('base64url'); // 128 characters

const codeChallengeMethod = 'S256';

const codeChallenge = crypto


 .update(codeVerifier) // hash the code verifier with the sha256 algorithm

 .digest('base64') // base64 encode, needs to be transformed to base64url

 .replace(/=/g, '') // remove =

 .replace(/\+/g, '-') // replace + with -

 .replace(/\//g, '_'); // replace / with _ now base64url encoded


await insertStringIntoRedis(state, codeVerifier, { EX: 600 }); // expire after 10 min


// build the authorization URL

const authorizationUrl = new URL('');

authorizationUrl.searchParams.set('code_challenge', codeChallenge);

authorizationUrl.searchParams.set('code_challenge_method', codeChallengeMethod);

authorizationUrl.searchParams.set('state', state);

authorizationUrl.searchParams.set('client_id', process.env.AIRTABLE_CLIENT_ID ?? '');




   process.env.OAUTH_REDIRECT_URI ?? ''



authorizationUrl.searchParams.set('response_type', 'code');



 'data.records:read data.records:write schema.bases:read schema.bases:write',



return { url: authorizationUrl.toString() };


After the user gets redirected to my app, I parse their code and state from the URL, and run this code:


export async function getAirtableTokens(

 authCode: string,

 authState: string,

): Promise<{ accessToken: string; refreshToken: string }> {

const codeVerifier = await getStringFromRedis(authState);

if (codeVerifier === null) throw new Error('Airtable redirect had a malformed state');


 const clientId = process.env.AIRTABLE_CLIENT_ID ?? '';

 const clientSecret = process.env.AIRTABLE_CLIENT_SECRET ?? '';

 const redirectUri = `${

   process.env.OAUTH_REDIRECT_URI ?? ''



 console.log(`authCode: ${authCode}`);

 console.log(`codeVerifier: ${codeVerifier}`);

 console.log(`clientId: ${clientId}`);

 console.log(`clientSecret: ${clientSecret}`);


 const encodedCredentials = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');

 const authorizationHeader = `Basic ${encodedCredentials}`;


 const headers = {

   'Content-Type': 'application/x-www-form-urlencoded',

   Authorization: authorizationHeader,



 const response = await axios({

   method: 'POST',

   url: '',


   // stringify the request body like a URL query string

   data: qs.stringify({

     client_id: clientId,

     code_verifier: codeVerifier,

     redirect_uri: redirectUri,

     code: authCode,

     grant_type: 'authorization_code',



   .then((resp) => {

     return as { access_token: string; refresh_token: string };


   .catch((err) => {

     console.log('Error from Airtable getting access tokens');


     throw new Error(`Error: ${JSON.stringify(err)}`);



 return {

   accessToken: response.access_token,

   refreshToken: response.refresh_token,




However, any time I run the `getAirtableTokens` function, I get a 400 response with an “invalid_grant” error.


I tried to follow the GitHub tutorial ( as closely as possible, yet the error continues to persist. 

1 Reply 1
4 - Data Explorer
4 - Data Explorer

Hi kk1,


Did you manage to fix your issue? I am having similiar problems with error code 'invalid_grant' and cannot seem to figure out the issue.