DEV Community

Cover image for Full-stack auth solution with Logto on Netlify: Protecting web apps and serverless functions
Xiao Yijun for Logto

Posted on • Originally published at blog.logto.io

Full-stack auth solution with Logto on Netlify: Protecting web apps and serverless functions

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:

Create a React app with Vite

Follow Vite's Getting Started Guide to create a React app.

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

  1. Navigate to the "Applications" page in the Logto Console
  2. 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
  3. Follow the integration guide provided in either:
  4. 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>
Enter fullscreen mode Exit fullscreen mode

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,
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Alternatively, you can create a netlify.toml file in your project root:

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200
Enter fullscreen mode Exit fullscreen mode

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!`,
  });
};
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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:

  1. 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
  2. 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>
Enter fullscreen mode Exit fullscreen mode
  1. 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],
}
Enter fullscreen mode Exit fullscreen mode
  1. 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}`,
  },
});
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

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}!`,
  });
};
Enter fullscreen mode Exit fullscreen mode

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.

  1. Visit the live site at https://<your-site-name>.netlify.app
  2. Click the "Sign in" button in the navigation bar
  3. Sign in with a Logto user account, and you will see you're signed in.
  4. Click the "Protected Resource" tab in the navigation bar, and you will be redirected to the protected page.
  5. Click the "View Protected Resource" button, and you will see the response data from the api/hello endpoint.
  6. 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)