Okta Strategy for Remix Auth
This package provides an authentication strategy for integrating Okta with Remix applications through the `remix-auth` library. It extends the `OAuth2Strategy` to handle Okta's specific OAuth 2.0 and OpenID Connect flows, supporting both Node.js and Cloudflare runtimes. The current stable version is 1.2.0, with updates generally following `remix-auth`'s release cadence and Okta API changes. `remix-auth-okta` enables developers to quickly set up user authentication against an Okta account, managing the redirect to Okta for login and processing the callback. Its key differentiators include tight integration with the `remix-auth` ecosystem, offering a standardized approach to adding Okta authentication, and flexibility to support both Okta's hosted login page and custom login forms within the Remix application.
Common errors
-
Error: invalid_client
cause The `clientID` or `clientSecret` provided to the `OktaStrategy` is incorrect, or the Okta application configuration is invalid (e.g., wrong type or disabled).fixVerify `clientID` and `clientSecret` against your Okta application settings. Ensure the Okta application is configured as a 'Web' application and that the client credentials are valid. -
Error: redirect_uri_mismatch
cause The `callbackURL` configured in `OktaStrategy` does not exactly match one of the allowed redirect URIs defined in your Okta application settings.fixGo to your Okta application settings and add or correct the 'Login redirect URIs' to precisely match the `callbackURL` specified in your `OktaStrategy` constructor. -
ReferenceError: sessionStorage is not defined
cause The `sessionStorage` object passed to the `Authenticator` is not correctly imported or configured for a server-side (Node.js/Cloudflare) Remix environment.fixEnsure `sessionStorage` is created using a server-side utility like `@remix-run/node`'s `createCookieSessionStorage` and correctly imported into `auth.server.ts`. It must include a `secrets` array for security. -
Error: Okta: Unable to retrieve user information from profile. [401 Unauthorized]
cause The access token obtained from Okta lacked sufficient scopes to retrieve the requested user profile information, or the token itself was invalid/expired during the profile fetch.fixVerify that the scopes configured in your Okta application (and potentially requested by the `OktaStrategy` if extended) include necessary profile scopes like `openid`, `profile`, `email`, etc., to allow access to user data.
Warnings
- gotcha Incorrectly configuring the `callbackURL` will lead to authentication failures or redirects to the wrong origin, resulting in 'invalid_redirect_uri' errors from Okta.
- breaking Major version upgrades of `remix-auth` (the peer dependency) may introduce API changes that require corresponding updates to `remix-auth-okta` or modifications in how `Authenticator` methods are used.
- gotcha When using the `withCustomLoginForm: true` option, you must also provide `oktaDomain` and implement a custom login form within your Remix app that collects user credentials (username/password).
- gotcha Missing or incorrect environment variables for `issuer`, `clientID`, or `clientSecret` will prevent the strategy from initializing or cause immediate authentication failures (e.g., 'invalid_client').
Install
-
npm install remix-auth-okta -
yarn add remix-auth-okta -
pnpm add remix-auth-okta
Imports
- OktaStrategy
const OktaStrategy = require('remix-auth-okta');import { OktaStrategy } from 'remix-auth-okta'; - Authenticator
const Authenticator = require('remix-auth');import { Authenticator } from 'remix-auth'; - User (Generic Type)
interface User { id: string; email: string; } export const authenticator = new Authenticator<User>(sessionStorage);
Quickstart
// app/utils/auth.server.ts
import { Authenticator } from "remix-auth";
import { OktaStrategy } from "remix-auth-okta";
import { createCookieSessionStorage } from "@remix-run/node"; // Example for session storage
// Define your user type that will be stored in the session
interface AppUser { id: string; email: string; }
// Configure session storage (replace with your actual session setup)
const sessionStorage = createCookieSessionStorage({
cookie: {
name: "_session",
httpOnly: true,
secure: process.env.NODE_ENV === "production", // Use secure cookies in production
secrets: [process.env.SESSION_SECRET ?? "s3cr3t"], // Must provide a secret
maxAge: 60 * 60 * 24 * 7, // 7 days
},
});
// Create an instance of the authenticator
export const authenticator = new Authenticator<AppUser>(sessionStorage);
// Initialize the Okta Strategy with environment variables
const oktaStrategy = new OktaStrategy(
{
issuer: process.env.OKTA_ISSUER ?? 'YOUR_OKTA_ISSUER_MISSING',
clientID: process.env.OKTA_CLIENT_ID ?? 'YOUR_OKTA_CLIENT_ID_MISSING',
clientSecret: process.env.OKTA_CLIENT_SECRET ?? 'YOUR_OKTA_CLIENT_SECRET_MISSING',
callbackURL: process.env.OKTA_CALLBACK_URL ?? 'http://localhost:3000/auth/okta/callback',
},
async ({ accessToken, refreshToken, extraParams, profile }) => {
// This callback runs after a successful Okta authentication.
// Here, you would typically find or create a user in your database
// based on the profile information (e.g., profile.email).
console.log("Okta Profile:", profile);
// Return a user object that will be stored in the session.
return { id: profile.id, email: profile.email ?? 'unknown@example.com' };
}
);
// Register the strategy with a unique name (e.g., "okta")
authenticator.use(oktaStrategy, "okta");