Unter der Haube von NextAuth.js: Ein skalierbares Authentifizierungssystem
Emily Parker
Product Engineer · Leapcell

Quellcodeanalyse von Next-Auth: Eine leistungsstarke und flexible Authentifizierungslösung
Einführung
Next-Auth ist eine leistungsstarke und flexible Authentifizierungsbibliothek, die bequeme Authentifizierungsfunktionen für Next.js-Anwendungen und andere React-Projekte bietet. Sie unterstützt mehrere Authentifizierungsmethoden und verfügt über eine vernünftige Aufteilung der Quellcodestruktur, was es Entwicklern erleichtert, sie zu verstehen und zu erweitern. Dieser Artikel analysiert eingehend die Quellcodestruktur und die wichtigsten Funktionen von Next-Auth.
Einführung in das Verzeichnis
Der Kernquellcode von Next-Auth befindet sich im Verzeichnis packages/next-auth/src
. Im Folgenden ist die Hauptstruktur dieses Verzeichnisses aufgeführt:
client
: Es kapselt hauptsächlich diefetch
-Methode und implementiert einen Broadcast-Event-Mechanismus, um auf Änderungen inlocalStorage
zu warten.core
: Es enthält die Hauptgeschäftslogik, und hier werden die APIs und Seiten von/api/auth/xxx
definiert.jwt
: Es bietet Verschlüsselungs- und Entschlüsselungsmethoden für JWT (JSON Web Token) und wird verwendet, um Token-bezogene Operationen bei der Authentifizierung zu verarbeiten.next
: Es definiert die Middleware in Next.js und bietet spezifische Unterstützung für Next.js-Anwendungen.providers
: Es bietet die Standardkonfigurationen verschiedener Authentifizierungsmethoden, wodurch es für Entwickler bequem ist, verschiedene Authentifizierungsanbieter zu integrieren.react
: Es bietet Front-End-Methoden wieuseSession
undgetToken
für React-Anwendungen, die verwendet werden, um Benutzer-Session-Informationen abzurufen und zu aktualisieren.utils
: Es definiert einige Hilfsmethoden, wie z. B. das Parsen von Routen und das Zusammenführen von Daten, um bei der Bearbeitung einiger häufiger Aufgaben zu helfen.
Darüber hinaus gibt es die Dateien index.ts
und middleware.ts
, die eine wichtige Rolle bei der Initialisierung der gesamten Bibliothek und der Middleware-Verarbeitung spielen.
Analyse des Client-Verzeichnisses
Das client
-Verzeichnis bietet hauptsächlich zwei Schlüsselfunktionen:
Fetch
-Kapselung: Es kapselt diefetch
-Methode für Netzwerkanfragen. Alle Operationen, die Netzwerkanfragen beinhalten, rufen diese gekapselte Methode auf, was für die einheitliche Verwaltung und Verarbeitung von Netzwerk-Request-bezogener Logik praktisch ist.- Broadcast-Event-Mechanismus: Er realisiert die Kommunikation zwischen verschiedenen Tabs oder Fenstern, indem er auf Änderungen in
localStorage
hört. Der Code lautet wie folgt:
export function BroadcastChannel(name = "nextauth.message") { return { /** Get notifications from other tabs/windows */ receive(onReceive: (message: BroadcastMessage) => void) { const handler = (event: StorageEvent) => { if (event.key!== name) return const message: BroadcastMessage = JSON.parse(event.newValue?? "{}") if (message?.event!== "session" ||!message?.data) return onReceive(message) } window.addEventListener("storage", handler) return () => window.removeEventListener("storage", handler) }, /** Notify other tabs/windows */ post(message: Record<string, unknown>) { if (typeof window === "undefined") return try { localStorage.setItem( name, JSON.stringify({...message, timestamp: now() }) ) } catch { /** * The localStorage API is not always available. For example, it does not work in private mode before Safari 11. * If an error occurs, the notification will be simply discarded. */ } }, } } export interface BroadcastMessage { event?: "session" data?: { trigger?: "signout" | "getSession" } clientId: string timestamp: number }
Derzeit ist der Haupt-Listener dieses Broadcast-Events der SessionProvider
in React. Wenn eine Änderung in localStorage
festgestellt wird, löst dies die in SessionProvider
definierte Methode __NEXTAUTH._getSession()
aus. Diese Methode wird verwendet, um die /api/auth/session
-API anzufordern, um das Benutzer-Session-Objekt abzurufen. Die Methode __NEXTAUTH._getSession()
hat die folgenden Aufrufmethoden:
__NEXTAUTH._getSession()
: Sie wird beim ersten Aufruf verwendet, um das Abrufen von Sitzungsinformationen zu initialisieren.__NEXTAUTH._getSession({ event: "storage" })
: Sie wird aufgerufen, wenn andere Tabs oder Fenster Nachrichten senden, um die Sitzung zu aktualisieren, um zirkuläre Aktualisierungen zu vermeiden.__NEXTAUTH._getSession({ event: "visibilitychange" })
: Sie wird ausgelöst, wenn der Tab aktiviert wird, um zu überprüfen, ob die Sitzung aktualisiert werden muss.__NEXTAUTH._getSession({ event: "poll" })
: Sie wird verwendet, um die Sitzung abzufragen und zu aktualisieren, um die Echtzeitnatur der Sitzungsinformationen sicherzustellen.
Im Folgenden finden Sie die spezifische Implementierung der Methode __NEXTAUTH._getSession()
:
React.useEffect(() => { __NEXTAUTH._getSession = async ({ event } = {}) => { try { const storageEvent = event === "storage" // If there is no client session yet, or there is an event from other tabs/windows, we should always update if (storageEvent || __NEXTAUTH._session === undefined) { __NEXTAUTH._lastSync = now() __NEXTAUTH._session = await getSession({ broadcast:!storageEvent, }) setSession(__NEXTAUTH._session) return } if ( // If the session expiration time is not defined, it is okay to use the existing value before the event triggers an update !event || // If there is no session on the client, we don't need to call the server to check (if logged in through other tabs/windows, it will come in the form of a "storage" event) __NEXTAUTH._session === null || // If the client session has not expired yet, exit early now() < __NEXTAUTH._lastSync ) { return } // An event has occurred or the session has expired, update the client session __NEXTAUTH._lastSync = now() __NEXTAUTH._session = await getSession() setSession(__NEXTAUTH._session) } catch (error) { logger.error("CLIENT_SESSION_ERROR", error as Error) } finally { setLoading(false) } } __NEXTAUTH._getSession() return () => { __NEXTAUTH._lastSync = 0 __NEXTAUTH._session = undefined __NEXTAUTH._getSession = () => {} } }, [])
Analyse des React-Verzeichnisses
Das react
-Verzeichnis bietet eine Reihe praktischer Methoden und Komponenten für Front-End-React-Projekte:
SessionProvider
-Komponente: Sie wird normalerweise um die äußerste Schicht der gesamten Anwendung gewickelt, um ein Session-Objekt für die Anwendung bereitzustellen und sicherzustellen, dass auf Session-Informationen in der gesamten Anwendung zugegriffen werden kann.useSession()
-Hook-Funktion: Sie wird verwendet, um das vonSessionProvider
bereitgestellte Session-Objekt zu konsumieren. Die Typdefinition des Session-Objekts lautet wie folgt:
export type SessionContextValue<R extends boolean = false> = R extends true ? | { update: UpdateSession; data: Session; status: "authenticated" } | { update: UpdateSession; data: null; status: "loading" } : | { update: UpdateSession; data: Session; status: "authenticated" } | { update: UpdateSession data: null status: "unauthenticated" | "loading" }
signIn()
-Funktion: Sie löst den Anmeldevorgang aus. Wenn es sich um eine OAuth-Anmeldung handelt, sendet sie eine POST-Anfrage an/auth/signin/{provider}
zur Authentifizierung.signOut()
-Funktion: Sie greift auf den/auth/signout
-Endpunkt zu und sendet außerdem ein Event, um andere Browser-Tabs zu benachrichtigen, um die Funktion des gleichzeitigen Abmeldens zu erreichen.getSession()
-Funktion: Sie wird verwendet, um das Benutzer-Session-Objekt abzurufen, wodurch es bequem ist, die Authentifizierungsinformationen des Benutzers in der Front-End-Anwendung abzurufen.getCsrfToken()
-Funktion: Sie ruft den Anti-Cross-Site-Request-Forgery-Token (XSS) ab, der insignIn
,signOut
undSessionProvider
zum Request-Body hinzugefügt werden muss, um die Sicherheit zu erhöhen.
Analyse des Next-Verzeichnisses
Das next
-Paket wurde speziell für Next.js-Anwendungen entwickelt und enthält die Entry-NextAuth()
-Methode des gesamten Pakets. Wir konzentrieren uns auf den Code des NextAuthApiHandler()
-Zweigs:
- Request-Konvertierung: Es konvertiert die Requests von Next.js in die interne Request-Datenstruktur, parst hauptsächlich Informationen wie
action
,cookie
undhttpmethod
und wird mit dertoInternalRequest()
-Methode implementiert. - Initialisierungsvorgänge: Es ruft die
init()
-Methode auf, die die Initialisierung von Optionen, die Behandlung von CSRF-Token (Erstellung oder Validierung) und die Behandlung von Callback-URLs (Lesen aus Query-Parametern oder Cookies) umfasst. Bei der Behandlung von Callback-URLs ruft es diecallbacks.redirect()
-Methode auf, um eine entsprechende Verarbeitung gemäß verschiedenen Szenarien durchzuführen (erster Eintrag oder Callback-Rückgabe). - SessionStore-Konstruktion: Es konstruiert das
SessionStore
-Objekt, das zum Verwalten desSessionToken
-Cookies verwendet wird. Da die Größe des Cookies zu groß sein kann, wird es zur Speicherung in mehrere Cookies unterteilt, z. B.xx.0
,xx.1
,xx.2
usw. - Request-Verarbeitungszweige: Je nach
httpmethod
wird es in zwei Zweige unterteilt,get
undpost
:get
-Request: Es definiert statische Seiten wiesignIn
,signOut
,error
undveryrequest
. Wenn es benutzerdefinierte Seiten gibt, wird es zu den benutzerdefinierten Seiten weitergeleitet. Darüber hinaus gibt es Schnittstellen wieproviders
,session
,csrf
undcallback
, die verwendet werden, um der Front-End-JavaScript Session- und Token-Informationen bereitzustellen oder Token und Cookies zu aktualisieren.post
-Request: Zuerst validiert es das CSRF-Token, indem es das Token im Request-Body mit dem Token im Cookie vergleicht, um Cross-Site-Angriffe zu verhindern. FürsignIn
- undsignOut
-Operationen bereitet es Cookies vor und springt zur OAuth-Site;callback
wird verwendet, um den Callback zu verarbeiten, nachdem die OAuth-Authentifizierung erfolgreich war; dieSession
-Schnittstelle wird vom Front-End-JavaScript verwendet, um das Session-Objekt abzurufen oder zu aktualisieren.
Analyse des Jwt-Verzeichnisses
Der Code zur Cookie-Verschlüsselung in Next-Auth befindet sich im Verzeichnis jwt
. Der Verschlüsselungsschlüssel stammt aus der Umgebungsvariable process.env.NEXTAUTH_SECRET
, und die folgende Methode wird zur Verschlüsselung verwendet (das Salt ist eine leere Zeichenfolge):
import hkdf from "@panva/hkdf" async function getDerivedEncryptionKey( keyMaterial: string | Buffer, salt: string ) { return await hkdf( "sha256", keyMaterial, salt, `NextAuth.js Generated Encryption Key${salt? ` (${salt})` : ""}`, 32 ) }
Der verschlüsselte Schlüssel wird verwendet, um das Token zu verschlüsseln und zu signieren, um ein Cookie zu generieren. Die spezifischen Codierungs- und Decodierungsmethoden sind wie folgt:
import { EncryptJWT, jwtDecrypt } from "jose"; export async function encode(params: JWTEncodeParams) { /** @note An empty `salt` indicates a session token. See {@link JWTEncodeParams.salt}. */ const { token = {}, secret, maxAge = DEFAULT_MAX_AGE, salt = "" } = params const encryptionSecret = await getDerivedEncryptionKey(secret, salt) return await new EncryptJWT(token) .setProtectedHeader({ alg: "dir", enc: "A256GCM" }) .setIssuedAt() .setExpirationTime(now() + maxAge) .setJti(uuid()) .encrypt(encryptionSecret) } /** Decode the JWT issued by Next-Auth.js. */ export async function decode(params: JWTDecodeParams): Promise<JWT | null> { /** @note An empty `salt` indicates a session token. See {@link JWTDecodeParams.salt}. */ const { token, secret, salt = "" } = params if (!token) return null const encryptionSecret = await getDerivedEncryptionKey(secret, salt) const { payload } = await jwtDecrypt(token, encryptionSecret, { clockTolerance: 15, }) return payload }
Schlussfolgerung
Next-Auth bietet leistungsstarke und flexible Authentifizierungsfunktionen durch eine vernünftige Aufteilung der Quellcodestruktur. Ob es sich um die Kapselung von Netzwerkanfragen, die Sitzungsverwaltung, die Unterstützung mehrerer Authentifizierungsmethoden oder die Berücksichtigung der Sicherheit (wie CSRF-Schutz und JWT-Verschlüsselung) handelt, es spiegelt die Exzellenz seines Designs wider. Entwickler können den Quellcode von Next-Auth tiefgreifend verstehen und erweitern, um die Authentifizierungsanforderungen verschiedener Projekte zu erfüllen.
Leapcell: Das Beste vom Serverlosen Webhosting
Zum Schluss möchte ich eine Plattform empfehlen, die sich am besten für die Bereitstellung von Diensten eignet: Leapcell
🚀 Bauen Sie mit Ihrer Lieblingssprache
Entwickeln Sie mühelos in JavaScript, Python, Go oder Rust.
🌍 Stellen Sie unbegrenzt Projekte kostenlos bereit
Zahlen Sie nur für das, was Sie nutzen – keine Anfragen, keine Gebühren.
⚡ Pay-as-You-Go, keine versteckten Kosten
Keine Leerlaufgebühren, nur nahtlose Skalierbarkeit.
📖 Entdecken Sie unsere Dokumentation
🔹 Folgen Sie uns auf Twitter: @LeapcellHQ