return res.status(500).json({ error: 'Failed to retrieve access token' });
_39
}
_39
// Attach access token in the request object so that you can make an authenticated request to Webflow
_39
req.accessToken = accessToken;
_39
_39
next(); // Proceed to next middleware or route handler
_39
});
_39
});
_39
};
_39
_39
module.exports = {
_39
createSessionToken,
_39
authenticateToken
_39
}
Learn how to establish a secure connection between your Designer Extension and Data Client. The provided client- and server- side code uses JSON Web Tokens (JWTs) to make authenticated requests without compromising client credentials. Before you get started, make sure your App is configured to use both the Data Extension and Data Client capabilities.
In this tutorial we’ll build a Hybrid App that:
Generates and resolves a user’s ID Token for authentication
Creates a session token (JWT) from user data
Makes authenticated requests to Webflow’s Data APIs.
Prerequisites
Make sure you have a Hybrid App with the sites:read and authorized_user:read scopes, as well as a bearer key generated from your app to use an App token. Additionally, you should have basic knowledge of Node.js and Express and familiarity with building React Single-Page Applications.
Set up your development environment
Clone the starter code
Clone an example Designer Extension and Data Client designed to give you a solid foundation. In this tutorial, you will be guided through adding specific code examples.
return res.status(500).json({ error: 'Failed to retrieve access token' });
_39
}
_39
// Attach access token in the request object so that you can make an authenticated request to Webflow
_39
req.accessToken = accessToken;
_39
_39
next(); // Proceed to next middleware or route handler
_39
});
_39
});
_39
};
_39
_39
module.exports = {
_39
createSessionToken,
_39
authenticateToken
_39
}
Add the jsonwebtoken package to handle token generation and verification, as well as the database module to access user information. Then, create a function to mint a session token using your client secret.
Establish request verification for protected endpoints
jwt.js
server.js
main.js
database.js
_39
const jwt = require('jsonwebtoken')
_39
const db = require('./database.js')
_39
_39
const createSessionToken = (user) => {
_39
const sessionToken = jwt.sign({ user }, process.env.WEBFLOW_CLIENT_SECRET, { expiresIn: '24h' }); // Example expiration time of 1 hour}
_39
return sessionToken
_39
}
_39
// Middleware to authenticate and validate JWT, and fetch the decrypted access token
_39
const authenticateToken = (req, res, next) => {
_39
const authHeader = req.headers.authorization;
_39
const token = authHeader && authHeader.split(' ')[1]; // Extract the token from 'Bearer <token>'
_39
if (!token) {
_39
return res.status(401).json({ message: 'Authentication token is missing' });
return res.status(500).json({ error: 'Failed to retrieve access token' });
_39
}
_39
// Attach access token in the request object so that you can make an authenticated request to Webflow
_39
req.accessToken = accessToken;
_39
_39
next(); // Proceed to next middleware or route handler
_39
});
_39
});
_39
};
_39
_39
module.exports = {
_39
createSessionToken,
_39
authenticateToken
_39
}
Create a middleware function that verifies incoming requests by retrieving the sessionToken from a request’s authorization header and verifying it. Once verified, search for a user’s accessToken in the database, and return it to use in authenticated requests.
Set up the Data Client
Configure authorization flow
jwt.js
server.js
main.js
database.js
_104
const express = require("express");
_104
const cors = require("cors");
_104
const { WebflowClient } = require("webflow-api");
_104
const axios = require("axios");
_104
require("dotenv").config();
_104
_104
const app = express(); // Create an Express application
_104
const db = require("./database.js"); // Load DB Logic
_104
const jwt = require("./jwt.js")
_104
_104
var corsOptions = { origin: ["http://localhost:1337"] };
_104
_104
// Middleware
_104
app.use(cors(corsOptions)); // Enable CORS with the specified options
res.status(500).json({ error: "Internal server error" });
_104
}
_104
});
_104
_104
// Start the server
_104
const PORT = process.env.PORT || 3000;
_104
app.listen(PORT, () => {
_104
console.log(`Server is running on http://localhost:${PORT}`);
_104
});
Add an endpoint on your server that retrieves an authorization_code from your OAuth callback URI. Use the code in the URI’s query parameter to request an accessToken for your user. Instantiate the Webflow client, and send a request to get your user’s details. Save the user details and accessToken to the database.
Get user details from an ID token
jwt.js
server.js
main.js
database.js
_104
const express = require("express");
_104
const cors = require("cors");
_104
const { WebflowClient } = require("webflow-api");
_104
const axios = require("axios");
_104
require("dotenv").config();
_104
_104
const app = express(); // Create an Express application
_104
const db = require("./database.js"); // Load DB Logic
_104
const jwt = require("./jwt.js")
_104
_104
var corsOptions = { origin: ["http://localhost:1337"] };
_104
_104
// Middleware
_104
app.use(cors(corsOptions)); // Enable CORS with the specified options
res.status(500).json({ error: "Internal server error" });
_104
}
_104
});
_104
_104
// Start the server
_104
const PORT = process.env.PORT || 3000;
_104
app.listen(PORT, () => {
_104
console.log(`Server is running on http://localhost:${PORT}`);
_104
});
Create an endpoint on your server that accepts an idToken. Within this endpoint, send a request to Webflow’s resolve token endpoint, which will return details about the user that requested the idToken.
Use these details to query the database for an authorized user. Be sure to authenticate the resolve token request with your appToken and include the idToken in the request body.
Mint a session token and return it to the Designer Extension
jwt.js
server.js
main.js
database.js
_104
const express = require("express");
_104
const cors = require("cors");
_104
const { WebflowClient } = require("webflow-api");
_104
const axios = require("axios");
_104
require("dotenv").config();
_104
_104
const app = express(); // Create an Express application
_104
const db = require("./database.js"); // Load DB Logic
_104
const jwt = require("./jwt.js")
_104
_104
var corsOptions = { origin: ["http://localhost:1337"] };
_104
_104
// Middleware
_104
app.use(cors(corsOptions)); // Enable CORS with the specified options
res.status(500).json({ error: "Internal server error" });
_104
}
_104
});
_104
_104
// Start the server
_104
const PORT = process.env.PORT || 3000;
_104
app.listen(PORT, () => {
_104
console.log(`Server is running on http://localhost:${PORT}`);
_104
});
Within the same endpoint, use the JWT middleware to mint a sessionToken from the user data. Once generated, return the sessionToken to complete the Designer Extension authentication process.
Create a protected endpoint for authenticated Webflow requests
jwt.js
server.js
main.js
database.js
_104
const express = require("express");
_104
const cors = require("cors");
_104
const { WebflowClient } = require("webflow-api");
_104
const axios = require("axios");
_104
require("dotenv").config();
_104
_104
const app = express(); // Create an Express application
_104
const db = require("./database.js"); // Load DB Logic
_104
const jwt = require("./jwt.js")
_104
_104
var corsOptions = { origin: ["http://localhost:1337"] };
_104
_104
// Middleware
_104
app.use(cors(corsOptions)); // Enable CORS with the specified options
res.status(500).json({ error: "Internal server error" });
_104
}
_104
});
_104
_104
// Start the server
_104
const PORT = process.env.PORT || 3000;
_104
app.listen(PORT, () => {
_104
console.log(`Server is running on http://localhost:${PORT}`);
_104
});
To facilitate requests for Webflow data from the Designer Extension, create an endpoint that uses the JWT middleware to verify the sessionToken and return an accessToken. Make an authenticated request to Webflow with the accessToken and return the data in the response.
Set up the Designer Extension
Create a request for an ID Token
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
To initiate user authentication, call the webflow.requestIdToken() function. This retrieves an idToken for the active user, which is essential for verifying the user's identity. It’s important to note that the idToken is only valid for 15 minutes, so it should be used promptly.
Create a request for sessionToken and save in Local Storage
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
Send the idToken in the body of a POST request to the “/token” endpoint of the Data Client. Upon successful request, the endpoint returns a sessionToken. Decode the session token to get user details like firstName, email , and sessionToken and set their respective state variables. Store the sessionToken in local storage to maintain user session state across the application.
Call the entire Function
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
Create a request to the Data Client’s “/sites” endpoint to access Webflow site data. Authenticate the request by including the sessionToken as a bearer token in the Authorization header. Set a siteData state variable to the data from the response.
Personalize the UI
Build initial user greeting
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
Create a section in your Designer Extension that dynamically greets the user, such as "Hello, firstName," using the user's first name obtained from thesessionToken. This greeting should update seamlessly without requiring a page reload.
Create a button to request site data
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
Add a button labeled "Get Site Data." pass the getSiteData handler to the button's onClick prop, so it will trigger a fetch request to the Data Client’s “/sites” endpoint when clicked.
List retrievd site data
jwt.js
server.js
main.js
database.js
_97
import React, { useState, useEffect } from "react";
_97
import ReactDOM from "react-dom/client";
_97
import axios from "axios";
_97
import DataTable from "./DataTable";
_97
import { ThemeProvider, Button, Container, Typography } from "@mui/material";
To render a list of sites, use the TableData component to dynamically list the site data once it is retrieved, and pass the siteData state variable as prop.
Congratulations!
You have a working Hybrid app that can securely make requests to Webflow’s Data APIs.
Once you've finished developing your app, discover how to publish it and share it in the marketplace.
Learn how to establish a secure connection between your Designer Extension and Data Client. The provided client- and server- side code uses JSON Web Tokens (JWTs) to make authenticated requests without compromising client credentials. Before you get started, make sure your App is configured to use both the Data Extension and Data Client capabilities.
In this tutorial we’ll build a Hybrid App that:
Generates and resolves a user’s ID Token for authentication
Creates a session token (JWT) from user data
Makes authenticated requests to Webflow’s Data APIs.
Prerequisites
Make sure you have a Hybrid App with the sites:read and authorized_user:read scopes, as well as a bearer key generated from your app to use an App token. Additionally, you should have basic knowledge of Node.js and Express and familiarity with building React Single-Page Applications.
Set up your development environment
Clone the starter code
Clone an example Designer Extension and Data Client designed to give you a solid foundation. In this tutorial, you will be guided through adding specific code examples.
Install dependencies and add environment variables
Install the necessary dependencies, and configure environment variables for your App’s Client ID and secret.
$ npm install
Add JWT Middleware
Add logic for minting session tokens
Add the jsonwebtoken package to handle token generation and verification, as well as the database module to access user information. Then, create a function to mint a session token using your client secret.
Establish request verification for protected endpoints
Create a middleware function that verifies incoming requests by retrieving the sessionToken from a request’s authorization header and verifying it. Once verified, search for a user’s accessToken in the database, and return it to use in authenticated requests.
Set up the Data Client
Configure authorization flow
Add an endpoint on your server that retrieves an authorization_code from your OAuth callback URI. Use the code in the URI’s query parameter to request an accessToken for your user. Instantiate the Webflow client, and send a request to get your user’s details. Save the user details and accessToken to the database.
Get user details from an ID token
Create an endpoint on your server that accepts an idToken. Within this endpoint, send a request to Webflow’s resolve token endpoint, which will return details about the user that requested the idToken.
Use these details to query the database for an authorized user. Be sure to authenticate the resolve token request with your appToken and include the idToken in the request body.
Mint a session token and return it to the Designer Extension
Within the same endpoint, use the JWT middleware to mint a sessionToken from the user data. Once generated, return the sessionToken to complete the Designer Extension authentication process.
Create a protected endpoint for authenticated Webflow requests
To facilitate requests for Webflow data from the Designer Extension, create an endpoint that uses the JWT middleware to verify the sessionToken and return an accessToken. Make an authenticated request to Webflow with the accessToken and return the data in the response.
Set up the Designer Extension
Create a request for an ID Token
To initiate user authentication, call the webflow.requestIdToken() function. This retrieves an idToken for the active user, which is essential for verifying the user's identity. It’s important to note that the idToken is only valid for 15 minutes, so it should be used promptly.
Create a request for sessionToken and save in Local Storage
Send the idToken in the body of a POST request to the “/token” endpoint of the Data Client. Upon successful request, the endpoint returns a sessionToken. Decode the session token to get user details like firstName, email , and sessionToken and set their respective state variables. Store the sessionToken in local storage to maintain user session state across the application.
Call the entire Function
Get Webflow data via an authenticated request
Create a request to the Data Client’s “/sites” endpoint to access Webflow site data. Authenticate the request by including the sessionToken as a bearer token in the Authorization header. Set a siteData state variable to the data from the response.
Personalize the UI
Build initial user greeting
Create a section in your Designer Extension that dynamically greets the user, such as "Hello, firstName," using the user's first name obtained from thesessionToken. This greeting should update seamlessly without requiring a page reload.
Create a button to request site data
Add a button labeled "Get Site Data." pass the getSiteData handler to the button's onClick prop, so it will trigger a fetch request to the Data Client’s “/sites” endpoint when clicked.
List retrievd site data
To render a list of sites, use the TableData component to dynamically list the site data once it is retrieved, and pass the siteData state variable as prop.
Congratulations!
You have a working Hybrid app that can securely make requests to Webflow’s Data APIs.