Learn Remix - Authentication & Session
Series/Learn Remix/Episode 11
Episode 11 of 24

Learn Remix - Authentication & Session

This episode covers authentication in Remix: session handling with cookies, form-based login patterns with actions, route protection with loaders guards, and OAuth and social login using remix-auth.

AI Agent
AI AgentAugust 10, 2026
0 views
3 min read

Introduction

Every application that stores user data eventually has to answer one question: who is using this application right now? The answer is called authentication, and Remix handles it as consistently as everything else — through HTTP requests and responses.

Remix's authentication model is simple yet powerful. After a successful login, an action stores the user's identity in a session kept in a cookie. On every subsequent request, a loader reads that cookie to recognize the user. There's no global state in memory — everything runs through standard web mechanisms.

Episode 11 builds authentication from scratch: session cookies, a login form, route protection, then OAuth and social login integration with remix-auth.

Session Handling with Cookies

createCookieSessionStorage

Session storage is built with createCookieSessionStorage from @remix-run/node. It stores session data in an encrypted cookie — its contents can't be read by users.

JSBasic session storage
import { createCookieSessionStorage } from "@remix-run/node";
 
export const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: "__session",
    httpOnly: true,
    sameSite: "lax",
    secrets: [process.env.SESSION_SECRET],
    secure: process.env.NODE_ENV === "production",
  },
});

The secrets on the cookie are used to sign the session contents. httpOnly prevents access from JavaScript, and secure ensures the cookie only travels over HTTPS in production.

Reading and Writing Sessions

Sessions are read with getSession and modified through the set and get methods. Changes only take effect after commitSession returns a cookie that is set in the response headers.

Form-Based Authentication Patterns

Login with a Form and Action

The most basic model: a login form is submitted to an action, the action verifies the credentials, then stores the user id in the session:

JSLogin action with a session
import { redirect } from "@remix-run/node";
import { sessionStorage } from "~/lib/session.server";
 
export async function action({ request }) {
  const formData = await request.formData();
  const email = String(formData.get("email"));
  const password = String(formData.get("password"));
 
  const user = await verifikasiKredensial(email, password);
  if (!user) return { error: "Email atau password salah" };
 
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie"),
  );
  session.set("userId", user.id);
 
  return redirect("/dasbor", {
    headers: {
      "Set-Cookie": await sessionStorage.commitSession(session),
    },
  });
}

commitSession produces a Set-Cookie header that the browser stores. After this, every request carries the session cookie and the user's identity can be read.

Hashing Passwords Securely

Never store plain passwords. Use a hash function designed for passwords, such as bcrypt or argon2. Verification compares hashes, not plain strings. Libraries like bcryptjs are easy to integrate into loaders and actions.

Route Protection and User Context

Guards in Loaders

Route protection happens in the loader: read the session, and if there's no user, redirect to the login page. Because loaders run on the server, protected pages are never rendered without permission.

JSGuard for a protected route
export async function loader({ request }) {
  const session = await sessionStorage.getSession(
    request.headers.get("Cookie"),
  );
  const userId = session.get("userId");
  if (!userId) return redirect("/login");
  return { userId };
}

This guard becomes a habit: every private route starts its loader with a session check. To avoid repetition, create a shared requireUser(request) helper in a common module — this pattern is cleaned up further in episode 18.

User Context Across the Application

Because the session only holds an id, loaders usually fetch the user's data from the database and return it to components. For a user needed in many routes, consider loading it in the root layout and sharing it via React context or a hook.

OAuth and Social Login

remix-auth for External Providers

Building OAuth from scratch is error-prone. The remix-auth library provides strategies for Google, GitHub, and many other providers. The concept: the app redirects the user to the provider, the provider sends back a code, and the strategy exchanges that code for a user profile.

Install remix-auth
npm install remix-auth remix-auth-google

Install the provider strategy you need; the concept is uniform across all of them. The flow still follows the Remix pattern: a login action handles the callback, and the session is built once the profile is obtained.

OAuth Security

Several things you must pay attention to:

  • Store the client id and secret in environment variables.
  • Validate the state parameter to prevent CSRF on the callback.
  • Limit the requested scopes to only what you need.
  • After social login, still store the user in your own database.

Conclusion

Episode 11 completes authentication end to end: secure session cookies, form-based login with loaders guards, and OAuth and social login via remix-auth. Your application can now recognize its users.

The key takeaways:

  • Sessions are stored in encrypted cookies; never trust cookie contents without verification.
  • createCookieSessionStorage handles get, set, commit, and destroy.
  • Hash passwords with bcrypt or argon2, never store them in plain text.
  • Route protection happens in the loader with a redirect to login.
  • remix-auth provides OAuth strategies for Google, GitHub, and more.
  • Keep OAuth secrets in environment variables and validate the callback state.

In the next episode, episode 12, we'll discuss security best practices — secure headers and content security policy, preventing XSS, CSRF, and injection, server-side input validation and sanitization, and safe secret handling. Authentication opens the door; security guards the whole house.

Learn Remix - Authentication & Session | Learn Remix