skip to Main Content

I have this /app/auth/login/route.ts

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs'
import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'

export async function POST(request: Request) {
  const requestUrl = new URL(request.url)
  const formData = await request.formData()
  const email = String(formData.get('email'))
  const password = String(formData.get('password'))
  const cookieStore = cookies()
  const supabase = createRouteHandlerClient({ cookies: () => cookieStore })

  await supabase.auth.signInWithPassword({
    email,
    password,
  })

  return NextResponse.redirect(requestUrl.origin, {
    status: 301,
  })
}

And the page which is on /app/login/page.tsx

import Link from "next/link";

export default function LoginPage() {
  return (
    <>
      <form action="/auth/login" method="post">
      <label htmlFor="email">Email</label>
      <input name="email" />
      <label htmlFor="password">Password</label>
      <input type="password" name="password" />
      <button>Sign In</button>
    </form>

    <Link href="/reset-password">Reset Password</Link>
    </>
  )
}

Those were the codes that I got from the documentation. I am, however, unable to implement where it displays that they have successfully logged in and the error handling.

I followed answers below, however, I am receiving this error:

POST http://localhost:3000/auth/login net::ERR_ABORTED 405 (Method Not> Allowed)

route.ts

import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
import { NextApiRequest, NextApiResponse } from 'next'; // Correct import for NextApiRequest and NextApiResponse
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server'; // Correct import for NextResponse

export async function handler(
  req: NextApiRequest, // Correct the parameter list
  res: NextApiResponse // Correct the parameter list
) {
  const jsonBody = await req.body.json(); // Change 'request' to 'req'
  const email = jsonBody.email;
  const password = jsonBody.password;
  const cookieStore = cookies();
  const supabase = createRouteHandlerClient({ cookies: () => cookieStore });

  try {
    const { error } = await supabase.auth.signInWithPassword({
      email,
      password,
    });

    if (error) {
      return res.status(401).json({ message: 'Login failed. Please check your credentials.' });
    }
    return res.status(200).json({ message: 'Login successful' });
  } catch (error) {
    console.error('An error occurred:', error);
    return res.status(500).json({ message: 'An unexpected error occurred' });
  }
}

api/login/page.tsx

'use client'

import { useRouter } from 'next/navigation';
import { useState } from 'react';

export default function LoginPage() {
  const router = useRouter();
  const [error, setError] = useState<string>('');
  const [email, setEmail] = useState<string>('');
  const [password, setPassword] = useState<string>('');

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError('');
  
    try {
      const response = await fetch('/auth/login', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ email, password }),
      });
  
      if (response.status === 200) {
        router.push('/');
      } else {
        if (response.headers.get('content-type')?.includes('application/json')) {
          // Check if the response contains JSON data.
          const result = await response.json();
          setError(result.message);
        } else {
          // Handle non-JSON responses (e.g., empty responses).
          setError('An unexpected error occurred.');
        }
      }
    } catch (error) {
      console.log(error, "error on the login page.tsx")
      console.error('An error occurred:', error);
      setError('An unexpected error occurred.');
    }
  }; 

  return (
    <>
      <form onSubmit={handleSubmit}>
        <label htmlFor="email">Email</label>
        <input
          type="email"
          name="email"
          value={email}
          onChange={(e) => setEmail(e.target.value)}
        />
        <label htmlFor="password">Password</label>
        <input
          type="password"
          name="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
        <button type="submit">Sign In</button>
      </form>

      {error && <p>{error}</p>}
    </>
  );
}

2

Answers


  1. I think you should send a POST json request, handling it with javascript:

    import { useRouter } from 'next/router';
    import { useState } from 'react';
    
    export default function LoginPage() {
      const router = useRouter();
      const [error, setError] = useState('');
      const [email, setEmail] = useState('');
      const [password, setPassword] = useState('');
    
      const handleSubmit = async (e) => {
        e.preventDefault();
        setError('');
    
        try {
          const response = await fetch('/auth/login', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ email, password }),
          });
    
          if (response.status === 200) {
            router.push('/some-page-after-login')
          } else {
            const result = await response.json();
            setError(result.message);
          }
        } catch (error) {
          console.error('An error occurred:', error);
          setError('An unexpected error occurred.');
        }
      };
    
      return (
        <>
          <form onSubmit={handleSubmit}>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
            <button type="submit">Sign In</button>
          </form>
    
          {error && <p>{error}</p>}
        </>
      );
    }
    

    And in the api:

    import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
    import { cookies } from 'next/headers';
    import { NextResponse } from 'next/server';
    
    export async function handler(
      req: NextApiRequest,
      res: NextApiResponse<ResponseData>
    ) {
      const jsonBody = await request.json();
      const email = jsonBody.email;
      const password = jsonBody.password;
      const cookieStore = cookies();
      const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
    
      try {
        const { user, error } = await supabase.auth.signInWithPassword({
          email,
          password,
        });
    
        if (error) {
          return res.status(401).json({ message: 'Login failed. Please check your credentials.' })
        }
        return res.status(200).json({ message: 'Login successful' })
      } catch (error) {
        console.error('An error occurred:', error);
        return res.status(500).json({ message: 'An unexpected error occurred' })
      }
    }
    
    
    Login or Signup to reply.
  2. I see you did implement a route handler for the /auth/login endpoint in Next.js, but in your first snippet, you have defined a function named POST. In the second snippet, you have renamed this function to handler. It would be clearer to use the same name for this function across both snippets.
    Since you get a 405, I suppose the server configuration or the route handler not being set up correctly to handle POST requests to this endpoint.

    First, make sure the file name of your route handler matches the URL path you are trying to handle. For example, if your route handler is in a file named route.ts, make sure it is located at pages/api/auth/login/route.ts in your project directory.
    Export your route handler function as the default export from your route handler file.

    // pages/api/auth/login/route.ts
    export default async function handler(
      req: NextApiRequest,
      res: NextApiResponse
    ) {
      // rest of your code
    }
    

    Also, make sure your route handler is correctly handling POST requests. You can use the req.method property to check the HTTP method of the incoming request and respond accordingly.

    if (req.method === 'POST') {
      // Handle POST requests
      // rest of your code for handling POST requests
    } else {
      // Respond with a 405 Method Not Allowed if the request is not a POST request
      res.status(405).send('Method Not Allowed');
    }
    

    I would assume you are using NextJS 13.4+

    Based on those suggestions, the code would be:

    Route Handler (route.ts):

    // pages/api/auth/login/route.ts
    
    import { createRouteHandlerClient } from '@supabase/auth-helpers-nextjs';
    import { cookies } from 'next/headers';
    import { NextResponse } from 'next/server';
    
    export async function POST(request: Request) {
      // Parse JSON body from the request
      const jsonBody = await request.json();
      const { email, password } = jsonBody;
    
      const cookieStore = cookies();
      const supabase = createRouteHandlerClient({ cookies: () => cookieStore });
    
      try {
        const { error } = await supabase.auth.signInWithPassword({
          email,
          password,
        });
    
        if (error) {
          // Return a JSON response with a 401 status code for unauthorized
          return NextResponse.json({ message: 'Login failed. Please check your credentials.' }, { status: 401 });
        }
        // Return a JSON response indicating success
        return NextResponse.json({ message: 'Login successful' });
      } catch (error) {
        console.error('An error occurred:', error);
        // Return a JSON response with a 500 status code for server error
        return NextResponse.json({ message: 'An unexpected error occurred' }, { status: 500 });
      }
    }
    

    Login Page (page.tsx):

    // pages/app/login/page.tsx
    
    import { useRouter } from 'next/router';
    import { useState } from 'react';
    
    export default function LoginPage() {
      const router = useRouter();
      const [error, setError] = useState<string>('');
      const [email, setEmail] = useState<string>('');
      const [password, setPassword] = useState<string>('');
    
      const handleSubmit = async (e: React.FormEvent) => {
        e.preventDefault();
        setError('');
    
        try {
          const response = await fetch('/api/auth/login/route', {  // Corrected the URL to match the route handler file location
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({ email, password }),
          });
    
          if (response.status === 200) {
            router.push('/');
          } else {
            if (response.headers.get('content-type')?.includes('application/json')) {
              const result = await response.json();
              setError(result.message);
            } else {
              setError('An unexpected error occurred.');
            }
          }
        } catch (error) {
          console.error('An error occurred:', error);
          setError('An unexpected error occurred.');
        }
      }; 
    
      return (
        <>
          <form onSubmit={handleSubmit}>
            <label htmlFor="email">Email</label>
            <input
              type="email"
              name="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
            />
            <label htmlFor="password">Password</label>
            <input
              type="password"
              name="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
            />
            <button type="submit">Sign In</button>
          </form>
    
          {error && <p>{error}</p>}
        </>
      );
    }
    

    In the route handler (route.ts), I have added a check to make sure the request method is POST using if (req.method !== 'POST').
    In the login page (page.tsx), I fixed the URL in the fetch function to match the location of the route handler file (/api/auth/login/route).

    The all process:

     +-----------------------+   User Input   +------------------------+
     |                       | -------------> |                        |
     | Client:               |   (email,      | Server:                |
     | pages/app/login/      |   password)    | pages/api/auth/login/  |
     | page.tsx              |                | route.ts               |
     |                       |                |                        |
     | 1. Render Login Page  |                |                        |
     |                       |                |                        |
     | 2. User submits       |                |                        |
     |    form               |                |                        |
     |                       |                |                        |
     | 3. handleSubmit       |   POST Req.    | 4. POST function       |
     |    triggers fetch     | -------------> |    executes:           |
     |    request to server  |                |                        |
     |                       |                |    - Parse request     |
     |                       |                |    - Authenticate      |
     |                       |                |      via Supabase      |
     |                       |                |    - Prepare response  |
     |                       |                |                        |
     +-----------------------+   Response    +------------------------+
                              <-------------   
        (success/error msg)  |                | (success/error msg)
                             |                |
     +-----------------------+                |
     |                       |                |
     | Client:               |                |
     | pages/app/login/      |                |
     | page.tsx              |                |
     |                       |                |
     | 5. Process response:  |                |
     |    - Success: Redirect|                |
     |      to homepage      |                |
     |    - Failure: Display |                |
     |      error message    |                |
     |                       |                |
     +-----------------------+                |
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search