인프런 영문 브랜드 로고
인프런 영문 브랜드 로고
BEST
Programming

/

Front-end

Next + Create a SNS service with React Query

We will create an SNS service similar to Twitter (X.com) using the React19 & Next15 & ReactQuery5 & Next Auth5 & MSW2 & socket.io4 & zustand stack. Finally, we will add SSR for search engine optimization!

(4.5) 88 reviews

3,154 students

Next.js
React
react-query
next-auth
msw
Thumbnail
zerocho님의 프로필 이미지

Edited

Receive next-auth server error & insert custom data into session & access pages according to permissions

Hello. This is Zerocho.

I think many of you have suffered from next-auth . It is still in beta, so there were many unstable aspects, but it seems to be getting better now. So I will introduce three things that were not covered in the lecture because they were not functional yet, but have been added.

Receive server error on front when signin

When you log in, the server can throw various errors. For example, 1. If the user does not exist, 2. If the password is incorrect, 3. Etc. But shouldn't you pass these to the front so that it can display a message appropriate to the situation ? But this basic function was not there until now and has been added only now.

Modify auth.ts as follows:

 import NextAuth, {CredentialsSignin} from "next-auth" ... providers: [ CredentialsProvider({ async authorize(credentials) { const authResponse = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/login`, { ... }) // 여기 주목!!! 서버에서 에러가 발생할 때 그 에러 내용이 서버에 담겨 있을 겁니다. console.log(authResponse.status, authResponse.statusText) if (!authResponse.ok) { const credentialsSignin = new CredentialsSignin(); if (authResponse.status === 404) { credentialsSignin.code = 'no_user'; } else if (authResponse.status === 401) { credentialsSignin.code = 'wrong_password'; } throw credentialsSignin; } const user = await authResponse.json() console.log('user', user); // id, name, image, email만 허용 return { id: user.id, name: user.nickname, image: user.image, } }, }), ]

Now, instead of returning null, you can throw a CredentialsSignin error . You can write an error message in the code property of the error.

 const response = await signIn("credentials", { username: id, password, redirect: false })

Now, when login fails, the response will contain an error code and a message. However, response.ok will still be true (if redirect is false, it will always be true).

Inserting custom data into a session object

According to the official documentation, only id, email, name, and image can be entered in the return of the current authorize function. This alone is a big limitation, and although it says that id can be entered, if entered, it disappears from the data of useSession().

  1. Then where did the id go?

  2. How do I insert other custom data?

    You can do this by creating a session directly like this:

auth.ts

 export const { handlers: { GET, POST }, auth, signIn, } = NextAuth({ pages: { signIn: '/i/flow/login', newUser: '/i/flow/signup', }, callbacks: { async session({ session, token }) { console.log('session callback', session, token); const authResponse = await fetch(내정보를 가져오는 서버 API); const userData = await authResponse.json(); (session as any).userData = userData; return session; } }, providers: [ CredentialsProvider({ async authorize(credentials) { ... // id, name, image, email만 허용 return { id: user.id, name: user.nickname, image: user.image, }

You can write an async session method in the callbacks property like this. Inside here, you can retrieve my information from the server once more. Then, you can put the response value in the session object and return it.

  1. The id that was lost earlier is in token.sub of this method.

  2. The value returned here becomes the data for auth() or useSession() . The value returned from authorize is not the final value, but is modified once more here.

This way, you can check user.userData in auth() or useSession().

Access pages based on permissions

Now that we can insert custom data into the session object, we can access pages based on permissions. In the callbacks.session async function, we can receive something like a role from the server and insert it. Let's say that session.userData.role has admin or normal permissions. And if the role is admin, we can access the admin page (/admin) , but if it's normal, we can't. How can we implement this?

Currently, middleware.ts is as follows. This means that it only runs on the routes written in the config, so remove the entire config.

 import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (!session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } } // See "Matching Paths" below to learn more export const config = { matcher: ['/compose/tweet', '/home', '/explore', '/messages', '/search'], }

And you can write it manually inside the middleware function. request.nextUrl.pathname contains the pathname you are currently trying to access.

 import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (['/compose/tweet', '/home', '/explore', '/messages', '/search'].includes(request.nextUrl.pathname) && !session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } }

After modifying it like this, we can access the authority as session.userData.role in the session of auth(), so we can check it and redirect like this:

 import { auth } from "./auth" import {NextResponse} from "next/server"; export async function middleware() { const session = await auth(); if (['/compose/tweet', '/home', '/explore', '/messages', '/search'].includes(request.nextUrl.pathname) && !session) { return NextResponse.redirect('http://localhost:3000/i/flow/login'); } if (request.nextUrl.pathname.startsWith('/admin') && session?.userData.role !== 'admin') { return NextResponse.redirect('http://localhost:3000/권한없음_알리는_모달_주소'); } }

You can somehow utilize next-auth, which is lacking in this way.

I'll let you know if there's any further updates. Thanks!

Comment