Netlify is a powerful platform for deploying and hosting modern web projects, offering seamless Git integration, automated builds, and serverless functions for a fast and scalable development workflow.
In this comprehensive guide, you will learn how to:
- Implement authentication in a Single-Page Application (SPA) using Logto on Netlify, demonstrated with a Vite + React example
- Secure your Netlify serverless functions with Logto authentication
- Deploy a production-ready application using our reference implementation: example-vite-react-logto
Checkout the online demo preview.
Prerequisites
These are the things you'll need to set up before starting this tutorial:
- A Netlify account
- A Logto Cloud account or a self-hosted Logto instance
Create a React app with Vite
Follow Vite's Getting Started Guide to create a React app.
npm create vite@latest
According to the creation guide and select your desired technology stack. In this article, we'll choose React + TypeScript.
Then enter the project root directory, install dependencies according to the guide, and run the application.
npm install
npm run dev
Deploy the app using Netlify
Follow the Get started with Netlify guides to deploy your application.
Once you have deployed your app, you can see the live site at https://<your-site-name>.netlify.app
.
Make a note of this URL as we'll need it later to configure Logto.
Integrate Logto into your app
To get started with Logto authentication:
- Navigate to the "Applications" page in the Logto Console
- Create a new application
- Select your frontend framework (React in our example)
- Note: You can create any Single-Page Application (SPA) or native app based on your needs
- Follow the integration guide provided in either:
- The Logto Console's built-in guide, or
- The official React authentication integration guide
- From the Application details page, make note of:
- Your Application ID
- The Logto endpoint (can be found in the "Endpoints & Credentials" section, typically in the format
https://<your-instance>.logto.app/
) These credentials will be required in subsequent steps.
Note that in our demo, we use the /callback route to handle Logto's Sign-in redirect. The Redirect URIs in Logto needs to be configured as https://<your-site-name>.netlify.app/callback
.
After users log out, we return to the homepage, so we set the Post sign-out redirect URIs to https://<your-site-name>.netlify.app
.
Then set the information of our created Logto Application in Netlify's Environment variables (Your website -> site configuration -> Environment variables):
VITE_LOGTO_ENDPOINT=<your-logto-endpoint>
VITE_LOGTO_APP_ID=<your-logto-app-id>
Then we use these configurations in our frontend project:
// App.tsx
const logtoConfig: LogtoConfig = {
endpoint: import.meta.env.VITE_LOGTO_ENDPOINT,
appId: import.meta.env.VITE_LOGTO_APP_ID,
}
You can view the final integrated code here: example-vite-react-logto.
Note that when we deploy to Netlify and log in through Logto, our URL doesn't automatically redirect to our Callback page. This is because Netlify doesn't support client-side routing for single-page applications (SPA) by default.
When you visit paths like /callback
, Netlify will try to find corresponding files on the server instead of forwarding the request to your React application.
At this point, we need to create a _redirects
file in the public directory of your Netlify project to tell Netlify to redirect all requests to your index.html:
/* /index.html 200
Alternatively, you can create a netlify.toml file in your project root:
[[redirects]]
from = "/*"
to = "/index.html"
status = 200
Now our routing should work properly.
Create backend APIs with Netlify functions
Netlify Functions provides a simple yet powerful way to build backend APIs. With Netlify Functions, we can write server-side logic without worrying about traditional server configuration and maintenance.
These functions are version-controlled, built, and deployed alongside your website, making the development and deployment process seamless between frontend and backend.
Let's start building our backend APIs using Netlify Functions.
First, we need to create a functions directory under the project's netlify directory, then create a hello.ts file:
// netlify/functions/hello.ts
export default async () => {
return Response.json({
message: `Hello world!`,
});
};
When we visit https://<your-site-name>.netlify.app/.netlify/functions/hello
, this function will be called and return "Hello world!"".
If you think the path /.netlify/functions/hello
looks a bit strange, you can set up a redirect to invoke the function by adding a redirect rule in the public/_redirects file:
/api/* /.netlify/functions/:splat
This way, when we visit https://<your-site-name>.netlify.app/api/hello
, it will call our function and return "Hello world!". This is actually the common way to build APIs using Netlify functions.
And we can access this API in our frontend project using fetch:
const response = await fetch(`${window.location.origin}/api/hello`);
const data = await response.json();
Protect your Netlify functions with Logto
Now that we have a backend API, we need to ensure that only authenticated users can access it. We'll protect our Netlify functions using Logto's authentication mechanism.
To protect our API endpoints:
-
Create an API Resource in Logto Console to represent our backend API:
- Go to "API Resources" in Logto Console and create a new API
- Set a name and API identifier (e.g.,
https://api.backend.com
) - Note down the API identifier on the API resource details page as we'll need it later
Configure your application to use this API resource by adding the identifier to your Netlify environment variables:
VITE_LOGTO_API_RESOURCE=<your-logto-api-resource-identifier>
- Update your Logto configuration to include this API resource:
const logtoConfig: LogtoConfig = {
endpoint: import.meta.env.VITE_LOGTO_ENDPOINT,
appId: import.meta.env.VITE_LOGTO_APP_ID,
resource: [import.meta.env.VITE_LOGTO_API_RESOURCE],
}
- When making requests to protected endpoints, your frontend needs to include a valid access token. Here's how to request and use it:
// ProtectedResource.tsx
// Get the access token for the protected resource
const accessToken = await getAccessToken(import.meta.env.VITE_LOGTO_API_RESOURCE);
// Include the token in your API requests
const response = await fetch(`${window.location.origin}/api/hello`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
Now, let's implement the token validation in our backend to ensure only requests with valid access tokens are processed.
Fist, we need to install the jose
dependency to help us verify the JWT token:
npm install jose
Then, we can implement the token validation in our backend:
// netlify/lib/auth.ts
import { createRemoteJWKSet, jwtVerify } from "jose";
const getTokenFromHeader = (headers: Headers) => {
const authorization = headers.get("authorization");
const bearerTokenIdentifier = "Bearer";
if (!authorization) {
throw new Error("Authorization header missing");
}
if (!authorization.startsWith(bearerTokenIdentifier)) {
throw new Error("Authorization token type not supported");
}
return authorization.slice(bearerTokenIdentifier.length + 1);
};
const verifyJwt = async (token: string) => {
const JWKS = createRemoteJWKSet(new URL('oidc/jwks', process.env.VITE_LOGTO_ENDPOINT));
const { payload } = await jwtVerify(token, JWKS, {
issuer: new URL('oidc', process.env.VITE_LOGTO_ENDPOINT).toString(),
audience: process.env.VITE_LOGTO_API_RESOURCE,
});
return payload;
};
export const verifyLogtoToken = async (request: Request) => {
try {
const token = getTokenFromHeader(request.headers);
const payload = await verifyJwt(token);
return payload;
} catch (error) {
throw new Error(`Unauthorized ${error}`);
}
}
Now, let's update our Netlify function to use the verifyLogtoToken
function:
// netlify/functions/hello.ts
import { verifyLogtoToken } from "../lib/auth.js";
export default async (request: Request) => {
const payload = await verifyLogtoToken(request);
const userId = payload.sub || "unknown";
return Response.json({
message: `Hello, ${userId}!`,
});
};
That's it! Now, our Netlify function is protected by Logto and only requests with valid access tokens will be processed.
Testing our demo
Now, deploy your app to Netlify and test it out! You can refer to the online demo preview here.
- Visit the live site at
https://<your-site-name>.netlify.app
- Click the "Sign in" button in the navigation bar
- Sign in with a Logto user account, and you will see you're signed in.
- Click the "Protected Resource" tab in the navigation bar, and you will be redirected to the protected page.
- Click the "View Protected Resource" button, and you will see the response data from the
api/hello
endpoint. - Click the "Sign out" button in the navigation bar, and you will be redirected to the home page.
Conclusion
This guide demonstrates how to integrate Logto authentication into a web application deployed on Netlify.
By configuring Logto applications and API resources, we implemented frontend authentication and protected Netlify Functions endpoints.
For more granular access control, you can leverage Logto's RBAC (Role-Based Access Control) capabilities by defining roles and permissions in Logto Console and validating user roles within Netlify Functions.
Top comments (0)