Crafting a web SDK for Logto in minutes
Tags: webdev, opensource, productivity, programming
Published: 2024-06-17 05:59:55

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
```

### 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,
});
```
### 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>;
};
```
### Step 4: Implement the hooks

Now, let's implement the hooks.

- `useLogto`: In this hook, we use the context to get the `LogtoClient `instance, 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 };
```
# 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](https://github.com/logto-io/js/tree/master/packages/react-sample).


# 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](https://github.com/logto-io/js/tree/master/packages/browser)
2. [Logto React SDK](https://github.com/logto-io/js/tree/master/packages/react)


{% cta https://logto.io/?ref=dev %} Try Logto Cloud for free {% endcta %}