Implementing Single Sign-On In React (Remix) In Under 10 Minutes
Tags: react, javascript, webdev, node
Published: 2024-01-08 08:48:38

---
title: Implementing Single Sign-On In React (Remix) In Under 10 Minutes
published: true
description: SSO enables users to securely authenticate with multiple applications and websites by using just one set of credentials. In this tutorial, you'll learn how to implement SSO in Remix.
tags: reactjs, javascript, webdev, nodejs
canonical_url: https://pmbanugo.me/implement-sso-in-react-remix-in-10-mins
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2024-01-08 08:42 +0000
---

Single sign-on (SSO) is an authentication scheme that enables users to authenticate with multiple applications using just one set of credentials (e.g., email and password). You may be familiar with SSO if you use Google services like Docs, Sheets, Drive, etc. You log in to Google Docs and when you visit Drive or Sheets, you’re automatically logged in.

In order words, single sign-on allows a user to log in once and access services/applications without re-entering authentication credentials.

## How Does This Work?

![sso-flow.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1665517946974/eO5ncxbDT.png)

SSO is achieved by having a central *identity/authentication* server through which authentication is performed, and then the session is shared with other domains/services without repeatedly asking for login credentials. Each service/domain will get a separate token or cookie when the users are authenticated. The user won't need to re-enter their login credential as long as there's an authenticated session to the identity server.

In this tutorial, we will implement single sign-on using [Clerk](https://clerk.dev/), and use GitHub as the OAuth server that'll verify the user’s credentials. We are going to modify an existing application to add protected routes and use the pre-built UI components.

![sample app](https://cdn.hashnode.com/res/hashnode/image/upload/v1674766339303/9c9cac70-f7a7-4bcb-8a23-910e9292ed6a.png)

## SSO for Remix Apps Using Clerk

Clerk is an SSO provider with integrations that help you securely adopt and implement a single sign-on authentication scheme for your apps in a few minutes (or hours if you have a complex use case). It has integrations for GitHub, Twitter, Apple and many more.

While it's possible to use protocols like OpenID Connect, OAuth and SAML for SSO flow, Clerk uses only the OAuth protocol at the moment. They provide SDKs and UI components to speed up the development time.

### Setting Up a Clerk Application

Let's begin by creating a Clerk application.

A Clerk app is a way to isolate the data and configuration for a specific application.

To create the application, go to the [Clerk Dashboard](https://dashboard.clerk.dev/) and click on the **Add application** card. You'll be presented with a subset of the available authentication options (you can change them later):

![Create Clerk application](https://cdn.hashnode.com/res/hashnode/image/upload/v1668705060962/q8tSLZXTw.png)

Enter your application name and click the **Show more** button in the *Social Connections section* to see the other available SSO providers.

Unselect Google and select GitHub, then click the **Finish** button at the bottom of the page. The application is created and you're taken to its homepage.

At the top of the page, you should see a menu that reads “*Instance*,” which can be used to switch between the development and production environment for the application. We'll work on the development environment.

The next step is to connect Clerk with GitHub as an OAuth application. Clerk uses a pre-configured shared OAuth credential and callback URI in development mode, but that's optional. You'll have to add the OAuth credential and redirect URI in the production environment when you need to use it. You're going to use the development environment for this tutorial—therefore, you don't need to create a GitHub OAuth app.

## Add the Clerk Client to Your Remix Application

You're going to clone a GitHub repo that'll be used as the boilerplate for our sample application. The repository can be found at [https://github.com/pmbanugo/remix-sso-clerk.dev](https://github.com/pmbanugo/remix-sso-clerk.dev).

You can fork and open the code in [GitHub Codespaces](https://www.telerik.com/blogs/introduction-github-codespaces), or open your terminal and run the following commands:

```bash
git clone https://github.com/pmbanugo/remix-sso-clerk.dev.git
cd remix-sso-clerk.dev
npm install
```

> If you use GitHub CLI, the command `gh repo clone pmbanugo/remix-sso-clerk.dev` would achieve the same for you.

Next, you need to install Clerk's Remix SDK. This gives you access to their pre-built components and React hooks, as well as helpers for Remix loaders. Run the command `npm install @clerk/remix` to install the SDK.

### Set Environment Variables

The next step is to add environment variables that the SDK will use. To do that, add a new file named **.env** to your project's root directory. Then add your Clerk keys to the file.

```plaintext
CLERK_PUBLISHABLE_KEY=your_publishable_key
CLERK_SECRET_KEY=your_secret_key
```

To get the respective secrets, go to the **API Keys** page in the Clerk dashboard. You'll find different sections for the varying secrets. Copy them and update the values in your *.env* file.

![Clerk API Keys](https://cdn.hashnode.com/res/hashnode/image/upload/v1674749846692/503f4335-1b68-4bf6-a780-4c2d3d5d7490.png)

### Configure Clerk Client

Now that you have the environment variables, the next step is to configure the application so that the authentication state can be accessed from any route.

Open the **app/root.tsx** file. Add the following import statement and a loader.

```typescript
import type { LoaderFunction } from "@remix-run/node";
import { rootAuthLoader } from "@clerk/remix/ssr.server";

export const loader: LoaderFunction = (args) => rootAuthLoader(args);
```

Next, remove the `export default` keywords from the `App` function definition and wrap it with the `ClerkApp` higher-order component.

```typescript
//Add this import statement as well
import { ClerkApp } from "@clerk/remix";

function App() {
  return (
    <Document>
      <Layout>
        <Outlet />
      </Layout>
    </Document>
  );
}

export default ClerkApp(App);
```

Clerk provides a `ClerkApp` HOC that provides the authentication state to your React tree. Clerk uses short-lived stateless JWT tokens by default and Remix's `CatchBoundary` in `app/root.tsx` to refresh expired authentication tokens.

<aside>
  <hr />
  <div class="row">
    <div class="col-4 u-normal-full u-small-mb0">
      <h4>
        Introduction to JSON Web Tokens (JWT)
      </h4>
    </div>
    <div class="col-8">
      <p>
        <a href="https://www.telerik.com/blogs/introduction-json-web-tokens-jwt" target="_blank">Learn more about using JWTs</a> to implement authorization in web applications.
      </p>
    </div>
  </div>
  <hr />
</aside>

Rename the existing `CatchBoundary()` in `app/root.tsx` to `CatchAll()` and export a new `CatchBoundary` function like so:

```typescript
// Updated the import statement for ClerkCatchBoundary
import { ClerkApp, ClerkCatchBoundary } from "@clerk/remix";

// renamed to CatchAll
function CatchAll() {
  const caught = useCatch();

  let message;    
  //... the rest of the exiting function's logic
}

export const CatchBoundary = ClerkCatchBoundary(CatchAll);
```

### Authenticating Users

Now we get to the interesting part, *authenticating users* 😎. We're going to use some of the UI components provided by Clerk. You can style them however you want, but for the sake of this tutorial, we will use the default style.

You're not limited to using the provided components. You can also build your own UI components and use the hooks provided in the SDK. You can read more about using hooks in the [documentation](https://clerk.dev/docs/reference/overview).

Let's get started with authenticating users 👩🏽‍💻🔐

We're going to add the `<SignInButton />` and `<SignOutButton />` components to the navigation bar, which will be used to sign in and sign out the user. While you still have the `root.tsx` file open, add the following import statement:

```typescript
import {
  ClerkApp,
  ClerkCatchBoundary,
  SignedIn,
  SignedOut,
  SignInButton,
  SignOutButton,
} from "@clerk/remix";
```

Scroll to line **118** where you have list items for the navigation bar, then add the following markup to the list.

```typescript
<li>
    <SignedOut>
        <SignInButton mode="modal" />
    </SignedOut>
    <SignedIn>
        <SignOutButton />
    </SignedIn>
</li>
```

The `<SignedOut />` component will display its children if the user is signed out, and the `<SignedIn />` component does the same but only for logged-in users. When the `<SignInButton />` is clicked, it will display a sign-in modal.

Those components will render a button, so you can style it by adding the following CSS to `app/styles/global.css` :

```css
button {
  background-color: var(--color-links);
  border: none;
  color: white;
  padding: 15px 20px;
  text-align: center;
  text-decoration: none;
  display: inline-block;
  font-size: 16px;
}
```

### Protected Routes

So far, we have used components to show or hide certain UI elements based on the logged-in state. There are cases where we'd like to guard certain routes and redirect unauthenticated users to the sign-in page. You can use the `getAuth()` function in your loader or action to check if the returned user is null. If it is, it means the user is signed out.

We're going to add a new `/demos/protected` route, which will redirect the user to the sign-in page if they're not already signed in. To implement this, create a new file **app/routes/demos/signin.tsx** and paste the code below in it.

```typescript
import { SignIn } from "@clerk/remix";

export default function LogIn() {
  return <SignIn />;
}
```

This will render the `<SignIn />` component from Clerk.

Add a new file **routes/demos/protected.tsx** and paste the following code in it:

```typescript
import { getAuth } from "@clerk/remix/ssr.server";
import type { LoaderFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { redirect } from "@remix-run/node";
import { useLoaderData } from "@remix-run/react";

export const loader: LoaderFunction = async (args) => {
  const { userId } = await getAuth(args);
  if (!userId) {
    return redirect("/demos/signin");
  }
  return json({ userId });
};

export default function Protected() {
  const data = useLoaderData<typeof loader>();

  return <h1>You're in! UserId is {data.userId}</h1>;
}
```

The loader function checks if there's a `userId` . If there is, it returns the *userId* and displays the message. If not, it redirects to the `/demos/signin` route.

Let's modify the Home page to include a link to this page. Open **app/routes/index.tsx** and add the code snippet below, on line **42**.

```typescript
{ to: "demos/protected", name: "Protected route" },
```

### Application Demo

At this point our application is ready and we can try it out. To test it yourself, open the terminal and run `npm run dev`. This should start up the dev server. Open *localhost:3000* in your browser and try to log in or visit the protected route.

![Demo](https://d585tldpucybw.cloudfront.net/sfimages/default-source/blogs/2023/2023-02/recorded-demo.gif)

## Wrap-up

This post covered what single sign-on is and how to implement it using GitHub and Clerk. Clerk is an SSO provider with integrations that help you securely adopt and implement a single sign-on authentication scheme for your apps in less time.

We used the UI components in this tutorial but you can do the same with your custom UI components and the React hooks from the Clerk SDK. You can read more about using hooks in the SDK [reference documentation](https://clerk.dev/docs/reference/overview).

You can find the completed code for this demo on [GitHub](https://github.com/pmbanugo/remix-sso-clerk.dev). If you have any questions, please comment here or reach out to me on [Twitter](https://twitter.com/p_mbanugo).

> Originally posted on Telerik's [Blog](https://www.telerik.com/blogs/implement-single-sign-on-react-remix-under-10-minutes)