简体   繁体   中英

message Unauthenticated laravel 9 sanctum logout with error post (Unauthorized) 401

I follow tutorial authenticated Laravel app ver 9 with sanctum but i got message authenticate on method logout "AuthController"


App\Http\Controllers\Api\AuthController


<?php

namespace App\Http\Controllers\Api;

use App\Models\User;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Hash;

class AuthController extends Controller
{
    //
    public function login(Request $request){
        $request->validate([
            'email'=>['required','email'],
            'password'=>['required']
        ]);
        $user = User::where('email',$request->email)->first();
        Auth::guard('web')->login($user);
        if(!$user || !Hash::check($request->password, $user->password)){
            return response(['message'=>'gk jelas']);
        } else {

            $token = $user->createToken('bisa_js-token')->plainTextToken;
            $response = [
                'user'=>$user,
                'token'=>$token
            ];
            return response($response);
        }
    }
    public function logout(Request $request){
        Auth::attempt([
            'email'=>$request->email,
            'password'=>$request->password
        ]);
        Auth::user()->token()->delete();
        return response(['message'=>'Successfully Logging out']);
    }
}

So, i have written function logout in controller but this method giveback response message authenticate.

Configure Sanctum

app\config\sanctum.php

<?php

use Laravel\Sanctum\Sanctum;

return [

    /*
    |--------------------------------------------------------------------------
    | Stateful Domains
    |--------------------------------------------------------------------------
    |
    | Requests from the following domains / hosts will receive stateful API
    | authentication cookies. Typically, these should include your local
    | and production domains which access your API via a frontend SPA.
    |
    */

    'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', sprintf(
        '%s%s',
        'localhost,localhost:3000,127.0.0.1,127.0.0.1:8000,::1',
        Sanctum::currentApplicationUrlWithPort()
    ))),

    /*
    |--------------------------------------------------------------------------
    | Sanctum Guards
    |--------------------------------------------------------------------------
    |
    | This array contains the authentication guards that will be checked when
    | Sanctum is trying to authenticate a request. If none of these guards
    | are able to authenticate the request, Sanctum will use the bearer
    | token that's present on an incoming request for authentication.
    |
    */

    'guard' => ['web'],

    /*
    |--------------------------------------------------------------------------
    | Expiration Minutes
    |--------------------------------------------------------------------------
    |
    | This value controls the number of minutes until an issued token will be
    | considered expired. If this value is null, personal access tokens do
    | not expire. This won't tweak the lifetime of first-party sessions.
    |
    */

    'expiration' => null,

    /*
    |--------------------------------------------------------------------------
    | Sanctum Middleware
    |--------------------------------------------------------------------------
    |
    | When authenticating your first-party SPA with Sanctum you may need to
    | customize some of the middleware Sanctum uses while processing the
    | request. You may change the middleware listed below as required.
    |
    */

    'middleware' => [
        'verify_csrf_token' => App\Http\Middleware\VerifyCsrfToken::class,
        'encrypt_cookies' => App\Http\Middleware\EncryptCookies::class,
    ],

];    

i also configured file cors.php

app\config\cors.php

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['/api/*', 'sanctum/csrf-cookie', '/login', '/logout'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['http://backend.bisa_js.test','http://localhost:3000'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => true,

];

In file Kernel.php, i have uncomment first before writing file api.php

App\Http\Kernel.php

protected $middlewareGroups = [
        'web' => [
            \App\Http\Middleware\EncryptCookies::class,
            \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
            \Illuminate\Session\Middleware\StartSession::class,
            \Illuminate\View\Middleware\ShareErrorsFromSession::class,
            \App\Http\Middleware\VerifyCsrfToken::class,
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],

        'api' => [
            \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
            'throttle:api',
            \Illuminate\Routing\Middleware\SubstituteBindings::class,
        ],
    ];

after writing Kernel, i also write file api.php in folder routes

app\routes\api.php

<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\Api\AuthController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::post('/login',[AuthController::class, 'login']);
Route::middleware('auth:sanctum')->post('/logout',[AuthController::class, 'logout']);
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
    return $request->user();
});

I built a spa application with laravel and react, after successfully logging in to the account in the cookie I can't log out in the logout controller method even though previously get /user in api was successfully sent with a bearer token

this my files are login and dropdown to authenticate login and logout.

Login.js

import React, {useState, useRef, useEffect} from "react"
import axios from "axios"
import {useCookies} from 'react-cookie'
import {useNavigate} from 'react-router-dom'
const Login = () => {
    const navigate = useNavigate()
    const [email, setEmail] = useState('')
    const errorEmailRef = useRef(null)
    const [errorEmail, setErrorEmail] = useState([])
    const [password, setPassword] = useState('')
    const errorPasswordRef = useRef(null)
    const [errorPassword, setErrorPassword] = useState([])
    const [errorMessage, setErrorMessage] = useState('')
    const errorMessageRef = useRef(null)
    const [cookie, setCookie, removeCookie] = useCookies(['login'])
    useEffect(()=>{
        if(cookie.token !== undefined && cookie.user !== undefined){
            document.body.className = ''
            navigate('/',{replace: true})
        }
    },[])
    const submitForm = (e) => {
        e.preventDefault()
        // axios.get('http://backend.bisa_js.test/sanctum/csrf-cookie').then((response)=>{
        //     console.log(response)
        // })
        axios.post('http://backend.bisa_js.test/api/login', {email, password, 
        headers: {
            'Accept':'application/json'
        }})
        .then((response)=>{
            console.log(response.data)
            setCookie('token',response.data.token, {path:'/'})
            setCookie('user',response.data.user, {path:'/'})
            navigate('/')
        })
        .catch((reject)=>{
            if(reject.response.data.errors !== undefined){
                const errors = reject.response.data.errors
                errors.email !== undefined ? setErrorEmail(errors.email) : setErrorEmail([])
                errors.password !== undefined ? setErrorPassword(errors.password) : setErrorPassword([])
                errorEmailRef.current.className = 'block'
                errorPasswordRef.current.className = 'block'
                setErrorMessage(reject.response.data.message)
            } else {
                if(errorMessageRef.current.classList.contains('hidden')){
                    errorMessageRef.current.classList.toggle('hidden')
                    errorMessageRef.current.classList.toggle('block')
                }
                setErrorMessage(reject.response.data.message)
            }
            console.log(reject.response)
        })
    }
    return (
        <div className="w-full h-screen bg-slate-800 flex flex-col justify-center">
            <div className="mb-3 p-2 w-[300px] mx-auto bg-red-800 rounded-lg shadow shadow-white text-white hidden" ref={errorMessageRef}>{errorMessage}</div>
            <div className="w-[300px] mx-auto p-5 h-max rounded bg-slate-900 text-white shadow shadow-white">
                
                <form className="bisa-js__form-login" onSubmit={submitForm}>
                    <div className="bisa-js__group-form mb-5">
                        <label className="block mb-3">Email</label>
                        <input type='email' value={email} onChange={(event)=>{
                            setEmail(event.target.value)
                        }} placeholder="type here your email..." 
                        className="block p-2 text-base rounded outline-none mb-2 bg-slate-900 shadow shadow-white text-white w-full"/>
                        <div ref={errorEmailRef} className="hidden">
                            {errorEmail.map((val, ind)=>(
                                <p className="text-red-500 font-medium" key={ind}>{val}</p>
                            ))}
                        </div>
                    </div>
                    <div className="bisa-js__group-form mb-5">
                        <label className="block mb-3">Password</label>
                        <input type='password' value={password} onChange={(event)=>{
                            setPassword(event.target.value)
                        }} placeholder="type here your password..."
                        className="block p-2 text-base rounded outline-none mb-2 bg-slate-900 shadow shadow-white text-white w-full"/>
                        <div ref={errorPasswordRef} className="hidden">
                        {errorPassword.map((val, ind)=>(
                                <p className="text-red-500 font-medium" key={ind}>{val}</p>
                            ))}
                        </div>
                    </div>
                    <button type="submit" className="p-2 rounded shadow shadow-white">Login</button>
                </form>
            </div>
        </div>
    )
}
export default Login

Dropdown.js

import React, { useRef, useState } from 'react'
import { useCookies } from 'react-cookie'
import axios from 'axios'
import {useNavigate} from 'react-router-dom'
const Dropdown = () => {
    const [cookie, setCookie, removeCookie] = useCookies(['login'])
    const dropdownRef = useRef('')
    const [toggleMenuBool, setToggleMenuBool] = useState(false)
    const navigate = useNavigate()
    function logout(){
        axios.defaults.withCredentials = true
        axios.get('http://backend.bisa_js.test/api/user', {
            headers: {
                'Accept': 'application/json',
                'Authorization': `Bearer ${cookie.token}`
            }
        }).then((response)=>{
            console.log(response)
        })

        axios.post('http://backend.bisa_js.test/api/logout', {
            headers: {
                'Accept':'application/json',
                'Authorization':`Bearer ${cookie.token}`
            }
        }).then((response)=>{
            console.log(response)
            // removeCookie('user',{
            //     path: '/'
            // });
            // removeCookie('token',{
            //     path: '/'
            // });
            // navigate('/login')
        })
    }
    return (
        <div className='bisa-js__dropdown w-max relative'>
            <button className={toggleMenuBool ? 'bisa-js__dropdown-toogle w-max py-2 px-4 border border-slate-800 text-white bg-slate-800' : 'bisa-js__dropdown-toogle w-max py-2 px-4 border border-slate-800 bg-transparent text-slate-800'} onClick={()=>{
                setToggleMenuBool(!toggleMenuBool)
                dropdownRef.current.classList.contains('hidden') ? dropdownRef.current.classList.remove('hidden') : dropdownRef.current.classList.add('hidden')
            }} >Account</button>
            <div className='bisa-js__dropdown-menu w-max absolute top-12 right-0 bg-white border border-slate-800 hidden' ref={dropdownRef}>
                <div className='bisa-js__dropdown-header p-3'>
                    <p>{cookie.user.name}</p>
                    <p className='text-sm font-medium'>{cookie.user.email}</p>
                </div>
                <hr/>
                <div className='bisa-js__logout'>
                    <button onClick={logout.bind(this)}>Logout</button>
                </div>
            </div>
        </div>
    )
}
export default Dropdown

Here is your logout function.

 public function logout(Request $request){
    Auth::attempt([
        'email'=>$request->email,
        'password'=>$request->password
    ]);
    Auth::user()->token()->delete();
    return response(['message'=>'Successfully Logging out']);
}

and the following piece of code expecting email and password from request.

 Auth::attempt([
        'email'=>$request->email,
        'password'=>$request->password
    ]);

Make sure you are getting the email and password in the request. if not so this line will throw an error (Unauthorized) 401. As I can see your logout function is not getting email and password from the post request. See following is your logout API request.

axios.post('http://backend.bisa_js.test/api/logout', {
        headers: {
            'Accept':'application/json',
            'Authorization':`Bearer ${cookie.token}`
        }
    }).then((response)=>{
        console.log(response)
        // removeCookie('user',{
        //     path: '/'
        // });
        // removeCookie('token',{
        //     path: '/'
        // });
        // navigate('/login')
    })

I think your logout function should look like the following.

public function logout(){
    Auth::user()->token()->delete();
    return response(['message'=>'Successfully Logging out']);
}

OR

Here is the link to the already given answer about the logout function try this. enter link description here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM