---
title: "Auth Setup"
description: "Install the nuxt-auth-utils module, register a GitHub OAuth app, configure environment variables, and create the OAuth callback handler."
canonical_url: "https://vercel.com/academy/nuxt-on-vercel/auth-setup"
md_url: "https://vercel.com/academy/nuxt-on-vercel/auth-setup.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-05-03T17:29:32.903Z"
content_type: "lesson"
course: "nuxt-on-vercel"
course_title: "Nuxt on Vercel"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Auth Setup

# Auth Setup

Authentication in Next.js usually means picking a library (NextAuth, Clerk, Lucia), reading 40 pages of docs, configuring adapters, setting up callback URLs, and hoping the session strategy you chose doesn't bite you in six months. It's powerful and flexible, which is another way of saying there are a lot of ways to get it wrong.

Nuxt has `nuxt-auth-utils`. One module, zero adapters, built-in session handling, and OAuth helpers for every major provider. You add it to your config, create one server route, set three environment variables, and authentication works. It's opinionated in the way that saves you from yourself.

## Outcome

Install `nuxt-auth-utils`, register a GitHub OAuth app, and create the server-side callback handler.

## Fast Track

1. Install `nuxt-auth-utils` and add it to `nuxt.config.ts`
2. Register a GitHub OAuth app and set environment variables
3. Create the GitHub OAuth handler in `server/routes/auth/github.get.ts`

## Hands-on exercise 3.1

Wire up GitHub OAuth from scratch.

**Requirements:**

1. Install `nuxt-auth-utils` and add it to the `modules` array in `nuxt.config.ts`
2. Register a new OAuth app on GitHub with the callback URL `http://localhost:3000/auth/github`
3. Create a `.env` file with `NUXT_OAUTH_GITHUB_CLIENT_ID`, `NUXT_OAUTH_GITHUB_CLIENT_SECRET`, and `NUXT_SESSION_PASSWORD`
4. Create `server/routes/auth/github.get.ts` that handles the OAuth callback
5. On success, store the user's login, avatar URL, and ID in the session

**Implementation hints:**

- `nuxt-auth-utils` auto-imports `defineOAuthGitHubEventHandler`, `setUserSession`, `getUserSession`, and `requireUserSession` in server routes
- It also auto-imports `useUserSession` in Vue components
- The module reads `NUXT_OAUTH_GITHUB_CLIENT_ID` and `NUXT_OAUTH_GITHUB_CLIENT_SECRET` automatically. No config mapping needed
- `NUXT_SESSION_PASSWORD` must be at least 32 characters. It encrypts the session cookie
- Server routes in `server/routes/` (not `server/api/`) map directly to URLs. `server/routes/auth/github.get.ts` becomes `/auth/github`

First, update the Nuxt config to include the module:

```typescript title="nuxt.config.ts" {6}
import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-05-01",

  modules: ["nuxt-auth-utils"],

  css: ["~/assets/css/main.css"],

  vite: {
    plugins: [tailwindcss()],
  },
});
```

One line. The module handles session management, cookie encryption, OAuth flows, and auto-importing its composables. In the Next.js world, this would be the equivalent of installing NextAuth, creating an auth config file, setting up the route handler, configuring providers, and wiring up the session provider component.

Next, register a GitHub OAuth app. Go to GitHub Settings > Developer settings > OAuth Apps > New OAuth App:

- **Application name:** Hot Springs Finder (Dev)
- **Homepage URL:** `http://localhost:3000`
- **Authorization callback URL:** `http://localhost:3000/auth/github`

GitHub gives you a Client ID and lets you generate a Client Secret. Put them in a `.env` file:

```bash title=".env"
NUXT_OAUTH_GITHUB_CLIENT_ID=your_client_id_here
NUXT_OAUTH_GITHUB_CLIENT_SECRET=your_client_secret_here
NUXT_SESSION_PASSWORD=a-random-string-at-least-32-characters-long
```

`nuxt-auth-utils` picks up the `NUXT_OAUTH_*` variables by convention. No config object, no provider setup. The naming convention IS the configuration.

Now the OAuth handler. This is the route GitHub redirects to after the user authorizes your app:

```typescript title="server/routes/auth/github.get.ts"
export default defineOAuthGitHubEventHandler({
  async onSuccess(event, { user }) {
    await setUserSession(event, {
      user: {
        login: user.login,
        avatar_url: user.avatar_url,
        id: user.id,
      },
    });
    return sendRedirect(event, "/springs");
  },
  onError(event, error) {
    console.error("GitHub OAuth error:", error);
    return sendRedirect(event, "/login?error=auth");
  },
});
```

`defineOAuthGitHubEventHandler` handles the entire OAuth dance: redirecting to GitHub, exchanging the code for a token, fetching the user profile. Your job is just to decide what to do with the result.

`setUserSession` stores whatever you pass to the `user` key in an encrypted, HTTP-only cookie. No database, no session store. The cookie IS the session. This is fine for our use case. For production apps with complex session needs, you'd add a database adapter, but the module supports that too.

Notice this file lives in `server/routes/auth/`, not `server/api/auth/`. The difference: `server/api/` routes are prefixed with `/api/`. `server/routes/` routes map directly to URLs. Our callback URL is `/auth/github`, not `/api/auth/github`.

\*\*Warning: Restart after .env changes\*\*

Nuxt reads `.env` at startup. If you add or change environment variables, restart the dev server. Hot reload doesn't pick up `.env` changes.

\*\*Note: Generate a session password\*\*

Need a random 32-character string? Run `openssl rand -base64 32` in your terminal. Or type whatever you want, as long as it's long enough. The password never leaves the server.

## Try It

Restart the dev server (to pick up the new module and `.env` changes):

```bash
pnpm dev
```

Visit `http://localhost:3000/auth/github`. You should be redirected to GitHub's authorization page. Authorize the app, and GitHub redirects you back to `/auth/github`, which runs your handler, sets the session, and redirects to `/springs`.

Open your browser's dev tools, go to the Application/Storage tab, and look for a cookie called `nuxt-session`. It should be there, HTTP-only, and encrypted.

If you see an error page instead, check:

- Are the Client ID and Secret correct in `.env`?
- Did you restart the dev server after creating `.env`?
- Is the callback URL in your GitHub OAuth app settings exactly `http://localhost:3000/auth/github`?

## Commit

```bash
git add -A && git commit -m "feat(auth): add nuxt-auth-utils with GitHub OAuth handler"
```

## Done-When

- [ ] `nuxt-auth-utils` is in the `modules` array in `nuxt.config.ts`
- [ ] `.env` contains the three required environment variables
- [ ] Visiting `/auth/github` redirects to GitHub and back
- [ ] After authorization, a `nuxt-session` cookie exists in the browser
- [ ] You can explain the difference between `server/routes/` and `server/api/`

## Solution

```typescript title="nuxt.config.ts"
import tailwindcss from "@tailwindcss/vite";

export default defineNuxtConfig({
  compatibilityDate: "2025-05-01",

  modules: ["nuxt-auth-utils"],

  css: ["~/assets/css/main.css"],

  vite: {
    plugins: [tailwindcss()],
  },
});
```

```bash title=".env"
NUXT_OAUTH_GITHUB_CLIENT_ID=your_client_id_here
NUXT_OAUTH_GITHUB_CLIENT_SECRET=your_client_secret_here
NUXT_SESSION_PASSWORD=a-random-string-at-least-32-characters-long
```

```typescript title="server/routes/auth/github.get.ts"
export default defineOAuthGitHubEventHandler({
  async onSuccess(event, { user }) {
    await setUserSession(event, {
      user: {
        login: user.login,
        avatar_url: user.avatar_url,
        id: user.id,
      },
    });
    return sendRedirect(event, "/springs");
  },
  onError(event, error) {
    console.error("GitHub OAuth error:", error);
    return sendRedirect(event, "/login?error=auth");
  },
});
```


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
