Single Sign-On SAML in Delphi con Entra ID, Okta e AD FS

· Componenti
Single Sign-On SAML in Delphi con Entra ID, Okta e AD FS

Prima o poi un grande cliente pone la domanda: il nostro personale può accedere alla vostra applicazione con l'account aziendale? Non intendono un altro nome utente e un'altra password. Intendono l'accesso a Microsoft Entra ID, Okta o AD FS che già usano per tutto il resto, con la propria politica delle password, il proprio secondo fattore e un unico punto da cui disattivare un account il giorno in cui qualcuno lascia l'azienda.

La risposta che il loro team identità si aspetta è SAML 2.0. Nella panoramica dei nuovi componenti di login SAML ha avuto un solo paragrafo. Questo articolo è l'intero flusso: cosa fa TsgcSAMLServiceProvider, il codice di una pagina di login e di un Assertion Consumer Service, come registrare la vostra applicazione presso gli identity provider più comuni, e come provare tutto questo già oggi senza un account da nessuna parte.

Come funziona l'accesso SAML

Sono coinvolte tre parti. La vostra applicazione è il service provider (SP). La directory del cliente è l'identity provider (IdP). Il browser porta i messaggi tra i due, quindi il vostro server e l'IdP non comunicano mai direttamente.

  1. L'utente apre l'URL di login. La vostra applicazione costruisce un AuthnRequest e reindirizza il browser verso l'IdP.
  2. L'IdP autentica l'utente, con qualsiasi regola di password, MFA o accesso condizionale che l'azienda abbia definito.
  3. L'IdP risponde con una SAMLResponse firmata, e il browser la invia tramite POST all'URL del vostro Assertion Consumer Service (ACS).
  4. La vostra applicazione convalida la risposta e crea una propria sessione per l'utente che questa indica.

Il passo quattro è il punto in cui le implementazioni SAML sbagliano, ed è la parte che il componente fa per voi.

Il service provider, passo dopo passo

TsgcSAMLServiceProvider non è un server HTTP. Costruisce e verifica i messaggi SAML, e lo chiamate dal request handler del server che la vostra applicazione già possiede, ad esempio un TsgcWebSocketHTTPServer o un TsgcHTTPServer.

Con EntityID, AssertionConsumerServiceURL e LoadIdPMetadata impostati una volta all'avvio, la pagina di login e l'ACS stanno in un unico request handler:

uses
  sgcAuth_SAML_SP;

procedure TMyApp.OnCommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
var
  vRelayState, vRequestID: string;
  oResult: TsgcSAMLResult;
begin
  if ARequestInfo.Document = '/saml/login' then
  begin
    // 1. send the browser to the identity provider
    vRelayState := NewRelayState;
    AResponseInfo.Redirect(FSAML.GetAuthnRequestRedirectURL(vRelayState,
      vRequestID));
    // 2. keep the request id, the response must answer it
    AddPendingRequest(vRelayState, vRequestID);
  end
  else if (ARequestInfo.Document = '/saml/acs') and
    SameText(ARequestInfo.Command, 'POST') then
  begin
    // 3. the browser posts SAMLResponse and RelayState back
    vRelayState := ARequestInfo.Params.Values['RelayState'];
    vRequestID := TakePendingRequest(vRelayState);
    oResult := TsgcSAMLResult.Create;
    try
      if FSAML.ProcessResponse(ARequestInfo.Params.Values['SAMLResponse'],
        vRelayState, vRequestID, oResult) then
      begin
        // 4. signed in: create your own session for this user
        CreateUserSession(AResponseInfo, oResult.NameID, oResult.SessionIndex);
        AResponseInfo.Redirect('/');
      end
      else
        AResponseInfo.ResponseNo := 403; // log oResult.ErrorMessage
    finally
      oResult.Free;
    end;
  end;
end;

NewRelayState, AddPendingRequest, TakePendingRequest e CreateUserSession rappresentano il vostro codice: un GUID, una lista thread safe indicizzata per RelayState che consegna ogni id di richiesta una sola volta, e il cookie di sessione della vostra applicazione. Gli attributi arrivano come righe Name=Value, quindi oResult.Attributes.Values['email'] ne legge uno per nome. Entra ID li nomina con claim URI come http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.

Il RelayState non è coperto dalla firma dell'IdP. Usatelo come chiave per trovare la vostra richiesta in sospeso, mai come un URL verso cui reindirizzare senza controllarlo.

Registrare la vostra applicazione presso l'identity provider

GetMetadata restituisce i metadati del service provider: il vostro entity ID e il vostro URL ACS con il binding HTTP-POST. Pubblicateli a un URL come /saml/metadata, oppure salvateli in un file, e consegnateli all'IdP. Ogni identity provider chiede gli stessi due valori, l'entity ID dell'SP e l'URL dell'ACS, quindi le note seguenti riguardano soprattutto dove ogni console li conserva. In tutti, lasciate disattivata la cifratura delle asserzioni.

Qualunque sia l'IdP, l'ultimo passo è lo stesso: passate i suoi metadati a LoadIdPMetadata. Quando il documento descrive più entità, il secondo parametro sceglie la vostra.

Cosa verifica ProcessResponse

Una risposta SAML è un documento XML firmato, e la maggior parte delle vulnerabilità SAML note sono modi per far leggere a un service provider qualcosa di diverso da ciò che è stato firmato. Una risposta viene accettata solo quando tutti questi controlli passano:

Il parser rifiuta anche le dichiarazioni DOCTYPE, quindi non ci sono entità esterne, e limita dimensione e profondità di nidificazione del documento. L'issuer deve essere l'IdP configurato. Il primo controllo fallito ferma la convalida e il suo motivo si trova in ErrorMessage: registratelo, e mostrate all'utente una semplice pagina “accesso non riuscito”.

Provalo senza un account

Non serve un tenant Entra ID per vedere SAML in funzione. Mock SAML è un identity provider di test gratuito su mocksaml.com. Accetta qualsiasi service provider e prende audience e URL ACS dall'AuthnRequest, quindi non c'è nulla da registrare.

La demo Demos\26.Authentication\03.SAML_ServiceProvider è un service provider completo su un TsgcWebSocketHTTPServer, con endpoint /login, /acs e /metadata su http://localhost:8090:

  1. Compilate la demo e tenete libcrypto-3.dll e libssl-3.dll accanto all'eseguibile. Sono nella cartella della demo, e OpenSSL verifica le firme RSA.
  2. Fate clic su Load IdP metadata. La fonte predefinita è l'URL dei metadati di mocksaml.com.
  3. Fate clic su Start, poi su Open Browser, e seguite il link di accesso.
  4. Su mocksaml.com, digitate un nome utente qualsiasi sul dominio example.com e una password qualsiasi.
  5. Il browser torna all'ACS, e la pagina mostra il NameID, il SessionIndex e gli attributi id, email, firstName e lastName.

Quando funziona, aprite http://localhost:8090/metadata, registratelo presso il vostro IdP reale, caricate i metadati dell'IdP nella demo e accedete di nuovo. Per AD FS, eseguite prima la demo con SSL, perché AD FS accetta solo https.

Limiti attuali

Documentazione

Dove trovarlo

TsgcSAMLServiceProvider è incluso nelle edizioni Enterprise e All-Access di sgcWebSockets, per Delphi e C++ Builder, e lo stesso componente fa parte di sgcWebSockets .NET. Se vi serve solo l'autenticazione, il pacchetto sgcAuth lo offre insieme agli altri componenti di login. L'unit è sgcAuth_SAML_SP, e nulla cambia in un'applicazione esistente finché non posizionate il componente su una form.

Continua a leggere

Guardalo in video

C'è un breve video, “SAML single sign-on in Delphi with Entra ID, Okta and AD FS”, sul canale eSeGeCe. Mostra il codice nell'IDE e un accesso dal vivo con la demo contro mocksaml.com.

Domande, feedback o aiuto per collegare il vostro identity provider? Contattateci. Riceverete una risposta dalle persone che hanno scritto il codice.