Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Avendaosander/Plataforma-social/llms.txt

Use this file to discover all available pages before exploring further.

Plataforma Social uses NextAuth.js with a CredentialsProvider to authenticate users against the GraphQL API. There is no OAuth — every account is created with an email and password, which is hashed server-side before being stored. The resulting session is a JWT that carries the user’s id and username, making both available in every protected route and component.

Registration Flow

New users register at /register, which renders the FormRegister component. The form collects four fields — username, email, password, and confirmPassword — and calls the POST_USER GraphQL mutation on submission.
1

Fill out the form

The FormRegister component manages a local state object with username, email, and password, plus a separate confirmPassword value. Client-side validation checks that no field is empty and that both password fields match before the mutation fires.
// frontend/app/components/register/FormRegister.tsx
const [register, setRegister] = useState({
  username: "",
  email: "",
  password: ""
})
const [confirmPassword, setConfirmPassword] = useState('')

const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault()

  if (register.email === '') {
    return toastCustom({ text: 'Debe ingresar un correo electronico', variant: "error" })
  }
  if (register.password === '') {
    return toastCustom({ text: 'Debe ingresar una contraseña', variant: "error" })
  }
  if (register.password !== confirmPassword) {
    return toastCustom({ text: 'Las contraseñas no coinciden', variant: "error" })
  }

  postUser({ variables: register })
}
2

Execute the POST_USER mutation

FormRegister calls the POST_USER Apollo mutation with the three variables. The Express + Apollo Server backend receives the mutation, hashes the password with bcrypt (10 rounds), and inserts a new User row. A Setting record is also created automatically for the new user at this step.
# frontend/app/lib/graphql/users.ts
mutation PostUser($username: String!, $email: String!, $password: String!) {
  postUser(username: $username, email: $email, password: $password) {
    id
    username
    email
    description
    avatar
  }
}
3

Redirect to login

On a successful mutation response (data?.postUser is truthy), the router navigates the user to /login:
// frontend/app/components/register/FormRegister.tsx
if (data?.postUser) {
  router.push('/login')
}

Login Flow

Registered users sign in at /login using the FormLogin component. The component calls NextAuth’s signIn function with the 'credentials' provider.
1

Submit credentials

FormLogin holds email and password in local state. On form submission it calls signIn('credentials', ...) with redirect: false so any error can be caught and shown as a toast.
// frontend/app/components/login/FormLogin.tsx
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
  e.preventDefault()

  if (login.email === '') {
    return toastCustom({ text: 'Debe ingresar un correo electronico', variant: "error" })
  }
  if (login.password === '') {
    return toastCustom({ text: 'Debe ingresar una contraseña', variant: "error" })
  }

  const res = await signIn('credentials', {
    email: login.email,
    password: login.password,
    redirect: false
  })

  if (res?.error) {
    setError(res.error)
    toastCustom({ text: res.error, variant: "error" })
  }
}
2

NextAuth authorize callback

The NextAuth handler at app/api/auth/[...nextauth]/route.ts receives the credentials. Its authorize function sends a LOGIN GraphQL query to the API to look up the user by email, then verifies the submitted password against the stored hash using bcrypt.compare.
// frontend/app/api/auth/[...nextauth]/route.ts
async authorize(credentials, req) {
  if (!credentials) {
    throw new Error('Credenciales incompletas');
  }

  try {
    const body = JSON.stringify({
      query: print(LOGIN),
      variables: { email: credentials.email },
    });

    const response = await fetch(`${process.env.API_ROUTE}`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body,
    });

    const { data, errors } = await response.json();

    if (errors) {
      const formattedErrors = formatGraphQLErrors(errors);
      throw new Error(`${formattedErrors}`);
    }

    const userFound = data.login;
    const matchPassword = await bcrypt.compare(credentials.password, userFound.password)

    if (!matchPassword) throw new Error('Contraseña incorrecta')

    return userFound
  } catch (error: any) {
    throw new Error(error.message);
  }
}
The LOGIN query used here is:
# frontend/app/lib/graphql/users.ts
query Login($email: String!) {
  login(email: $email) {
    id
    username
    email
    password
    description
    avatar
  }
}
3

JWT and session callbacks

After successful authorization, NextAuth runs the jwt callback to embed id and username into the token, then the session callback to expose them on session.user.
// frontend/app/api/auth/[...nextauth]/route.ts
callbacks: {
  async jwt({ token, user, session }) {
    if (user) {
      return {
        ...token,
        id: user.id,
        username: user.username,
      }
    }
    return token
  },
  async session({ session, token, user }) {
    return {
      ...session,
      user: {
        ...session.user,
        id: token.id,
        username: token.username
      }
    }
  },
},

NextAuth Configuration Summary

The full authOptions object exported from the route handler configures the provider, callbacks, custom pages, and secret:
OptionValue
ProviderCredentialsProvideremail + password fields
JWT callbackAdds id and username to the token
Session callbackExtends session.user with id and username
Custom sign-in page/login
Secretprocess.env.NEXTAUTH_SECRET
// frontend/app/api/auth/[...nextauth]/route.ts
export const authOptions: NextAuthOptions = {
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: 'Username', type: 'text', placeholder: 'Username' },
        password: { label: 'Password', type: 'password', placeholder: 'Password' },
      },
      async authorize(credentials, req) { /* ... */ },
    })
  ],
  callbacks: { jwt: async () => { /* ... */ }, session: async () => { /* ... */ } },
  pages: {
    signIn: '/login',
  },
  secret: process.env.NEXTAUTH_SECRET,
}

const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

Route Protection

All routes under /home are protected by Next.js middleware. Unauthenticated visitors who attempt to access any /home/:path* URL are automatically redirected to /login by the NextAuth middleware export.
// frontend/middleware.ts
export { default } from 'next-auth/middleware'

export const config = {
  matcher: ["/home/:path*"]
}
The middleware re-exports NextAuth’s default middleware unchanged. No custom logic is needed — NextAuth checks for a valid session token and redirects to the configured signIn page (/login) when none is found.

Accessing the Session in Components

Use the useSession() hook from next-auth/react inside any Client Component to read the current user’s session data. The session object is extended by the session callback to include id and username:
import { useSession } from "next-auth/react"

// Inside a component:
const { data: session, status } = useSession()

// session.user.id       — UUID from the User table
// session.user.username — display username
// session.user.email    — email address (standard NextAuth field)
The my-profile page uses this pattern to fetch the current user’s data from the GraphQL API:
// frontend/app/home/my-profile/page.tsx
const { data: session, status } = useSession()
const { loading, error, data } = useQuery(GET_USER, {
  variables: {
    id: session?.user.id
  }
})

Logout

Signing out is handled in the Navbar component. It calls signOut with redirect: false to receive the callback URL, then performs a client-side navigation to /login:
// frontend/app/components/ui/Navbar.tsx
const logout = async () => {
  const data = await signOut({ redirect: false, callbackUrl: '/login' })
  router.push(data.url)
}
Also, if a user is already authenticated and navigates to /login, FormLogin redirects them back to /home automatically:
// frontend/app/components/login/FormLogin.tsx
const { status } = useSession()

if (status === "authenticated") {
  redirect('/home')
}
Passwords are hashed server-side using bcrypt with 10 salt rounds before being stored in the database. Never store or log plaintext passwords. The LOGIN query intentionally returns the hashed password field only so that NextAuth can call bcrypt.compare — this field is never exposed to the frontend session or client-side code.

Build docs developers (and LLMs) love