0

I need to make a authentification with Microsoft GRAPH.

Laravel Framework : 9.46.0 react : ^18.2.0

The login and callback functions are done on the server side with laravel. Calling these functions and storing user data should be done on the client side with REACT.

I thought of doing the login function and then resending the login link forward. But then, where to call my callback ?

Also when I try to redirect my user to the login link I get a CORS error.

Here is my server side:

<?php

namespace App\Http\Controllers;

use App\Http\Controllers\Controller;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Carbon\Carbon;
use App\Models\User;
 use Microsoft\Graph\Graph;
use Microsoft\Graph\Model;
  use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
 use League\OAuth2\Client\Provider\GenericProvider;
 
class AuthController extends Controller
{
  
//SIGN IN 
    public function signin()
    {

        // Initialize the OAuth client
        $oauthClient = new  GenericProvider([
            'clientId' => env('OAUTH_APP_ID'),
            'clientSecret' => env('OAUTH_APP_PASSWORD'),
            'redirectUri' => env('OAUTH_REDIRECT_URI'),
            'urlAuthorize' => env('OAUTH_AUTHORITY') . env('OAUTH_AUTHORIZE_ENDPOINT'),
            'urlAccessToken' => env('OAUTH_AUTHORITY') . env('OAUTH_TOKEN_ENDPOINT'),
            'urlResourceOwnerDetails' => '',
            'scopes' => env('OAUTH_SCOPES')
        ]);

        $authUrl = $oauthClient->getAuthorizationUrl(); 

        // Save client state so we can validate in callback
        session(['oauthState' => $oauthClient->getState()]);

        // Redirect to AAD signin page
        return redirect()->away($authUrl);
    }

//CALL BACK
 public function callback(Request $request)
  {
    // Validate state
    $expectedState = session('oauthState');
    $request->session()->forget('oauthState');
    $providedState = $request->query('state');

    if (!isset($expectedState)) {
      // If there is no expected state in the session,
      // do nothing and redirect to the home page.
      return redirect('/');
    }

    if (!isset($providedState) || $expectedState != $providedState) {
      return redirect('/')
        ->with('error', 'Invalid auth state')
        ->with('errorDetail', 'The provided auth state did not match the expected value');
    }

    // Authorization code should be in the "code" query param
    $authCode = $request->query('code');
    if (isset($authCode)) {
      // Initialize the OAuth client
      $oauthClient = new \League\OAuth2\Client\Provider\GenericProvider([
        'clientId' => env('OAUTH_APP_ID'),
        'clientSecret' => env('OAUTH_APP_PASSWORD'),
        'redirectUri' => env('OAUTH_REDIRECT_URI'),
        'urlAuthorize' => env('OAUTH_AUTHORITY') . env('OAUTH_AUTHORIZE_ENDPOINT'),
        'urlAccessToken' => env('OAUTH_AUTHORITY') . env('OAUTH_TOKEN_ENDPOINT'),
        'urlResourceOwnerDetails' => '',
        'scopes' => env('OAUTH_SCOPES')
      ]);

      try {
        // Make the token request
        $accessToken = $oauthClient->getAccessToken('authorization_code', [
          'code' => $authCode
        ]);

        $graph = new Graph();
        $graph->setAccessToken($accessToken->getToken());

        $user_ms = $graph->createRequest('GET', '/me?$select=displayName,mail,userPrincipalName')
          ->setReturnType(Model\User::class)
          ->execute();

        $photo_path = public_path() . '/assets/img/avatars/' . md5($user_ms->getUserPrincipalName()) . '.jpg';
        if (!file_exists($photo_path)) {
          try {
            $photo = $graph->createRequest("GET", "/me/photo/\$value")->download($photo_path);
          }
          catch (ClientException $e) {
            unlink($photo_path);
          }
        }

        if ($user = User::select('id')->where('username', $user_ms->getUserPrincipalName())->first()) {
          $u = $user;

        }
        else {
          $u = new User();
          $u->email = $user_ms->getMail();
          $u->username = $user_ms->getUserPrincipalName();
          $u->displayName = $user_ms->getDisplayName();
        }

        $u->last_login_at = Carbon::now()->toDateTimeString();
        $u->save();

        Auth::loginUsingId($u->id);

      }
      catch (League\OAuth2\Client\Provider\Exception\IdentityProviderException $e) {
        return redirect('/')
          ->with('error', 'Error requesting access token')
          ->with('errorDetail', $e->getMessage());
      }
    }

    return redirect()->intended('/')
      ->with('error', $request->query('error'))
      ->with('errorDetail', $request->query('error_description'));
  }

My CORS config :

<?php

return [ 

    'paths' => ['api/*'],
    'allowed_methods' => ['*'],
    'allowed_origins' => ['http://localhost:3000'],
    'allowed_headers' => ['*'],
    'exposed_headers' => [],
    'max_age' => 0,
    'supports_credentials' => false,
];

my login component in react

import React  from 'react';
import { Button } from 'react-bootstrap';
import { Box } from '@mui/material';
import axios from 'axios';

function LoginButton() {
  
  const handleLogin = async () => {
    try {
      const response = await axios.get(
        `${process.env.REACT_APP_API_URL2}api/login`
      );
      // Redirect to the Microsoft Graph login page
      window.location.href = response.data;
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <Box textAlign="center" alignSelf="center">
      <Button
        size="large"
        color="buttonBlue"
        variant="outlined"
        className="ml-auto login_button buttonYellow   button"
        onClick={handleLogin}
      >
        Se connecter
      </Button>
    </Box>
  );
}

export default LoginButton;

I have already tried to make a microsoft graph login 100% in react and it is working. I have also tried 100% in laravel and it's working as well, but now, I don't know how I can linked react and laravel.

The cors error when I try to redirect to the link in response.data : "myLink" redirected from 'http://127.0.0.1:8000/api/login') from origin 'http://localhost:3000' has been blocked by CORS policy

Elodie
  • 69
  • 7
  • Can't you simply set ``window.location.href = `${process.env.REACT_APP_API_URL2}api/login`;`` and let the browser do the redirection? – Heiko Theißen Apr 25 '23 at 13:33
  • I still have the same cors error. But maybe is not the right way to make an authentification with graph in laravel and react ? – Elodie Apr 25 '23 at 14:30
  • I meant: set window.location.href = `${process.env.REACT_APP_API_URL2}api/login`; and leave out the `axios.get`. That should remove the CORS error. It's indeed wrong to launch the authentication flow with `axios.get`, see also [here](https://stackoverflow.com/questions/72382892/access-to-fetch-at-https-accounts-google-com-o-oauth2-v2-auth-has-been-blocked). – Heiko Theißen Apr 25 '23 at 14:50
  • This is what i have done (I have read the other post) but now, i don't have cors error (of course) but still not redirect. It seems like the redirect url go in my localhost:3000. Maybe the form point to my http://localhost:8000/api/login but does not interact with OAuth . – Elodie Apr 25 '23 at 15:50

0 Answers0