본문으로 건너뛰기
cccskills
무료GitHub에 공개

mfa

Build multi-factor authentication flows with @auth0/nextjs-auth0 v4. The skill covers redirect and server-triggered step-up checks, managing authenticators, and popup challenges, including how to pass MFA tokens safely and handle common errors.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Multi-factor authentication with @auth0/nextjs-auth0 v4: redirect step-up via Universal Login (amr gating), server-side step-up on mfa_required, the MFA management API (getAuthenticators, enroll, challenge, verify), and reactive step-up via mfa.challengeWithPopup(). Covers the beforeSessionSaved amr hook, the secure mfa_token handoff, and MFA error classes.

auth0/nextjs-auth0스타 2,312(GitHub 2,312 + cccskills 0)

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • Requiring MFA before a sensitive action
  • Redirecting users to complete an MFA challenge
  • Enrolling or verifying an authenticator
  • Triggering an MFA challenge in a popup

포함된 파일(1)

  • SKILL.md8.2 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

@auth0/nextjs-auth0 — MFA (v4)

Minimum version: 4.15.0 for base MFA + the MFA management APIs; reactive popup step-up (mfa.challengeWithPopup) requires 4.19.0+.

Framework-specific surface only. The shared mechanic, tenant config, amr/error tables, and full MFA API detail live in guides/mfa.md at the package root.

Singleton client (lib/auth0.ts):

import { Auth0Client } from "@auth0/nextjs-auth0/server";
export const auth0 = new Auth0Client({ mfaTokenTtl: 300 }); // seconds; matches Auth0's mfa_token expiry. Or AUTH0_MFA_TOKEN_TTL env var.

MFA methods are on auth0.mfa (server, @auth0/nextjs-auth0/server) and the mfa named export (client, @auth0/nextjs-auth0/client). Errors import from @auth0/nextjs-auth0/errors.

Flow 0 — redirect step-up via Universal Login (use this when the task says "redirect them to step up")

Gate the action on the amr claim; when MFA is missing, redirect through v4's login route with the PAPE acr_values + max_age=0. v4 has no handleLogin export — login is middleware-driven, so pass the params one of two ways (both verified on 4.30.0). Do not grep node_modules to rediscover this.

Step 1 — persist amr so the gate can read it. Add the beforeSessionSaved hook (see "Reading amr" below); without it session.user.amr is always undefined and the redirect loops.

Step 2 — trigger the step-up. Either:

(a) Redirect to /auth/login with query params (simplest). handleLogin forwards every query param except challengeMode/returnTo straight to /authorize:

import { redirect } from "next/navigation";

const MFA_ACR = "http://schemas.openid.net/pape/policies/2007/06/multi-factor";
redirect(`/auth/login?acr_values=${encodeURIComponent(MFA_ACR)}&max_age=0&returnTo=/dashboard/transfer`);

(b) Custom Route Handler calling auth0.startInteractiveLogin (type-safe). Signature: startInteractiveLogin(options?: StartInteractiveLoginOptions): Promise<NextResponse>:

// app/auth/step-up/route.ts
import type { NextResponse } from "next/server";
import type { StartInteractiveLoginOptions } from "@auth0/nextjs-auth0/types";
import { auth0 } from "@/lib/auth0";

export async function GET(): Promise<NextResponse> {
  const options: StartInteractiveLoginOptions = {
    authorizationParameters: {          // AuthorizationParameters from "@auth0/nextjs-auth0/types"
      acr_values: "http://schemas.openid.net/pape/policies/2007/06/multi-factor",
      max_age: 0,                       // number (seconds); 0 forces a fresh challenge
    },
    returnTo: "/dashboard/transfer",
  };
  return auth0.startInteractiveLogin(options); // returns the redirect NextResponse
}

Step 3 — gate the sensitive action (Server Component or Server Action), enforced server-side:

// app/dashboard/transfer/page.tsx
import { redirect } from "next/navigation";
import { auth0 } from "@/lib/auth0";

export default async function TransferPage() {
  const session = await auth0.getSession(); // SessionData | null
  if (!session?.user.amr?.includes("mfa")) {
    redirect("/auth/step-up"); // or the /auth/login?... URL from step 2(a)
  }
  // render the transfer form; re-check the same amr in the Server Action before moving funds
}

Step 4 — after Universal Login completes MFA it returns to returnTo; beforeSessionSaved writes amr, the gate now passes, and the transfer runs. A frontend amr check is UX only — always enforce on the server.

Flow 1 — server-side step-up (Route Handler / Server Action)

Force a refresh so the post-login Action runs and can throw MfaRequiredError:

const { token } = await auth0.getAccessToken({ audience: "https://my-api", refresh: true }); // refresh required; default returns cached token

Pass mfa_token to the MFA page via an httpOnly cookie, never the URL:

import { cookies } from "next/headers";
import { redirect } from "next/navigation";
import { MfaRequiredError } from "@auth0/nextjs-auth0/server";

try {
  const { token } = await auth0.getAccessToken({ audience: "https://my-api", refresh: true });
  // proceed with token
} catch (error) {
  if (error instanceof MfaRequiredError) {
    const session = await auth0.getSession();
    (await cookies()).set("mfa_token",
      JSON.stringify({ sub: session?.user.sub, token: error.mfa_token }),
      { httpOnly: true, secure: true, sameSite: "lax", maxAge: 300 });
    redirect("/mfa");
  }
  throw error;
}

On /mfa, read the cookie back. If auth0.getSession() returns null (user logged out or session expired), redirect to login and delete the MFA cookie. Otherwise re-check sub === session.user.sub to prevent one user completing another's MFA challenge, drive auth0.mfa.*, then (await cookies()).delete("mfa_token") after a successful verify.

Flow 2 — MFA management API (auth0.mfa.*)

All take { mfaToken }:

  • getAuthenticators({ mfaToken }) → Authenticator[] Authenticator: { id: string, authenticatorType: 'otp'|'oob', oobChannel?: 'sms'|'voice'|'auth0'|'email', active: boolean }
  • enroll({ mfaToken, authenticatorTypes, oobChannels?, phoneNumber?, email? }) — OTP: ["otp"] → { barcodeUri: string, secret: string, recoveryCodes?: string[] }; OOB: ["oob"] + oobChannels → { oobCode: string, bindingMethod?: string }.
  • challenge({ mfaToken, challengeType: 'oob', authenticatorId }) → { oobCode: string, bindingMethod?: string } (OOB only — not needed for OTP).
  • verify({ mfaToken, otp: string }) / ({ mfaToken, oobCode, bindingCode: string }) / ({ mfaToken, recoveryCode }) → MfaVerifyResponse (tokens written to session). bindingCode is required when using OOB. When recoveryCode is used, the response may include a new recovery_code — show it to the user once.

Callable from a Server Component/Action (auth0.mfa.verify) or a client component (import { mfa } from "@auth0/nextjs-auth0/client").

Flow 3 — reactive popup step-up (client components)

mfa.challengeWithPopup() calls window.open(), so it must run from a direct user-gesture handler (a button onClick), never from an async catch:

"use client";
import { getAccessToken, mfa } from "@auth0/nextjs-auth0/client";
import { MfaRequiredError } from "@auth0/nextjs-auth0/errors";

async function handleStepUp() {
  const { token } = await mfa.challengeWithPopup({ audience: "https://api.example.com" });
  await callApi(token);
}

Options: audience (required), scope, acr_values (defaults to the PAPE MFA URI), timeout (ms, default 60000), popupWidth/popupHeight. Popup errors: PopupBlockedError, PopupCancelledError, PopupTimeoutError, PopupInProgressError, ExecutionContextError (called server-side). If a CSP blocks inline scripts, set cspNonce on Auth0Client or the popup completes but postMessage never returns (PopupTimeoutError).

Reading amr

v4 persists only a default claim subset, so amr is dropped by default. Opt it in on the client with a beforeSessionSaved hook — copy the claim from the ID token onto session.user, then read session.user.amr server-side:

export const auth0 = new Auth0Client({
  async beforeSessionSaved(session, idToken) {
    if (!idToken) return session;
    const claims = decodeJwt(idToken); // e.g. jose's decodeJwt; idToken is the raw JWT string
    return { ...session, user: { ...session.user, amr: claims.amr } };
  },
});

session is SessionData ({ user, tokenSet, internal, ... }); the hook's return type is the same. amr lands at session.user.amr and survives to auth0.getSession().

Server error classes

MfaRequiredError, MfaNoAvailableFactorsError, MfaTokenExpiredError, MfaTokenInvalidError, MfaGetAuthenticatorsError, MfaEnrollmentError, MfaChallengeError, MfaVerifyError (from @auth0/nextjs-auth0/errors).

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

Set up Auth0 authentication in Next.js with @auth0/nextjs-auth0 v4 for App Router or Pages Router. Configure the client and middleware, add login and user context, protect pages and API routes, and retrieve sessions or access tokens for API calls.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Adding Auth0 login to a Next.js application
  • Configuring middleware and authentication routes
  • Protecting pages and API endpoints
auth0/nextjs-auth02.3천2026년 10월 26일 업데이트

Use this skill to build B2B authentication for multiple customer organizations with Auth0 and @auth0/nextjs-auth0 v4. It covers passing an organization at login, reading and validating its ID, accepting invitations, and configuring organizations and connections.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Separating login and access by customer organization
  • Passing an organization ID through a login flow
  • Accepting an organization invitation in a Next.js app
auth0/nextjs-auth02.3천2026년 10월 26일 업데이트

passkeys

무료

Implement passkey signup, sign-in, and enrollment for signed-in users with @auth0/nextjs-auth0 v4. The skill explains how server handlers create challenges and exchange credentials, while the browser performs WebAuthn and serializes the result.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Adding passkey signup to a Next.js application
  • Building passkey sign-in with browser WebAuthn
  • Letting signed-in users enroll another passkey
auth0/nextjs-auth02.3천2026년 10월 26일 업데이트

auth0의 스킬 모두 보기

이 스킬의 문제 신고