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