Vroeg of laat stelt een grote klant de vraag: kunnen onze medewerkers inloggen op jullie applicatie met hun bedrijfsaccount? Ze bedoelen niet nog een gebruikersnaam en wachtwoord. Ze bedoelen de Microsoft Entra ID-, Okta- of AD FS-login die ze al voor al het andere gebruiken, met hun eigen wachtwoordbeleid, hun eigen tweefactorauthenticatie en één plek om een account uit te schakelen op de dag dat iemand vertrekt.
Het antwoord dat hun identiteitsteam verwacht, is SAML 2.0. In het overzicht van de nieuwe loginonderdelen kreeg SAML één alinea. Dit bericht is de volledige flow: wat TsgcSAMLServiceProvider doet, de code van een loginpagina en een Assertion Consumer Service, hoe u uw applicatie registreert bij de gangbare identity providers, en hoe u dit alles vandaag nog test zonder ergens een account te hebben.
Hoe SAML-aanmelding werkt
Er zijn drie partijen bij betrokken. Uw applicatie is de service provider (SP). De directory van de klant is de identity provider (IdP). De browser draagt de berichten tussen beide over, zodat uw server en de IdP nooit rechtstreeks met elkaar communiceren.
- De gebruiker opent uw login-URL. Uw applicatie bouwt een AuthnRequest en stuurt de browser door naar de IdP.
- De IdP meldt de gebruiker aan, met welke wachtwoord-, MFA- of conditional-access-regels het bedrijf ook hanteert.
- De IdP antwoordt met een ondertekende SAMLResponse, en de browser post deze naar de Assertion Consumer Service (ACS)-URL.
- Uw applicatie valideert de respons en maakt een eigen sessie aan voor de gebruiker die daarin wordt genoemd.
Stap vier is waar SAML-implementaties fout gaan, en dat is precies het deel dat het component voor u doet.
De service provider, stap voor stap
TsgcSAMLServiceProvider is geen HTTP-server. Het bouwt en controleert de SAML-berichten, en u roept het aan vanuit de request handler van de server die uw applicatie al heeft, bijvoorbeeld een TsgcWebSocketHTTPServer of een TsgcHTTPServer.
- Beschrijf uw applicatie. Stel
EntityIDin, de unieke naam van uw applicatie (meestal de URL van de metadata), enAssertionConsumerServiceURL, de https-URL waar de respons binnenkomt. - Beschrijf de identity provider. Roep
LoadIdPMetadataaan met het metadatadocument van de IdP. Het leest de entity ID van de IdP, de login-URL en binding, en alle ondertekeningscertificaten. Zonder metadata stelt uIdPEntityID,IdPSSOURLenIdPCertificateshandmatig in. - Stuur de aanvraag.
GetAuthnRequestRedirectURLgeeft de URL terug waarnaar de browser moet worden doorgestuurd. Voor een IdP die alleen de HTTP-POST-binding aanbiedt, geeftGetAuthnRequestPostFormin plaats daarvan een pagina terug die de aanvraag post. - Bewaar het aanvraag-id. Beide methoden geven het id van de nieuwe AuthnRequest terug. Bewaar dit op de server, geïndexeerd op een willekeurige RelayState of de sessiecookie, en verwijder het zodra het antwoord binnenkomt, zodat elke aanvraag maar één keer kan worden beantwoord.
- Verwerk de respons. Roep op de ACS-URL
ProcessResponseaan met de geposte SAMLResponse, de RelayState en het bewaarde aanvraag-id. Als ditTrueteruggeeft, bevat eenTsgcSAMLResultdeNameID, deSessionIndexen elk attribuut dat de IdP heeft verzonden. Als ditFalseteruggeeft, geeftErrorMessagede reden aan en wordtOnSAMLErrorgeactiveerd.
Met EntityID, AssertionConsumerServiceURL en LoadIdPMetadata eenmalig ingesteld bij het opstarten, passen de loginpagina en de ACS in één 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 en CreateUserSession staan voor uw eigen code: een GUID, een thread safe lijst geïndexeerd op RelayState die elk aanvraag-id één keer uitgeeft, en de sessiecookie van uw applicatie. Attributen komen binnen als Name=Value-regels, dus oResult.Attributes.Values['email'] leest er één op naam. Entra ID benoemt ze met claim-URI's zoals http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress.
De RelayState valt niet onder de handtekening van de IdP. Gebruik hem als sleutel om uw eigen openstaande aanvraag te vinden, nooit als een URL waarnaar u ongecontroleerd doorstuurt.
Uw applicatie registreren bij de identity provider
GetMetadata geeft de metadata van de service provider terug: uw entity ID en uw ACS-URL met de HTTP-POST-binding. Serveer deze op een URL zoals /saml/metadata, of sla ze op in een bestand, en geef ze aan de IdP. Elke identity provider vraagt om dezelfde twee waarden, de entity ID van de SP en de ACS-URL, dus de onderstaande notities gaan vooral over waar elke console deze bewaart. Laat bij alle de versleuteling van assertions uitgeschakeld.
- Microsoft Entra ID. Enterprise applications, New application, Create your own application (non-gallery). Kies bij Single sign-on voor SAML, en upload dan de SP-metadata of vul Identifier (Entity ID) en Reply URL in. Wijs gebruikers of groepen toe, en laad de App Federation Metadata Url die wordt getoond in SAML Certificates.
- Okta. Applications, Create App Integration, SAML 2.0. Single sign-on URL is uw ACS-URL, met “Use this for Recipient URL and Destination URL” aangevinkt, en Audience URI is uw entity ID. Voeg attribute statements toe zoals email, firstName en lastName, wijs personen of groepen toe, en laad de Metadata URL vanaf het tabblad Sign On.
- AD FS. Voeg een claims aware Relying Party Trust toe en importeer de SP-metadata. AD FS accepteert alleen https-endpoints. Voeg claim rules toe die een Name ID versturen, bijvoorbeeld E-Mail-Addresses verstuurd als E-Mail Address, en vervolgens E-Mail Address omgezet naar Name ID. De IdP-metadata staat op
https://<adfs-host>/FederationMetadata/2007-06/FederationMetadata.xml. - Google Workspace. Admin console, Apps, Web and mobile apps, Add custom SAML app. Download de IdP-metadata, voer uw ACS-URL en entity ID in, kies de Name ID (bijvoorbeeld het primaire e-mailadres) en schakel de app in voor uw gebruikers.
- Keycloak. Maak een SAML client aan waarvan de Client ID uw entity ID is, of importeer de SP-metadata. Keycloak ondertekent standaard het hele document, dus schakel ook Sign assertions in. Als Client signature required aan staat, stelt u
SignAuthnRequests,SPCertificateenSPPrivateKeyin. De IdP-metadata staat ophttps://<host>/realms/<realm>/protocol/saml/descriptor.
Welke IdP het ook is, de laatste stap is hetzelfde: geef de metadata door aan LoadIdPMetadata. Als het document meerdere entiteiten beschrijft, kiest de tweede parameter de uwe.
Wat ProcessResponse controleert
Een SAML-respons is een ondertekend XML-document, en de meeste bekende SAML-kwetsbaarheden zijn manieren om een service provider iets anders te laten lezen dan wat er is ondertekend. Een respons wordt alleen geaccepteerd als elk van deze controles slaagt:
- De handtekening, tegen het IdP-certificaat. De respons wordt alleen geverifieerd met de certificaten in
IdPCertificates. Een certificaat dat in het bericht is ingesloten, wordt nooit vertrouwd, omdat een aanvaller er ook een kan insluiten. MetWantAssertionsSigned, de standaardinstelling, moet de assertion een eigen handtekening dragen. - Bescherming tegen signature wrapping. De handtekening moet verwijzen naar een element waarvan de ID uniek is in het document, en na de verificatie wordt alleen het ondertekende element gelezen. Een niet-ondertekende assertion die naast de ondertekende is gesmokkeld, wordt nooit bekeken.
- Eén enkele assertion. De respons moet precies één assertion bevatten, direct onder de response.
- Audience en recipient. De audience moet uw
EntityIDzijn en de recipient uwAssertionConsumerServiceURL, zodat een assertion die voor een andere applicatie is uitgegeven, wordt geweigerd. - Het tijdvenster. NotBefore en NotOnOrAfter worden gecontroleerd tegen UTC met een tolerantie van
ClockSkewseconden, standaard twee minuten.MaxAssertionAgekan ook beperken hoe oud een assertion mag zijn. - InResponseTo. De respons moet het door u bewaarde aanvraag-id beantwoorden. Ongevraagde, door de IdP geïnitieerde responsen worden geweigerd tenzij u
AllowIdPInitiatedinstelt. - Replay-cache. Het ID van elke geaccepteerde assertion wordt bewaard tot deze verloopt, zodat dezelfde respons die twee keer wordt gepost, wordt geweigerd. De cache is thread safe en bevindt zich in het geheugen. Wanneer meerdere servers de aanmelding delen, overschrijft u
DoAddToReplayCacheom de ID's in een gedeelde opslag te bewaren. - SHA-1 standaard uitgeschakeld. RSA-SHA1-handtekeningen en SHA-1-digests worden geweigerd tenzij u
AllowSHA1instelt voor een IdP die deze nog nodig heeft.
De parser weigert ook DOCTYPE-declaraties, zodat er geen externe entiteiten zijn, en beperkt de grootte en nestingdiepte van het document. De issuer moet de door u geconfigureerde IdP zijn. De eerste mislukte controle stopt de validatie en de reden staat in ErrorMessage: log deze, en toon de gebruiker een eenvoudige “aanmelden mislukt”-pagina.
Probeer het zonder account
U hoeft geen Entra ID-tenant te hebben om SAML in actie te zien. Mock SAML is een gratis test-identity provider op mocksaml.com. Het accepteert elke service provider en haalt de audience en de ACS-URL uit de AuthnRequest, dus er is niets te registreren.
De demo Demos\26.Authentication\03.SAML_ServiceProvider is een volledige service provider op een TsgcWebSocketHTTPServer, met de endpoints /login, /acs en /metadata op http://localhost:8090:
- Bouw de demo en houd libcrypto-3.dll en libssl-3.dll naast het uitvoerbare bestand. Ze staan in de demomap, en OpenSSL verifieert de RSA-handtekeningen.
- Klik op Load IdP metadata. De standaardbron is de metadata-URL van mocksaml.com.
- Klik op Start, daarna op Open Browser, en volg de aanmeldlink.
- Typ op mocksaml.com een willekeurige gebruikersnaam op het domein example.com en een willekeurig wachtwoord.
- De browser keert terug naar de ACS, en de pagina toont de NameID, de SessionIndex en de attributen id, email, firstName en lastName.
Als dat werkt, open dan http://localhost:8090/metadata, registreer dit bij uw echte IdP, laad de IdP-metadata in de demo en meld u opnieuw aan. Voor AD FS voert u de demo eerst uit met SSL, omdat AD FS alleen https accepteert.
Huidige beperkingen
- Geen versleutelde assertions. Een respons met een EncryptedAssertion of een versleutelde NameID wordt geweigerd. Laat de versleuteling van assertions uitgeschakeld in de IdP. De assertion is nog steeds ondertekend en gaat over https.
- Geen Single Logout. SLO is niet geïmplementeerd.
SessionIndexwordt teruggegeven zodat uw applicatie zijn eigen sessie kan beëindigen en zijn eigen logout kan opbouwen.
Documentatie
Waar u het kunt krijgen
TsgcSAMLServiceProvider zit in de edities Enterprise en All-Access van sgcWebSockets, voor Delphi en C++ Builder, en hetzelfde component maakt deel uit van sgcWebSockets .NET. Als u alleen authenticatie nodig heeft, biedt het sgcAuth-pakket het samen met de andere loginonderdelen. De unit is sgcAuth_SAML_SP, en er verandert niets in een bestaande applicatie totdat u het component op een form plaatst.
Lees verder
Bekijk het
Er is een korte video, “SAML single sign-on in Delphi with Entra ID, Okta and AD FS”, op het eSeGeCe-kanaal. Deze toont de code in de IDE en een live aanmelding met de demo tegen mocksaml.com.
Vragen, feedback of hulp bij het koppelen van uw identity provider? Neem contact op. U krijgt antwoord van de mensen die de code hebben geschreven.
