0

I have a laravel and react application

I use Laravel 10 in the back for authentication management

for authentication I use Socialite to authenticate my user with Microsoft

the configuration in my .env file for microsoft has already been done and is functional

I'm now a little lost

  1. How do I store my token on the Laravel side?

  2. how does this happen for the refresh token

I need to know if my user is logged in to protect my server side and client side routes

on the client side, I will have my Routes.js file which surrounds my routes and which is itself surrounded by an Auth Provider

Routes.js

return (
    <Router>
      <AuthProvider>
        <NavBar />
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route element={<ProtectedRoute />}>
            <Route path="/home" element={<Home />} />
            <Route path="/generator" element={<GeneratorPage />} />
          </Route>
        </Routes>
      </AuthProvider>
)

Auth provider allows me to check with the token if the user is well authenticated in auth provider, I use 'api' which is already configured to call my server with 'withCredentials' set to true

useAuth.js

import React, { createContext, useContext, useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import api from 'config/api';

const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const [isAuthenticated, setIsAuthenticated] = useState(false);
  const navigate = useNavigate();

  useEffect(() => {
    const checkAuthStatus = async () => {
      try {
        const response = await api.get('api/auth/check-token');
        setIsAuthenticated(response.data.isAuthenticated);
        if (!response.data.isAuthenticated) {
          navigate('/home');
        }
      } catch (error) {
        console.error('Failed to check authentication status:', error);
        navigate('/login');
      }
    };

    checkAuthStatus();
  }, []);

  return (
    <AuthContext.Provider value={{ isAuthenticated, setIsAuthenticated }}>
      {children}
    </AuthContext.Provider>
  );
};

export function useAuth() {
  return useContext(AuthContext);
}

In order to authenticate my user, I have a Login.js page which when clicking on 'Connect with Azure' will launch the back function in order to obtain the authentication redirection link So far it works and I get the redirection link and I can insert my identifier

Once authenticated, I call 'api/auth/exchange' which allows me, thanks to the 'code' obtained in the url, to retrieve user information and store my token in a cookie on the server side.

Here, when I log response, I can see that I am recovering my user information but I get the response 'Set-Cookie header is ignored in response from url: http://localhost:8000/api/auth/exchange. The combined size of the name and value must be less than or equal to 4096 characters.' However, the token is not at all 4096 characters long.

Login.js

import React from 'react';
import Container from '@mui/material/Container';
import { useNavigate } from 'react-router-dom';
import api from 'config/api';

export const Login = () => {
  const navigate = useNavigate();

  React.useEffect(() => {
    const handleTokenExchange = async () => {
      console.log('exchange');
      const urlParams = new URLSearchParams(window.location.search);
      const code = urlParams.get('code');
      if (code) {
        try {
          const response = await api.post('api/auth/exchange', { code });
          if (response.status === 200) {
            console.log(response);
            // Navigate to home if exchange is successful
            navigate('/home');
          } else {
            console.error('Token exchange failed', response.status);
            // Handle errors, maybe navigate to error page or show an error message
          }
        } catch (err) {
          console.error('Error during token exchange:', err);
          // Handle errors
        }
      }
    };

    handleTokenExchange();
  }, []);

  const handleLogin = async () => {
    try {
      const res = await api.get('api/auth/azure/redirect');
      window.location.href = res.data;
    } catch (err) {
      console.log('Error initiating Azure login:', err);
    }
  };

  return (
    <Container sx={{ height: '100vh' }} fixed>
      <button onClick={handleLogin}>Connect with Azure</button>
    </Container>
  );
};

Finally, '/home' is in protected route. Protected route thanks to 'useAuth' checks if my user is well authenticated to return protected routes or not.

ProtectedRoute.js

import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from 'context/authContext';

export const ProtectedRoute = () => {
  const { isAuthenticated } = useAuth();

  return isAuthenticated ? <Outlet /> : <Navigate to="/login" replace />;
};

On the server side, I have my routes to call my Microsoft redirection function, my function to save the token and send it back to the front, and the use of my services to check if the user is well authenticated

api.php

Route::get('/auth/check-token', [AuthController::class, 'checkToken']);
Route::post('/auth/exchange', [AuthController::class, 'exchangeCodeForToken']);
Route::get('/auth/azure/redirect', [AuthController::class, 'authRedirectWithAzure']);```


Route::middleware('verifyjwt')->group(function () {
Route::post('/offer', [OfferController::class, 'createOffer']);

});

authController.php

<?php

namespace App\Http\Controllers;


//use Illuminate\Support\Facades\Log;

use App\Http\Controllers\Controller;
use App\Http\Status\HttpStatus;
use App\Services\AuthService;
use Illuminate\Http\Request;
use Laravel\Socialite\Facades\Socialite;
use Throwable;
use App\Services\LogService;
use Illuminate\Support\Facades\Cookie;
use Illuminate\Support\Facades\Log;

class AuthController extends Controller
{

    /**
     * A protected variable to access the log service and it's functions.
     *
     * @var LogService
     */
    protected $logService;

    protected $authService;


    /**
     * UserController constructor.
     *
     * @param LogService $logService The log service to initialize the protected variable.
     */
    public function __construct(LogService $logService, AuthService $authService)
    {

        $this->logService = $logService;
        $this->authService = $authService;
    }

    public function checkToken(Request $request)
    {
        $token = $request->cookie('auth_token');
        $isValid = $this->authService->checkToken($token);

        return response()->json(['isAuthenticated' => $isValid]);
    }
    public function authRedirectWithAzure(Request $request)
    {

        try {
            $url = Socialite::driver('azure')->stateless()->redirect()->getTargetUrl();

            if ($url) {
                return response()->json($url);
            }
        } catch (Throwable $th) {

            return $this->logService->sendLog(
                $th->getMessage(),
                'Failed to connect to Microsoft Azure.',
                $request->ip(),
                $request->url(),
                'Failed to connect to Microsoft Azure.',
                HttpStatus::INTERNAL_SERVER_ERROR,
                null
            );
        }
    }


    public function exchangeCodeForToken(Request $request)
    {

        try {
            $user = Socialite::driver('azure')->stateless()->user();

            if ($user) {


                $token = $user->token;

                $cookie = cookie('auth_token', $token, 120, null, null, true, true, false, 'Lax');


                return response()->json([
                    'userData' => $user->user,
                    'message' => 'Authentication successful',
                ], 200)->withCookie($cookie);
            } else {

                return $this->logService->sendLog(
                    'error',
                    'Error to process user data and set cookie response',
                    'processexchangeCodeForTokenRequest',
                    $request->ip(),
                    $request->url(),
                    "Request to exchangeCodeForToken don't success.",
                    HttpStatus::INTERNAL_SERVER_ERROR,
                    null,
                    null
                );
            }
        } catch (\Throwable $th) {
            return $this->logService->sendLog(
                $th->getMessage(),
                'Unexpected error during Azure authentication.',
                $request->ip(),
                $request->url(),
                'Unexpected error',
                HttpStatus::INTERNAL_SERVER_ERROR,
                null
            );
        }
    }
}

And then the AuthService.php and VerifyAuthMiddleware

AuthService.php

<?php

namespace App\Services;

use Tymon\JWTAuth\Exceptions\JWTException;
use Tymon\JWTAuth\Facades\JWTAuth;


class AuthService
{
    public function checkToken($token)
    {
        try {
            $user = JWTAuth::setToken($token)->authenticate();

            // Si l'utilisateur associé au token est trouvé, le token est valide
            if ($user) {
                return true;
            }

            return false;
        } catch (JWTException $e) {
            return false;
        }
    }
}

VerifyAuthMiddleware.php

<?php

namespace App\Http\Middleware;

use Closure;
use Tymon\JWTAuth\Facades\JWTAuth;
use Tymon\JWTAuth\Exceptions\JWTException;

class VerifyAuthMiddleware
{
    public function handle($request, Closure $next)
    {
        try {
            $user = JWTAuth::parseToken()->authenticate();
        } catch (JWTException $e) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        if (!$user) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $next($request);
    }
}

Problem: when I look at my cookies on the client side, I don't see any cookie called 'auth_token'

I receive an error that the token exceeds 4096 but this is not normally the case.

I already tried to log my "$user" to see exactly what it contained and it does contain 'token'

I tried to use 'makeCookie' instead of cookie but without success

I also tried changing the settings in 'cookie'

I added in cors php config : 'paths' => ['api/*', 'sanctum/csrf-cookie'], 'supports_credentials' => true,

but no success too.

I am missing something ?

0