Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Oauth2 Microsoft Openid Laravel Package

alancting/oauth2-microsoft-openid

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup for Laravel Integration

Since this package is primarily designed for Symfony, Laravel developers will need to adapt it using knpuniversity/oauth2-client-bundle or php-league/oauth2-client directly. Here’s how to start:

  1. Install Dependencies

    composer require league/oauth2-client alancting/oauth2-microsoft-openid
    
  2. Configure the OAuth Client Create a service provider to register the Microsoft provider (Laravel doesn’t natively support Symfony bundles). Example:

    // app/Providers/AuthServiceProvider.php
    use Alancting\OAuth2\OpenId\Client\Provider\AzureAdProvider; // or AdfsProvider
    use League\OAuth2\Client\Provider\GenericProvider;
    
    public function boot()
    {
        $this->registerMicrosoftProvider();
    }
    
    protected function registerMicrosoftProvider()
    {
        $provider = new AzureAdProvider([
            'clientId'                => env('AZURE_AD_CLIENT_ID'),
            'clientSecret'            => env('AZURE_AD_CLIENT_SECRET'),
            'redirectUri'             => env('AZURE_AD_REDIRECT_URI'),
            'urlAuthorize'            => 'https://login.microsoftonline.com/' . env('AZURE_AD_TENANT') . '/oauth2/v2.0/authorize',
            'urlAccessToken'          => 'https://login.microsoftonline.com/' . env('AZURE_AD_TENANT') . '/oauth2/v2.0/token',
            'urlResourceOwnerDetails' => 'https://graph.microsoft.com/v1.0/me',
            'scopes'                  => ['openid', 'profile', 'offline_access'],
        ]);
    
        $this->app->singleton('oauth.microsoft', function () use ($provider) {
            return new GenericProvider($provider->getBaseConfiguration());
        });
    }
    
  3. Create a Login Route Use Laravel Socialite or manually handle the OAuth flow:

    Route::get('/login/microsoft', function () {
        $provider = app('oauth.microsoft');
        $authUrl = $provider->getAuthorizationUrl();
        return redirect()->to($authUrl);
    });
    
    Route::get('/login/microsoft/callback', function (Request $request) {
        $provider = app('oauth.microsoft');
        $token = $provider->getAccessToken('authorization_code', [
            'code' => $request->query('code')
        ]);
        $user = $provider->getResourceOwner($token);
        // Store user data in session/database
    });
    

Implementation Patterns

1. Authentication Workflow

Leverage the package’s built-in token handling for seamless integration:

  • Token Retrieval: Use getOAuthCredential() to fetch tokens (access, ID, refresh).
    $credential = app('oauth.microsoft')->getOAuthCredential();
    $accessToken = $credential->getAccessToken();
    $idTokenJWT = $credential->getIdTokenJWT();
    
  • User Data Extraction: Decode the JWT to extract claims (e.g., upn, name):
    $payload = $idTokenJWT->getPayload();
    $userEmail = $payload['upn'] ?? $payload['email'];
    

2. Multi-Resource Scopes

Request additional API permissions (e.g., Microsoft Graph) by extending scopes:

# .env
AZURE_AD_API_RESOURCE_1=https://graph.microsoft.com/.default
AZURE_AD_API_RESOURCE_2=https://your-api.com/.default

Configure in provider options:

$provider->setProviderOptions([
    'other_resource_scopes' => [
        env('AZURE_AD_API_RESOURCE_1'),
        env('AZURE_AD_API_RESOURCE_2'),
    ],
]);

3. Logout Handling

Redirect users to Microsoft’s logout endpoint:

$logoutUrl = app('oauth.microsoft')->getLogoutUrl();
return redirect()->to($logoutUrl);

4. Role Assignment

Assign roles dynamically based on token claims:

$roles = ['ROLE_USER'];
if ($payload['roles'] ?? null) {
    $roles = array_merge($roles, array_map(fn($role) => 'ROLE_' . strtoupper($role), $payload['roles']));
}
auth()->loginUsingId($userId, $roles);

5. Refresh Tokens

Handle token expiration gracefully:

try {
    $credential = $client->getOAuthCredential();
} catch (\League\OAuth2\Client\Provider\Exception\TemporaryCredentialsException $e) {
    $refreshToken = $e->getRefreshToken();
    $newCredential = $client->getOAuthCredential($refreshToken);
}

Gotchas and Tips

Pitfalls

  1. Deprecated Features:

    • The package no longer supports retrieving image URLs (removed in v2.2.0). Use Microsoft Graph API directly for user photos.
    • PHP 5.5+ is unsupported (minimum PHP 7.1).
  2. Scope Configuration:

    • Ensure openid and profile scopes are always included for ID token validation.
    • Azure AD requires offline_access for refresh tokens.
  3. Token Validation:

    • Always validate the iss (issuer) and aud (audience) claims in the ID token to prevent spoofing:
      $issuer = 'https://login.microsoftonline.com/' . env('AZURE_AD_TENANT') . '/v2.0';
      if ($payload['iss'] !== $issuer) {
          throw new \RuntimeException('Invalid issuer');
      }
      
  4. ADFS vs. Azure AD:

    • ADFS: Uses unique_name as the default user_key. Customize via provider_options.
    • Azure AD: Uses upn (user principal name) by default. Override with user_key if needed.

Debugging Tips

  1. Enable League OAuth Debugging: Add this to your .env to log OAuth requests:

    LEAGUE_OAUTH_DEBUG=true
    
  2. Token Inspection: Use jwt.io to decode ID tokens for manual validation.

  3. Common Errors:

    • invalid_grant: Check redirect_uri matches registered URIs in Azure AD/ADFS.
    • AADSTS70002: Invalid client secret or tenant ID.
    • AADSTS50011: Missing openid scope.

Extension Points

  1. Custom User Provider: Extend Alancting\OAuth2\OpenId\Client\User\UserProvider to map Microsoft claims to your user model:

    class CustomUserProvider extends UserProvider
    {
        protected function getUserData($idTokenPayload)
        {
            return [
                'id'    => $idTokenPayload['oid'],
                'email' => $idTokenPayload['upn'],
                'name'  => $idTokenPayload['name'],
            ];
        }
    }
    
  2. Custom Authenticator: For Laravel, create a custom guard authenticator:

    use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
    
    class MicrosoftAuthenticator extends AbstractOAuth2Authenticator
    {
        public function authenticate(Request $request)
        {
            try {
                $provider = app('oauth.microsoft');
                $token = $provider->getAccessToken('authorization_code', [
                    'code' => $request->query('code'),
                ]);
                $user = $provider->getResourceOwner($token);
                return $this->createUser($user, $token);
            } catch (IdentityProviderException $e) {
                return redirect()->route('login')->with('error', $e->getMessage());
            }
        }
    }
    
  3. Logout Event Listener: Listen for Laravel’s Illuminate\Auth\Events\Logout to trigger Microsoft logout:

    Event::listen(Logout::class, function () {
        $logoutUrl = app('oauth.microsoft')->getLogoutUrl();
        return redirect()->to($logoutUrl);
    });
    

Performance Tips

  • Cache Tokens: Store access tokens in the session or cache (e.g., Redis) to avoid repeated requests.
  • Batch User Lookups: Use Microsoft Graph’s $select query parameter to fetch only required fields:
    $graphUrl = 'https://graph.microsoft.com/v1.0/me?$select=displayName,mail';
    $response = $client->get($graphUrl, [
        'headers' => ['Authorization' => 'Bearer ' . $accessToken],
    ]);
    

Security Considerations

  • Token Storage: Never store refresh tokens in plaintext. Encrypt them using Laravel’s encryption services.
  • CSRF Protection: Ensure your redirect URIs are registered in Azure AD/ADFS and validate them server-side.
  • PKCE for SPAs: If using this package in a single-page app, implement PKCE (Proof Key for Code Exchange) for enhanced security.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui