This tutorial guides you through setting up an OAuth 2.0 authentication flow. This flow allows users to grant limited permissions to your App and enables your App to request an access token to perform actions on behalf of the user.
By the end of this tutorial, your Webflow App will be able to obtain an access token on behalf of a user using the Authorization Code Grant flow.
Authorization Code Grant Flow
Webflow uses the Authorization Code Grant flow to provide access tokens to Apps. This flow involves a series of interactions between Webflow's authorization server and your web app. Here’s how the process works when a user visits your site for the first time:
- User sign-up/Login: A user signs up or logs in to your App. At some point, they may need to perform an action that requires using the Webflow API.
- Authorization request: To make requests to the Webflow API on the user’s behalf, your App redirects the user to an authorization screen. Here, they can review the permissions your App is requesting and authorize access to specific Webflow Sites or a Workspace.
- User authorization: Once the user grants authorization, Webflow redirects them back to your App via a redirect URI specified during the app setup, adding a
code
parameter to the query string. - Token Request: Your app uses the
code
to make a secure request to Webflow's authorization server to obtain an access token. If the request is valid, Webflow responds with an access token. - API Requests: Your app can now use this access token to make requests to the Webflow API on behalf of the user.
Get an access token
Requirements
Before you begin, ensure you have the following:
- A Webflow App created with the "Data Client" building block. Learn more here
- Your app's client credentials:
client_id
andclient_secret.
Note: Only workspace administrators are authorized to view a client secret. If you're not a site administrator, please contact one to get the secret for you.
1. Set up your server
Before you can request an access token, you'll need to set up your server to handle the OAuth 2.0 flow. We recommend using JavaScript or Python, as we provide SDKs for these languages that can help simplify the authentication process.
Follow the below examples in Node.js or Python to help you create a server that can accept requests and communicate with the Webflow authorization server.
- Install the necessary packages
Ensure all required libraries and dependencies are installed.
npm install express dotenv webflow-api
-
Store environment variables
Create a
.env
file to store your sensitive information like theCLIENT_ID
andCLIENT_SECRET
.CLIENT_ID=your_client_id CLIENT_SECRET=your_client_secret REDIRECT_URI=your_redirect_uri #optional STATE=your_unique_state #optional -
Initialize server framework
Set up the server to listen on a specific port.
require('dotenv').config(); const express = require('express'); const { WebflowClient } = require('webflow-api'); const app = express(); const port = 3000; /* We'll add the necessary endpoints here in the steps below. */ app.listen(port, () => { console.log(`Server is running at http://localhost:${port}`); });
-
Install the necessary packages
Ensure all required libraries and dependencies are installed.
pip install flask requests python-dotenv >webflow
-
Store environment variables
Create a
.env
file to store your sensitive information like theCLIENT_ID
andCLIENT_SECRET
.CLIENT_ID=your_client_id CLIENT_SECRET=your_client_secret REDIRECT_URI=your_redirect_uri #optional STATE=your_unique_state #optional -
Initialize your server framework
Set up the server to listen on a specific port.
from flask import Flask, request, redirect import requests from webflow import WebflowClient from dotenv import load_dotenv import os load_dotenv() app = Flask(__name__) client_id = os.getenv('CLIENT_ID') client_secret = os.getenv('CLIENT_SECRET') redirect_uri = os.getenv('REDIRECT_URI') state = os.getenv('STATE') # We'll add the necessary endpoints here in the steps below if __name__ == '__main__': app.run(port=3000)
2. Create an authorization link
To enable users to install your App, you need to create an authorization link. This link directs users to a Webflow authorization screen where they can grant your App permission to access their Webflow data.
Where do I put this link?
App Marketplace Submission: Supply this link in your application to the App marketplace. Users will use it to install your App from the marketplace.
Your Site: Place this link on your site to direct users to try out or install your App.
You can create the authorization link using various methods, with the recommended approach being through our JavaScript and Python SDKs.
Parameters and information needed
To create the authorization link, you will need the following information:
Unique ID for your application. Can be found in the dashboard.
This value should always be "code".
Include the scopes your app needs. Use the format - resource:access_level | e.g. assets:read.
The URI to redirect the user once they've granted authorization. This must match what's used in your App settings.
A token value provided by your application to prevent CSRF attacks. If passed, the authorization server should respond with this parameter.
Constructing the authorization link
Using the SDK
To simplify the process of creating authorization links and handling OAuth flows, you can use the provided JavaScript and Python SDKs. These SDKs offer convenient methods to generate the authorization URL with the required parameters.
require('dotenv').config();
const express = require('express');
const { WebflowClient } = require('webflow-api');
const app = express();
const port = 3000;
// Endpoint to redirect to the authorization link
app.get('/auth', (req, res) => {
const authorizeUrl = WebflowClient.authorizeURL({
state: process.env.STATE,
scope: 'sites:read',
clientId: process.env.CLIENT_ID,
redirectUri: process.env.REDIRECT_URI,
});
res.redirect(authorizeUrl);
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
from flask import Flask, request, redirect
import requests
from webflow import WebflowClient
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
client_id = os.getenv('CLIENT_ID')
client_secret = os.getenv('CLIENT_SECRET')
redirect_uri = os.getenv('REDIRECT_URI')
state = os.getenv('STATE')
# Endpoint to redirect to the authorization link
def auth():
authorize_url = WebflowClient.authorize_url(
client_id=client_id,
redirect_uri=redirect_uri,
scope='sites:read',
state=state
)
return redirect(authorize_url)
if __name__ == '__main__':
app.run(port=3000)
Manually create the authorization link
Authorization URL
https://webflow.com/oauth/authorize
Construct the authorization URL using the gathered parameters:
- Note: When URL encoding multiple scopes, they should be connected by a space (
%20
). Additionally, scopes are written in the formatscope:action
, so the colon (:
) should be encoded as%3A
.
For example passing the following scopes:sites:read
,sites:write
, andpages:read
should look likesites%3Aread%20sites%3Awrite%20pages%3Aread
https://webflow.com/oauth/authorize?response_type=code&client_id=YOUR_CLIENT_ID&scope=assets%3Aread%20assets%3Awrite%20authorized_user%3Aread%20cms%3Aread%20cms%3Awrite%20custom_code%3Aread%20custom_code%3Awrite%20forms%3Aread%20forms%3Awrite%20pages%3Aread%20pages%3Awrite%20sites%3Aread%20sites%3Awrite
Copying the Link from the Dashboard
In your Workspace Dashboard:
- Select to "Apps and Integrations" in the left-hand menu.
- Scroll to the "App Development" section and find your App.
- Copy the link from the "Install" button.
3. Handle redirect to the callback URI
When users click on the authorization link, they will be taken to a screen where they can review and grant the necessary permissions for your App.
After the user authorizes the App, Webflow redirects them to your server using the redirect URI specified in your app settings. This GET
request to your server includes the following query parameters:
code
- A single-use authorization code that you'll exchange for an access token. This code is only valid for 15 minutes.state
: optional - The unique state value you provided in the initial request. Ensure this value matches the original to protect against CSRF attacks.
Let's set up an endpoint to handle the callback request and store these parameters, as you'll need them in the next step to request an access token from Webflow.
See the example below for details on completing the following steps:
- Setup the callback endpoint
Create a route to handle the redirect from Webflow - Verify
state
parameter
Optionally, check that the state parameter matches the one sent in the authorization request. - Extract authorization code
Retrieve the code from the query parameters.
Example
require('dotenv').config();
const express = require('express');
const { WebflowClient } = require('webflow-api');
const app = express();
const port = 3000;
// Endpoint to redirect to the authorization link
app.get('/auth', (req, res) => {
const authorizeUrl = WebflowClient.authorizeURL({
state: process.env.STATE,
scope: 'sites:read',
clientId: process.env.CLIENT_ID,
redirectUri: process.env.REDIRECT_URI,
});
res.redirect(authorizeUrl);
});
// Endpoint to receive info from the callback URL
app.get('/callback', async (req, res) => {
const { code, state } = req.query; // Store code and state parameters as variables
if (state !== process.env.STATE) {
return res.status(400).send('State does not match');
}
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
from flask import Flask, request, redirect
import requests
from webflow import WebflowClient
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
client_id = os.getenv('CLIENT_ID')
client_secret = os.getenv('CLIENT_SECRET')
redirect_uri = os.getenv('REDIRECT_URI')
state = os.getenv('STATE')
# Endpoint to redirect to the authorization link
@app.route('/auth')
def auth():
authorize_url = WebflowClient.authorize_url(
client_id=client_id,
redirect_uri=redirect_uri,
scope='sites:read',
state=state
)
return redirect(authorize_url)
# Endpoint to receive info from the callback URL
@app.route('/callback')
def callback():
authorization_code = request.args.get('code') // Store parameters
state_received = request.args.get('state') // Store parameters
if state_received != state:
return 'State does not match', 400
if __name__ == '__main__':
app.run(port=3000)
4. Request an access token
Now that you have the authorization_code
you can exchange it for an access_token
. The access token request should be made as soon as possible after authorization as an unconfirmed authorization_code
is only valid for 15 minutes.
Let's walk through the steps to create a smooth flow for a user:
-
Make a request for an access token to Webflow's authorization server
In the same endpoint we just set up, create a request to Webflow with the following parameters. Webflow requires these parameters to ensure that the entity requesting the access token is the same entity that received the authorization code.Authorization server endpoint
POST https://api.webflow.com/oauth/access_token
Request Parameters
stringrequiredUnique ID for your application. Can be found in the dashboard.
stringrequiredPrivate value unique to your application. Can be found in the dashboard.
stringrequiredAuthorization code used to retrieve an
access_token
for the user. Can only be used once. -
Store the access token securely
For demonstration purposes, we're storing the access token in a variable and printing it to the terminal. However, this approach is not secure for production. You should store the access token securely in a database or environment variables. For comprehensive guidance on securely storing tokens, please refer to our example apps on GitHub -
Redirect the user within your App
After successfully obtaining the access token, redirect the user to an appropriate location within your app. This could be a dashboard, a welcome page, or any other relevant section. Ensure that the user experience is smooth and they are informed about the successful authentication.
Example
require('dotenv').config();
const express = require('express');
const { WebflowClient } = require('webflow-api');
const app = express();
const port = 3000;
// Endpoint to redirect to the authorization link
app.get( '/auth', (req, res) => {
const authorizeUrl = WebflowClient.authorizeURL({
state: process.env.STATE,
scope: 'sites:read',
clientId: process.env.CLIENT_ID,
redirectUri: process.env.REDIRECT_URI, // If the redirect URI is included in the auth link, it must also be included in the request for an access token
});
res.redirect(authorizeUrl);
});
// Endpoint to receive info from the callback URL
app.get('/callback', async (req, res) => {
const { code, state } = req.query;
if (state !== process.env.STATE) {
return res.status(400).send('State does not match');
}
try {
// Request an access token
const accessToken = await WebflowClient.getAccessToken({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
code: code,
redirect_uri: process.env.REDIRECT_URI,
});
// Store the access token securely, e.g., in a database or session (not shown here for brevity)
console.log(accessToken)
// Redirect the user to your App
res.redirect('/dashboard')
} catch (error) {
console.error('Error during OAuth process:', error);
res.status(500).send('Internal Server Error');
}
});
// Dashboard route for demonstraton purposes
app.get('/dashboard', async (req, res) => {
res.send('Welcome to your dashboard!')
});
app.listen(port, () => {
console.log(`Server is running at http://localhost:${port}`);
});
from flask import Flask, request, redirect
import requests
from webflow import WebflowClient
from dotenv import load_dotenv
import os
load_dotenv()
app = Flask(__name__)
client_id = os.getenv('CLIENT_ID')
client_secret = os.getenv('CLIENT_SECRET')
redirect_uri = os.getenv('REDIRECT_URI')
state = os.getenv('STATE')
# Endpoint to redirect to authorization link
@app.route('/auth')
def auth():
authorize_url = WebflowClient.authorize_url(
client_id=client_id,
redirect_uri=redirect_uri,
scope='sites:read',
state=state
)
return redirect(authorize_url)
# Endpoint to recieve info from the callback URI
@app.route('/callback')
def callback():
authorization_code = request.args.get('code')
state_received = request.args.get('state')
if state_received != state:
return 'State does not match', 400
# Request an access token
try:
access_token = WebflowClient().get_access_token(
client_id=client_id,
client_secret=client_secret,
code=authorization_code,
redirect_uri=redirect_uri,
)
# Store the access token securely, e.g., in a database or session (not shown here for brevity)
print(access_token)
# Redirect the user to the dashboard
return redirect('/dashboard')
except Exception as e:
print('Error during OAuth process:', e)
return 'Internal Server Error', 500
# Dashboard route for demonstration purposes
@app.route('/dashboard')
def dashboard():
return 'Welcome to your dashboard'
if __name__ == '__main__':
app.run(port=3000)
5. Start your server and test the authentication flow
You're ready to test your newly created authentication flow! In this step, we'll start our server, navigate to the authorization screen, and get an access token from Webflow.
-
Create a secure tunnel using ngrok OPTIONAL
-
Install ngrok
Download and install ngrok using a package manager or directly from their website.brew install ngrok/ngrok/ngrok
choco install ngrok
-
Authorize the ngrok agent
Copy your authentication token from ngrok, and enter the following command in your terminal.ngrok config add-authtoken $YOUR_AUTHTOKEN
-
Create a secure tunnel
In a new terminal window, run the following command to create a tunnel to your local server:
Ngrok will provide you with a public HTTPS URL that forwards to your local server running on port 3000. It should look something like this:ngrok http 3000
https://your-ngrok-url.ngrok.io
-
Update your redirect URI in your App's settings
- In your workspace settings, navigate to Apps & Integrations > App Development
- Find your App and click the "Edit App" button
- Navigate to the "Building Blocks" menu
- Update the Redirect URI with your NGROK URL, ensuring that you include the correct endpoint. For example, if your endpoint is
/callback
, your Redirect URI should look something likehttps://your-ngrok-url.ngrok.io/callback
.
-
Update the redirect URI in your
.env
file
Be sure to also update your redirect URI in your.env
file if you're passing your callback URI when creating an authorization link.REDIRECT_URI=https://your-ngrok-url.ngrok.io/callback
-
Install ngrok
-
Start your server
Enter the following command into your terminal to start your server.node server.js
python app.py
-
Start the authorization flow
- Start the authentication process Open your browser and go to http://localhost:3000/auth. You will be redirected to Webflow's authorization screen for your App.
- Authorize your app Select the workspace or sites you want your app to access, and click the "Authorize" button.
- Redirect to your app Upon successful authorization, you will be redirected to your app's dashboard.
- Verify the access token Check your terminal to see the access token printed out.
Didn't see what you expected? Read the troubleshooting guide below.
Revoke an access token
To revoke an access token that has been issued to your application, make a POST request to the following endpoint with the below parameters:
https://webflow.com/oauth/revoke_authorization
Request Parameters
The unique identifier for your OAuth application.
The secret key associated with your OAuth application.
The access token that you wish to revoke.
Example Request
curl -X POST https://webflow.com/oauth/revoke_authorization \
-H "Content-Type: application/json" \
-d '{
"client_id": "2ccc1b455c782fd60093590c83ee5e315b36bd6640507bb48570e5d0265c2854",
"client_secret": "d26ec60528020e1caf426db1a20dceaf5f4e3581bb29bc659b2886d46a7160ed",
"access_token": "53db404efe82daea0c65c635a49bc9388e470146b4d800f559cb9a7f3daf83f1"
}'
Response
If the request is successful, the access token will be revoked, and the response will return an HTTP status code of 200
OK with the following response body:
{
"did_revoke": true
}
Possible Errors
Error Type | Description |
---|---|
invalid_client | The client_id or client_secret is invalid or does not match the provided credentials. |
invalid_token | The access_token provided does not exist or has already been revoked. |
invalid_request | The request is missing one or more required parameters, or is otherwise malformed. |
Troubleshooting
- The
client_id
is correct and matches the one provided in your app's settings. - The scopes you are requesting are registered and valid for your app in the dashboard.
- If you are including the
redirect_uri
parameter, verify that it matches the one registered for your app in the dashboard. - Ensure that the URL is properly constructed and encoded if you are creating it manually.
- If you included a
redirect_uri
in your authorization link, you must also include it in your request for an access token. - Ensure your environment variables are loading, and you're sending the correct
client_id
andclient_secret
. - Make sure you have a fresh
code
value, these tokens are single-use and can not be used again. Also, the token is only valid for 15 minutes after it has been granted.