Crafting a web SDK for Logto in minutes

palomino

Palomino

Posted on June 17, 2024

Crafting a web SDK for Logto in minutes

Learn how to create a custom SDK for Logto using @logto/browser.


Logto, an open-source auth platform, offers a plethora of official SDKs designed to simplify integration for various frameworks and platforms. However, there are still many platforms that do not have official SDKs.

To bridge this gap, Logto provides the fundamental package @logto/browser, designed to help developers craft custom SDKs tailored to specific requirements. This package implements the core functionalities of Logto, detached from any specific framework or platform, as long as it supports JavaScript and runs in a browser environment.

In this guide, we will walk you through the steps to create a React SDK using @logto/browser, this SDK will implement the sign-in flow. You can follow the same steps to create an SDK for any other JavaScript-based platform that running in browser.

The sign-in flow

Before we start, let's understand the sign-in flow in Logto. The sign-in flow consists of the following steps:

  1. Redirect to Logto: The user is redirected to the Logto sign-in page.
  2. Authenticate: The user inputs their credentials and authenticates with Logto.
  3. Redirect back to your app: After successful authentication, the user is redirected back to your app with an auth code.
  4. Code exchange: Your app exchanges the auth code for tokens.

Brief introduction of @logto/browser

The @logto/browser package exposes a LogtoClient class that provides the core functionalities of Logto, including methods for sign-in flow:

  1. signIn(): Generates the OIDC auth URL, and redirects to it.
  2. handleSignInCallback(): Check and parse the callback URL and extract the auth code, then exchange the code for tokens by calling token endpoint.
  3. isAuthenticated(): Check if the user is authenticated.

Crafting the React SDK

In the SDK, we will provide 2 hooks: useLogto and useHandleSignInCallback, and along with a LogtoProvider component:

  1. useLogto: A hook that provides the signIn method to trigger the sign-in flow, and the isAuthenticated state to check if the user is authenticated.
  2. useHandleSignInCallback: A hook that handles the callback URL and exchanges the auth code for tokens, complete the sign-in flow.

To use the SDK, you can simply wrap your app with the LogtoProvider component, and use the hooks to check auth state, sign-in and handle the callback.

Step 1: Install the package

First, install the @logto/browser package using npm or other package managers:

npm install @logto/browser
Enter fullscreen mode Exit fullscreen mode

Step 2: Define the context of React

Define the context of the provider, containing 3 parts:

  1. The underlying LogtoClient instance which will be initialized in the provider, and used in the hooks.
  2. The authentication state.
  3. The method to set the authentication state.

Create a new file context.tsx and write the following code:

import type LogtoClient from '@logto/browser';
import { createContext } from 'react';

export type LogtoContextProps = {
  /** The underlying LogtoClient instance (from `@logto/browser`). */
  logtoClient?: LogtoClient;
  /** Whether the user is authenticated or not. */
  isAuthenticated: boolean;
  /** Sets the authentication state. */
  setIsAuthenticated: React.Dispatch<React.SetStateAction<boolean>>;
};

export const throwContextError = (): never => {
  throw new Error('Must be used inside <LogtoProvider> context.');
};

/**
 * The context for the LogtoProvider.
 *
 * @remarks
 * Instead of using this context directly, in most cases you should use the `useLogto` hook.
 */
export const LogtoContext = createContext<LogtoContextProps>({
  logtoClient: undefined,
  isAuthenticated: false,
  setIsAuthenticated: throwContextError,
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Implement the provider

With the context ready, let's implement the provider. The provider will initialize the LogtoClient instance, check if the user is authenticated, and provide the context to its children.

Create a new file provider.tsx:

import LogtoClient, { type LogtoConfig } from '@logto/browser';
import { type ReactNode, useEffect, useMemo, useState, useCallback } from 'react';

import { LogtoContext } from './context.js';

export type LogtoProviderProps = {
  config: LogtoConfig;
  children?: ReactNode;
};

export const LogtoProvider = ({ config, children }: LogtoProviderProps) => {
  const [loadingCount, setLoadingCount] = useState(1);
  const memoizedLogtoClient = useMemo(() => ({ logtoClient: new LogtoClient(config) }), [config]);
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  useEffect(() => {
    (async () => {
      const isAuthenticated = await memoizedLogtoClient.logtoClient.isAuthenticated();

      setIsAuthenticated(isAuthenticated);
    })();
  }, [memoizedLogtoClient]);

  const memoizedContextValue = useMemo(
    () => ({
      ...memoizedLogtoClient,
      isAuthenticated,
      setIsAuthenticated,
    }),
    [memoizedLogtoClient, isAuthenticated, setIsAuthenticated]
  );

  return <LogtoContext.Provider value={memoizedContextValue}>{children}</LogtoContext.Provider>;
};
Enter fullscreen mode Exit fullscreen mode

Step 4: Implement the hooks

Now, let's implement the hooks.

  • useLogto: In this hook, we use the context to get the LogtoClientinstance, and provide the signIn method and isAuthenticated state. You can continue to add more methods to this hook.
  • useHandleSignInCallback: This hook will read the callback URL from the browser, extract the auth code, and exchange it for tokens. It will also set the authentication state to true after the user is authenticated.

Create a new file hooks.ts and write the following code:

import type LogtoClient from '@logto/browser';
import { useCallback, useContext, useEffect, useMemo, useRef } from 'react';

import { LogtoContext, throwContextError } from './context.js';

type Logto = {
  isAuthenticated: boolean;
} & Pick<LogtoClient, 'signIn'>;

const useHandleSignInCallback = (callback?: () => void) => {
  const { logtoClient, isAuthenticated, setIsAuthenticated } = useContext(LogtoContext);

  useEffect(() => {
    if (!logtoClient) {
      return;
    }

    (async () => {
      if (!isAuthenticated) {
        await logtoClient.handleSignInCallback(window.location.href);
        setIsAuthenticated(true);
        callbackRef.current?.();
      }
    })();
  }, [isAuthenticated, logtoClient, setIsAuthenticated]);

  return {
    isAuthenticated,
  };
};

const useLogto = (): Logto => {
  const { logtoClient, isAuthenticated } = useContext(LogtoContext);

  const client = logtoClient ?? throwContextError();

  const methods = useMemo(
    () => ({
      signIn: client.signIn,
      // other methods
    }),
    [client, proxy]
  );

  return {
    isAuthenticated,
    ...methods,
  };
};

export { useLogto, useHandleSignInCallback };
Enter fullscreen mode Exit fullscreen mode

Checkpoint: using the SDK

Now, you have crafted the React SDK for Logto. You can use it in your app by wrapping the app with the LogtoProvider component, and using the hooks to check the auth state, sign in, and handle the callback. You can check the official React sample project here.

Conclusion

In this guide, we have walked you through the steps to create a React SDK for Logto implementing the basic auth flow. The SDK provided here is a basic example. You can extend it by adding more methods and functionalities to meet your app's needs.

You can follow the same steps to create an SDK for any other JavaScript-based platform that runs in a browser.

Resources:

  1. Logto Browser SDK
  2. Logto React SDK

Try Logto Cloud for free

💖 💪 🙅 🚩
palomino
Palomino

Posted on June 17, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related