Setting up Airtable OAuth authentication using Python

I am attempting to set up OAuth authentication for Airtable through Python. I have code that works perfectly for Google APIs using google_auth_oauthlib.flow.InstalledAppFlow.from_client_secrets_file to facilitate the authentication process seamlessly.

from google_auth_oauthlib.flow import InstalledAppFlow

def authenticate_user():
    token_data = None
    # Verify if stored credentials are present
    if os.path.exists(AUTH_TOKEN_FILE):
        token_data = Credentials.from_authorized_user_file(AUTH_TOKEN_FILE, PERMISSION_SCOPES)
    
    # Deal with expired or absent credentials
    if not token_data or not token_data.valid:
        if token_data and token_data.expired and token_data.refresh_token:
            token_data.refresh(Request())
        else:
            auth_flow = InstalledAppFlow.from_client_secrets_file(CLIENT_SECRETS_FILE, PERMISSION_SCOPES)
            token_data = auth_flow.run_local_server(port=0)
        
        # Save credentials for future sessions
        with open(AUTH_TOKEN_FILE, 'w') as file:
            file.write(token_data.to_json())
    
    return token_data

For Airtable, I constructed a similar JSON configuration file containing the necessary OAuth parameters:

{"installed": {
    "client_id": "your_client_id_here",
    "auth_uri": "https://airtable.com/oauth2/v1/authorize",
    "token_uri": "https://oauth2.googleapis.com/token", 
    "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
    "client_secret": "your_secret_here",
    "redirect_uris": ["http://localhost"]
}}

Sadly, this method does not work successfully with Airtable. I also attempted to employ the requests_oauthlib library:

from oauthlib.oauth2 import WebApplicationClient
from requests_oauthlib import OAuth2Session
import secrets
import base64
import hashlib

def create_pkce_codes():
    verifier = base64.urlsafe_b64encode(secrets.token_bytes(32)).rstrip(b"=").decode('utf-8')
    challenge = base64.urlsafe_b64encode(hashlib.sha256(verifier.encode('utf-8')).digest()).rstrip(b"=").decode('utf-8')
    return verifier, challenge

def setup_oauth():
    app_id = 'your_app_id'
    callback_url = 'https://localhost:8080/'
    permissions = ['schema.bases:read']
    
    verifier_code, challenge_code = create_pkce_codes()
    
    oauth_client = WebApplicationClient(app_id)
    session = OAuth2Session(client=oauth_client, redirect_uri=callback_url, scope=permissions)
    
    auth_url, session_state = session.authorization_url(
        url='https://airtable.com/oauth2/v1/authorize',
        code_challenge_method="S256",
        code_challenge=challenge_code
    )
    
    print("Visit this URL:", auth_url)
    callback_response = input('Enter the complete redirect URL: ')
    
    access_token = session.fetch_token(
        token_url="https://api.airtable.com/oauth/token",
        authorization_response=callback_response,
        client_secret=None,
        code_verifier=verifier_code
    )
    
    return access_token

This approach gets me closer to a solution but ends with an INVALID_API_VERSION error. I need guidance on either properly setting the redirect URI for the Google library approach or resolving the API version problem with the OAuth2Session method.

Been wrestling with Airtable OAuth myself recently and hit the same issues. The problem is Airtable’s OAuth works differently than Google’s - especially with PKCE requirements and endpoints. Your requests_oauthlib code looks good, but there’s an issue with client authentication. Airtable wants the client credentials passed differently than what you’re doing. Try adding the client ID explicitly in your fetch_token call: access_token = session.fetch_token(token_url=“https://airtable.com/oauth2/v1/token”, authorization_response=callback_response, client_id=app_id, code_verifier=verifier_code). Also double-check that your app’s OAuth settings in Airtable match your code exactly. The redirect URI needs to be identical - character for character. I noticed you’re using https://localhost:8080/ in code but had http://localhost in your JSON config earlier. Check your scope format too. Some people have better luck with data.records:read instead of schema.bases:read depending on what you actually need.

Hit this same wall a few months back when integrating Airtable into our data pipeline. The INVALID_API_VERSION error happens because you’re mixing up the token endpoints.

Your second approach is almost right, but you’re using the wrong token URL. Change this line:

token_url="https://api.airtable.com/oauth/token"

To:

token_url="https://airtable.com/oauth2/v1/token"

Also caught another issue in your JSON config - you’re using Google’s token URI instead of Airtable’s. Should be https://airtable.com/oauth2/v1/token there too.

The Google auth library won’t work directly because Airtable requires PKCE for security, which that library doesn’t handle for non-Google services. Your requests_oauthlib approach is the right path.

One more thing - make sure your redirect URI in the code exactly matches what you set in your Airtable app settings. Even trailing slashes matter. I spent half a day debugging that once.

After you get the token, you can test it works by hitting https://api.airtable.com/v0/meta/bases with the authorization header.