Installation
composer require thenetworg/oauth2-azure
Ensure your Laravel project has the PHP League OAuth2 Client installed as a dependency.
Register an Azure AD Application
https://your-app.com/auth/callback).Basic Provider Setup
use TheNetworg\OAuth2\Client\Provider\Azure;
$provider = new Azure([
'clientId' => env('AZURE_CLIENT_ID'),
'clientSecret' => env('AZURE_CLIENT_SECRET'),
'redirectUri' => env('AZURE_REDIRECT_URI'),
'scopes' => ['openid', 'profile', 'email'],
]);
First Use Case: Authorization Code Flow Redirect users to Azure for authentication:
$authorizationUrl = $provider->getAuthorizationUrl();
return redirect()->to($authorizationUrl);
Handle the callback:
$token = $provider->getAccessToken('authorization_code', [
'code' => request('code'),
]);
$user = $provider->getResourceOwner($token);
$authUrl = $provider->getAuthorizationUrl(['scope' => $provider->getScope()]);
return redirect()->away($authUrl);
$token = $provider->getAccessToken('authorization_code', [
'code' => request('code'),
]);
session()->put('azure_token', $token);
$user = $provider->getResourceOwner(session('azure_token'));
$baseGraphUri = $provider->getRootMicrosoftGraphUri(null);
$provider->scope = 'openid profile email offline_access ' . $baseGraphUri . '/User.Read';
$token = session('azure_token');
$me = $provider->get($provider->getRootMicrosoftGraphUri($token) . '/v1.0/me', $token);
if ($token->hasExpired() && $token->getRefreshToken()) {
$token = $provider->getAccessToken('refresh_token', [
'refresh_token' => $token->getRefreshToken(),
]);
session()->put('azure_token', $token);
}
$logoutUrl = $provider->getLogoutUrl('https://your-app.com');
return redirect()->away($logoutUrl);
Create a dedicated service provider to manage the Azure provider instance:
// app/Providers/AzureAuthServiceProvider.php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use TheNetworg\OAuth2\Client\Provider\Azure;
class AzureAuthServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(Azure::class, function ($app) {
return new Azure([
'clientId' => env('AZURE_CLIENT_ID'),
'clientSecret' => env('AZURE_CLIENT_SECRET'),
'redirectUri' => env('AZURE_REDIRECT_URI'),
'scopes' => ['openid', 'profile', 'email'],
]);
});
}
}
// app/Http/Middleware/AuthenticateWithAzure.php
namespace App\Http\Middleware;
use Closure;
use TheNetworg\OAuth2\Client\Provider\Azure;
class AuthenticateWithAzure
{
protected $provider;
public function __construct(Azure $provider)
{
$this->provider = $provider;
}
public function handle($request, Closure $next)
{
if (!$request->user()) {
$authUrl = $this->provider->getAuthorizationUrl();
return redirect()->away($authUrl);
}
return $next($request);
}
}
$provider = new Azure([
'clientId' => env('AZURE_CLIENT_ID'),
'clientCertificatePrivateKey' => file_get_contents(env('AZURE_CERT_PRIVATE_KEY_PATH')),
'clientCertificateThumbprint' => env('AZURE_CERT_THUMBPRINT'),
'redirectUri' => env('AZURE_REDIRECT_URI'),
]);
State Validation
Always validate the state parameter in the callback to prevent CSRF attacks:
if (!isset($_GET['state']) || $_GET['state'] !== session('oauth_state')) {
abort(403, 'Invalid state parameter');
}
Token Expiry Handling Tokens expire quickly (typically 1 hour). Always check for expiry and refresh tokens when making API calls:
if ($token->hasExpired()) {
if ($token->getRefreshToken()) {
$token = $provider->getAccessToken('refresh_token', [
'refresh_token' => $token->getRefreshToken(),
]);
} else {
// Redirect to login if no refresh token
return redirect()->route('login');
}
}
Scope Configuration
resource to specify the API (e.g., https://graph.microsoft.com).scope directly (e.g., openid profile email https://graph.microsoft.com/User.Read).Redirect URI Mismatch
Ensure the redirectUri in your Laravel app matches exactly with the one registered in Azure AD (including https:// and trailing slashes).
Certificate Thumbprint
When using certificates, ensure the thumbprint is correctly formatted (e.g., B4A94A83092455AC4D3AC827F02B61646EAAC43D). A single mistake can cause authentication failures.
Enable Guzzle Debugging Add this to your provider initialization to log HTTP requests:
$provider = new Azure([...]);
$provider->getHttpClient()->getEmitter()->attach(
new \GuzzleHttp\Middleware::tap(function ($request) {
\Log::debug('Request:', [
'url' => (string) $request->getUri(),
'method' => $request->getMethod(),
'headers' => $request->getHeaders(),
]);
})
);
Validate Token Manually Use Azure's token validation endpoint to debug token issues:
curl -X POST "https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "client_id={client_id}&client_secret={client_secret}&grant_type=client_credentials"
Check Azure AD Logs Navigate to Azure Portal > Azure Active Directory > Monitor > Sign-ins to inspect authentication attempts.
Use Laravel Sessions for Tokens Store tokens in the session to persist them across requests:
session()->put('azure_token', $token);
$token = session('azure_token');
Leverage Resource Owner Extract user details easily:
$user = $provider->getResourceOwner($token);
$email = $user->getUpn(); // User Principal Name (UPN)
$name = $user->getFirstName() . ' ' . $user->getLastName();
Custom Headers for API Requests Add custom headers (e.g., for Microsoft Graph):
$headers = ['Accept' => 'application/json', 'Content-Type' => 'application/json'];
$response = $provider->get('https://graph.microsoft.com/v1.0/me', $token, $headers);
B2C Specifics For Azure AD B2C, set custom policies and endpoints:
$provider->pathAuthorize = "/oauth2/v2.0/authorize";
$provider->pathToken = "/oauth2/v2.0/token";
$provider->scope = ["
How can I help you explore Laravel packages today?