Přihlašování přes Microsoft Azure (Entra ID, oauth2, OIDC) (Návod)

ludek2
Člen | 1
+
+1
-

Píšeme aplikaci pro firmu, kde mají povinné dvoufaktorové (2FA, MFA) ověřování proti Microsoftímu serveru.

Vyřešíme v Nette s pomocí contributte/oauth2-client.

0. Registrace naší aplikace na portal.azure.com, resp. entra.microsoft.com

  • Důležité je správně nastavit Redirect URI, čili kam Microsoft po přihlášení uživatele vrátí (např. http://localhost/projekt/www/sign/azure-callback)
  • Dále získat: Application (client) ID, Client Secret/Value, Directory (tenant) ID
  • V sekci Authentication u vaší aplikace nezapomeňte povolit ID tokens a v API permissions přidat delegované oprávnění User.Read.

(Přejeme pevné nervy.)

1. Instalace balíčků
oauth2-client neumí Azure, takže musíme přiinstalovat ještě Third-party Provider Client oauth2-azure.

composer require contributte/oauth2-client
composer require thenetworg/oauth2-azure

2. Flow
Protože základní Flow v balíčku contributte/oauth2-client je abstraktní, vytvoříme jednoduchou implementaci v pomocné třítě app/Model/AzureFlow.php, kde mu jen pomůžeme získat správný Provider (tedy Azure). Tuto třídu potom zaregistrujeme jako službu v configu a předáme si ji jako závislost do přihlašovacího presenteru, kde nám vygeneruje externí odkaz a zpracuje odpověď autorizačního serveru:

<?php

declare(strict_types=1);

namespace App\Model;

use Contributte\OAuth2Client\Flow\AuthCodeFlow;
use TheNetworg\OAuth2\Client\Provider\Azure;

/**
 * Azure AD implementation of OAuth2 AuthCodeFlow
 */
class AzureFlow extends AuthCodeFlow {

    /**
     * @return Azure
     */
    public function getProvider(): Azure {
        return $this->provider;
    }
}

3. Konfigurace
local.neon nastavíme parametry, co jsme dostali při registraci aplikace:

parameters:
    azure:
        clientId: 'vaše-client-id' # Application (client) ID
        clientSecret: 'vaše-client-secret' # Client Secret/Value
        tenant: 'vaše-tenant-id' # Directory (tenant) ID
        redirectUri: 'http://localhost/projekt/www/sign/azure-callback'

4. Registrace služeb
common.neon: zaregistrujeme Azure providera a Flow. Důležité je vynutit verzi koncového bodu 2.0 a definovat scopes (tzn. co nám bude vráceno zpět).

services:
    azureProvider:
        factory: TheNetworg\OAuth2\Client\Provider\Azure([
            clientId: %azure.clientId%,
            clientSecret: %azure.clientSecret%,
            redirectUri: %azure.redirectUri%,
            tenant: %azure.tenant%,
            defaultEndPointVersion: '2.0',
            scopes: ['openid', 'profile', 'email']
        ])

    azureFlow: App\Model\AzureFlow(@azureProvider)

Podle toho se

  • připraví provider: Nette vytvoří instanci Azure providera a naplní ho vašimi údaji z local.neon.
  • sestaví Flow: Nette vyrobí instanci AzureFlow a do jejího konstruktoru automaticky vloží připravený provider.

5. Implementace přihlašování
SignPresenter vytvoříme akci pro přesměrování ven na Azure a callback pro zpracování návratu. Abychom mohli volat AzureFlow, musíme ho vložit jako závislost.

use App\Model\AzureFlow;

// Model\AzureFlow si předejte jak jste zvyklí

/** @var AzureFlow */
private $azureFlow;

public function __construct(AzureFlow $azureFlow)
{
	$this->azureFlow = $azureFlow;
}

// Přesměruje na odkaz ve kterém jsou parametry, kterým Azure rozumí.
// Na tuto akci bude odkazovat přihlašovací tlačítko.
public function actionAzureLogin(): void {
    $this->redirectUrl($this->azureFlow->getAuthorizationUrl());
}

// Bod návratu po přihlášení u Microsoftu
public function actionAzureCallback(): void {
    try {
        // 1. Získání tokenu
        $token = $this->azureFlow->getAccessToken($this->getHttpRequest()->getQuery());
		// 2. Získání dat o uživateli z MS
        $azureUser = $this->azureFlow->getProvider()->getResourceOwner($token);
		// Azure vrací e-mail v poli 'mail' nebo 'userPrincipalName'
        $email = $azureUser->getUpn() ?: $azureUser->getEmail();

		if (!$email) { throw new \Exception('Z Azure AD se nepodařilo získat e-mail uživatele.'); }

        // 3. Hledáme uživatele u sebe podle obdrženého emailu
		$identity = $this->database->table('users')->where('email', $email)->fetch();

        if (!$identity) {
			// Uživatel je v Azure v pořádku, ale nemá u nás založený účet
            $this->flashMessage("Uživatel '$email' nemá v aplikaci povolen přístup. Kontaktujte administrátora.", 'danger');
            $this->redirect('in');
        }

        // 4. Přihlášení do Nette
        $this->getUser()->login($identity);
		$this->flashMessage('Úspěšné přihlášení účtem Microsoft.', 'success');

        // Přesměrování
        $this->restoreRequest($this->backlink);
        $this->redirect('Homepage:');

        } catch (\Throwable $e) {
            // Pokud jde o výjimku pro přesměrování, necháme projít, jinak se přesměrování neprovede
            if ($e instanceof AbortException) { throw $e; }

            $this->flashMessage('Chyba při přihlašování přes Azure: ' . $e->getMessage(), 'danger');
            $this->redirect('in');
        }
    }

6. Šablona
do šablony Sign.in.latte dáme přihlašovací tlačítko

<a n:href="azureLogin" class="btn btn-outline-primary">
    Přihlásit se přes Microsoft
</a>

Hotovo.

Editoval ludek2 (Dnes 19:48)

Felix
Nette Core | 1279
+
0
-

Super, diky za sdileni. Bude se hodit.