MSXFAQ MeetNow aktiv: Komm doch einfach dazu.

Simple SAML Sample

Diese Seite beschreibt eine einfache Einrichtung einer Applikation in meinem Tenant, damit sich Anwender meines Tenants auf eine Webseite zugreifen, dann auf Entra ID umgeleitet und bei der Rückkehr mit einem Token ankommen, welches die Webanwendung dann als Anmeldeinformation nutzen kann.

Worum geht es?

Stellen Sie sich vor, sie haben eine selbst entwickelte Webapplikation und müssten sich um die Anmeldung selbst kümmern. Natürlich können  Sie nun eine Anmeldung per "Formular" mit "Benutzername + Kennwort" vorstehen und die Information in einer eigenen Datenbank speichern. Das ist aber unsicher und kein Single-SignIn. Sie könnten Insbesondere mit dem IIS auch einfach die Anmeldung per "Negotiate/NTLM/Kerberos" nutzen. Das funktioniert gut, wenn Sie im LAN sind, der Computer "Domain Joined" ist und die Anwender als Domainbenutzer angemeldet sind. Für Kerberos müssen Sie aber eine Verbindung zum KDC haben und 2FA/MFA ist so auch erst einmal nicht vorhanden.

Die meisten Firmen haben aber schon einen Microsoft 365 Mandanten und damit auch eine Anmeldung an Entra ID für ihre Benutzer eingerichtet. Die meisten Benutzer sind mit ihrem Client auch schon authentifiziert und dies ist unabhängig von einer NT-Domain. EntraID Joined und Entra-ID Konto samt Primary Refresh Token (PRT) reichen aus aus. Ihre Applikation muss nur ihren Anwender beim Anonymen Zugriff zu Entra ID senden und den Rücksprung als "Form-Post" annehmen, decodieren und verifizieren. Aber dann hat ihre Software ein OAUTH-Token und kann den Anwender identifizieren. Alle Funktionen wie Multi Faktor, Login-Protokollierung, Benutzerverwaltung etc. nimmt ihnen Entra ID ab.

Für die Einrichtung brauchen wir nur wenige Dinge:

  • AppRegistration in Entra ID
    Damit definieren unsere eigene Applikation als Token-Empfänger und könnten über Claims sogar weitere Details ermitteln wie z.B.
  • Enterprise App in EntraID
    Zur App wird auch eine Enterprise App angelegt, über welche wir z.B. die Berechtigungen für Benutzer und Gruppen steuern können.
  • Eine Webseite zur Umleitung
    Ich habe mir einfach eine "Index.htm"-Seite angelegt, die einen 302-Redirect zu Entra ID mit der passenden Payload
  • Eine Webseite für den Rücksprung
    Entra-ID wird nach der Anmeldung diesen wieder mit einem HTTP-POST zur definierten URL schicken. Dort wartet eine HTML-Seite, genauer eine PH-Seite", die einfach nur die übertragenen Daten decodiert und ausgibt.

Dies ist also eher eine Beispielanwendung, um die Anwendung und Funktion zu testen und nicht für die Produktion. Insbesondere die Verifizierung, Schutz gegen DoS-Attacken, Buffer Overflow etc. sind alle nicht implementiert und dazu sollten sie fertige bewährte Pakete nutzen und nicht selbst sich an der Decodierung versuchen.

App Definition in Entra

Zuerst lege ich eine  App Registration in meinem Tenant an. Auf "https://portal.azure.com" gehe ich in den Bereich der App Registrations und addiere eine neue App.

Die App braucht natürlich einen Namen und hier kann ich schon auswählen, ob ich eine "Single Tentant Only"-App für meinen Tenant oder sogar eine App für mehrere Tenants bereitstellen möchte. Darauf gehe ich später noch ein. Zudem muss ich die Plattform, z.B. "Web", auswählen und eine Rücksprung-Adresse mitliefern. Das ist die URL, zu der ein WebClient nach der Anmeldung zurückkehrt und sein Token per POST übermittelt

Nachdem die App angelegt werden, muss ich für meinen "SimpleSAMLSample nichts mehr einrichten. Allerdings muss ich neben der Redirect-URI noch die AppID und die TenantID aus der Übersicht abrufen:

Diese brauche ich im nächsten Schritt./p>

Webseite zur Umleitung zu Entra

ZZuerst dachte ich an eine einfache HTML-Seite mit einem 302 Redirect. Aber das wäre zu wenig, wenn ich muss den Client per HTTP-POST zu Entra ID Umleiten und zumindest die GUID der Applikation und die Rücksprungadresse mitgeben, welche in der App registriert ist. Das ist wichtig, damit sich nicht jemand anderes als diese App bösartig ausgeben kann. Ich habe mir dazu folgende kleine PHP-Seite gebaut. (Ok. die KI hat etwas mitgeholfen).

<?php
$tenant = "604d9047-44e5-443a-ad8f-98abe5748b0a";
$appId  = "4099d059-adaf-4ee1-aa79-af1b961d180b";
$ssoUrl = "https://login.microsoftonline.com/$tenant/saml2";
$acs = "https://www.msxfaq.de/test/saml/acs.php";

$xml = <<<XML
<samlp:AuthnRequest
 xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol"
 ID="_123456"
 Version="2.0"
 IssueInstant="2026-06-25T12:00:00Z"
 ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
 AssertionConsumerServiceURL="$acs">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"><![CDATA[$appId]]></saml:Issuer>
</samlp:AuthnRequest>
XML;

$url = $ssoUrl . '?SAMLRequest=' . urlencode(base64_encode(gzdeflate($xml)));
header('Location: ' . $url, true, 302);
echo "Redirecting to SSO URL: $url";
exit;

Der Code ist schnell erklärt aber da am Ende ein "header(Location:..." steht, darf nichts davor eine Ausgabe erzeugen, da ansonsten PHP keinen Header mehr schreiben kann. Eine einfache Leerzeile kann zu viel sein und achten Sie beim Speichern als Unicode-Datei, dass kein BOM davor geschrieben wird.

Oben sind die GUIDs der Applikation und des Tenant, die Entra ID SSO-URL und auch die Rücksprungadresse hinterlegt.

Mit den Werten können Sie nicht anfangen, denn Entra ID prüft, ob die von ihrer Applikation angeforderte RedirectURI zur Definition bei der App passt. Sie können selbst keine App mit der gleichen GUID in Entra anlegen und wenn Sie die Daten hier verwenden, dann springen ihre Clients zu meinem Tenant zurück, wenn die Benutzer denn entsprechend definiert wären.

Aus den vier Daten baut PHP eine XML-Datei zusammen, die dann erst mit gzdeflate komprimiert dann BASE64-Encoded und zuletzt noch URLEncoded wird. Daraus wird dann die eigentliche URL ,welche der Client per Redirect im Header zur Umleitung bringt.

Anmeldung bei Entra ID

Der Client springt zu  "$ssoUrl = "https://login.microsoftonline.com/<GUID des Tenant>/saml2" und Entra ID decodiert die XML-Struktur. Wenn der Anwender schon angemeldet ist. dann wird Entra ID ein OAUTH-Ticket erstellen und dem Client in der Form übergeben, damit der Client diese an die "Redirect-URL" mittels Post zurücksendet. Wenn der Anwender noch nicht authentifiziert ist, dann startet die ganz normale Entra ID Anmeldemaske.

Webseite mit Redirect Processing

Der Client kommt in meinem Beispiel auf die ACS-URL unter der Adresse "https://www.msxfaq.de/test/saml/acs.php" zurück und hat als Payload ein POST mit dem Feld "SAMLResponse" Am Anfang der Seite steht wieder de PHP-Code, der den Inhalt von "SAMLResponse" ohne weitere Verifikation auseinandernimmt, decodiert und dann in die entsprechenden Felder der HTML-Vorlage einbindet.

<?php

if (!isset($_POST['SAMLResponse'])) {
    die("Keine SAMLResponse empfangen.");
}

$base64 = $_POST['SAMLResponse'];

$xmlString = base64_decode($base64);

if ($xmlString === false) {
    die("Base64 konnte nicht decodiert werden.");
}

// XML laden
$dom = new DOMDocument();

if (!$dom->loadXML($xmlString)) {
    die("Ungültiges XML.");
}

// XML hübsch formatieren
$dom->preserveWhiteSpace = false;
$dom->formatOutput = true;

$xpath = new DOMXPath($dom);

$xpath->registerNamespace(
    "saml",
    "urn:oasis:names:tc:SAML:2.0:assertion"
);

$xpath->registerNamespace(
    "samlp",
    "urn:oasis:names:tc:SAML:2.0:protocol"
);

function getFirstValue(DOMXPath $xpath, string $query): string
{
    $node = $xpath->query($query)->item(0);
    return $node ? trim($node->textContent) : "";
}

?>
<!DOCTYPE html>
..... die Datei geht hier noch weiter

IIch habe ihnen die Datei hier als TXT-Datei zum Download bereitgestellt.

simple_saml_sample.zip

Beispiel mit Entra ID User im eigenen Tenant

Wenn alles wie erwartet funktioniert, dann gehe ich auf die URL https://www.msxfaq.de/test/saml und nach etwas "Geflacker" sehe ich die decodierten Daten:

Die Applikation bekommt ein OAUTH-Token des Anwender mit dem Displayname, Vorname, Nachname und Name (=UPN). Mein Code decodiert nur den POST aber prüft nicht die Signatur. Das sollten Sie natürlich nicht tun. Schauen Sie sich den Issuer an und prüfen Sie entsprechend das Token, ehe Sie dem Anwender den Zugriff anhand der gelieferten Identitätsinformationen liefern.

Beispiel mit anderen Benutzern

Nachdem die Anmeldung mit dem einzelnen eigenen Benutzer funktioniert hat, habe ich weitere Benutzer auf die Webseite gesendet.

Benutzer

Ergebnis

Beschreibung

Eigener Benutzer

OK

Den Fall habe ich schon beschrieben und hat gezeigt, dass die Funktion im Grunde gegeben ist

Gastbenutzer Entra ID

OK

Wenn ein Benutzer im Tenant der Application vorhanden ist, dann kann er sich problemlos anmelden und sendet sein SAML-Token an meine App

Gast als Microsoft Konto

OK

Wenn ein Microsoft-Konto (früher LiveID, Passport) als Gast eingeladen wurde, dann kann er sich ebenfalls anmelden.

Fremdes EntraID Konto

Error

Nutzt ein Anwender die Anmeldung an der App ohne ein Konto im Tenant der Application zu haben, dann kann er sich mit dem Fehler AADST50020 nicht anmelden.

Fremdes Microsoft-Konto

Error

Zuletzt habe ich noch ein Microsoft Konto (Consumer) verwendet, welches nicht im Tenant eingeladen wurde. Auch das war dann erfolglos. Anders als bei Entra ID habe ich aber keine Fehlernummer gesehen.

 

Solange mein Redirect die genaue AppID und die TenantID überliefert, kann Entra ID auch nur diesen Tenant befragen. Gibt es den Benutzer in diesem Tenant nicht, dann kommt eine Fehlermeldung.

In meinem Tenant sehe ich nur meine erfolgreichen Anmeldungen an den entsprechenden Konten aber nicht die fehlerhaften Anmeldungen durch Identitäten, die ich nicht kenne.

Multi Tenant

Wenn ich meine App aber auch anderen Tenants ohne vorherige Einladung der User als "Gast" zur Verfügung stellen will, dann muss ich diese etwas anpassen. Es gibt dazu aus meiner Sicht zwei Wege:

  • Eigene App je Tenant
    Ich kann den Admin deren anderen App bitte, eine App Registation anzulegen nur mir die AppID und TenantID zu übergeben. Wenn der Benutzer sich dann an meiner Webseite meldet, dann müsste ich den Benutzer um seinen Anmeldenamen bitten, um dann den Redirect zur richtigen AppID und TenantID zu machen. Das ist einfach aber skaliert nicht wirklich und ist für den Anwender zumindest bei der ersten Anmeldung kein "Single SignOn". Diese Option beschreibe ich daher nicht weiter.
  • Multi Tenant App
    Viel besser ist es, wenn ich die App in meinem Tenant für viele andere Tenants erreichbar mache. Das muss ich nur entsprechend definieren. Diese Einstellungen schaue ich mir im weiteren an.

Ich muss dazu im Entra ID Portal meine App von "Single Tenant" auf "Multi Tenant" in der gewünschten Ausprägung umstellen.

Wer bei der Einrichtung einer neuen App aufgepasst hat, konnte dort auch schon die Einstellmöglichkeit sehen. Wenn die Anwendung schon eingerichtet ist, dann können Sie die Konfiguration in der Application Registration unter "Authentication - Supportet Accounts" umstellen.

 

Manchmal kommt eine Fehlermeldung, das dies nicht möglich ist, das der Parameter "requestedAccessTokenVersion" nicht korrekt gesetzt ist. Diesen können Sie im Manifest dann z.B. auf "2" setzen.

Check the Application Manifest: Inspect the application manifest or the update payload to ensure that the api.requestedAccessTokenVersion property is set to 1, 2, or null. If you don't need to change it, you can omit this property from the update.

Die Lösung ist dann recht einfach:

Wenn signInAudience auf AzureADandPersonalMicrosoftAccount festgelegt ist, muss der Wert 2 lauten.
Microsoft Entra-App-Manifest (Azure AD Graph-Format). https://learn.microsoft.com/de-de/entra/identity-platform/reference-app-manifest

Danach können Sie dann auch den Wert für "signInAudience" auf  "AzureADandPersonalMicrosoftAccount" stellen. Wenn sie auf LiveID-Konten verzichten können, dann können Sie sogar eine Allow-Liste für Tenants pflegen.

So bekommen dann nur Benutzer ein Token, welche im richtigen Tenant sind.

Achtung:
Das sichert ihre App nicht da vor, dass Angreifer mutwillig falsch formatierte HTTP-POST-Requests an ihre Applikation senden. Die Eingangsseite muss also hier "sicher" und "schnell" sein, um solche Angriffe korrekt zu versarbeiten, zu verifizieren und schnell abzublocken.

Testen Sie daher ihre App zuerst als "Single Tenant App" in ihrem eigenen Tenant, ehe Sie fremde Konten zur Authentifizierung erlauben. Ich muss dann aber auch meine App noch anpassen, den die SSOUrl darf nicht mehr die Tenant-ID enthalten, in der Entra ID dann nach den Benutzern und Apps sucht.

 // URL für einen bestimten Tenant
$ssoUrl = "https://login.microsoftonline.com/$tenant/saml2";

// URL für alle Tenants und LiveIDs
$ssoUrl = "https://login.microsoftonline.com/common/saml2";

// URL für alle Tenants ohne LiveIDs
$ssoUrl = "https://login.microsoftonline.com/organizations/saml2";

// URL nur für LiveIDs
$ssoUrl = "https://login.microsoftonline.com/consumer/saml2";

Wenn ein Anwender aus einem anderen Tenant nun ihre App verwendet, dann wird er zuerst um seine "Zustimmung" (User Consent) gefragt. Ich warte allen Administratoren aber diese Möglichkeit zu sperren. (Siehe Checkliste Tenant Einrichtung). Benutzer sollten nicht eigenverantwortlich einer App die Berechtigungen auf von ihnen erreichbare Daten gewähren können. Anwender dürfen in der Regel ja auch keine eigenen Programme auf ihrem PC installieren. Sie können aber in Entra ID einen Prozess einrichten, damit Benutzer eine neue App beantragen können.


https://portal.azure.com/#view/Microsoft_AAD_IAM/ConsentPoliciesMenuBlade/~/AdminConsentSettings

Alternativ können sie eigene interne Prozesse definieren. Die Freigabe einer AppID ist einer Anforderung zur Installation einer neuen lokalen Software vergleichbar. Als Administrator können Sie über die URL mit der AppID als ClientID dies einfach erreichen. Für meine SAMLTEST-App wäre dies dann

https://login.microsoftonline.com/common/adminconsent?client_id=4099d059-adaf-4ee1-aa79-af1b961d180b

Das "Unverified" sollte sie bei kommerziellen Applikationen alarmieren. Jede Firme, die etwas auf sich hält, wird seine Domain über das Microsoft Partner Programm verifizieren. Geben Sie bitte nie einer App Rechte, die sie nicht kennen.

Nachdem ich die "richtige" SAML2-URL aufrufe und der Admin oder User vorher seinen "Consent" erteilt hat, konnte ich auch Identitäten anderer Tenants und sogar Microsoft Konten zur Anmeldung nutzen.

Hinweis: Ich kann als Webseitenbetreiber hier nicht heimlich im Hintergrund einen Besucher zu einem Aufruf der Entra ID-URLs verleiten um ihn aus seiner Anonymität zu zu holen. Das geht nur, wenn es einen Consent zu meiner App gibt. Ansonsten bekommt der Benutzer die Anfrage.
Wenn Sie also eine Webseite besuchen, auf plötzlich eine Consent-Abfrage kommt, dann sollten Sie skeptisch werden

Anmeldelog im Usertenant JA
Anmeldelog im Apptenant JA

Anmeldeprotokolle

Wenn ein Anwender des Tenant A ein Ticket für eine Applikation in Tenant A anfordert, dann hinterlässt dies eine Spur in den Sign-in Logs. Aber das gilt auch für Anwender aus anderen Identity-Systemen, d.h. anderen Tenants als auch Microsoft-Konten. Hier schaue ich immer erst einmal in die Enterprise Applikation zur App.

Sie sehen hier alle Anwender, die sich an der App authentifiziert haben. Ich habe hier alle drei Klassen (Same Tenant, anderer Tenant, Microsoft Konto) dabei. Im Sign-In-Log des User-Tenants sehe ich die Anmeldung meiner Anwender an einer entsprechenden App.

So kann ich als Administrator erkennen, welche Apps die Benutzer verwenden. Im Anmeldeforest gibt es die Enterprise App, nachdem jemand "Consent" gewährt hat.

Provisioning mit SAML-Token

Die Identität und Authentifizierung ist im Anmeldetenant hinterlegt. Ihre Applikation muss natürlich auch die Anwender "kennen" und viele Applikationen speichern zum Anwender auch Rollen und Einstellungen ab. Wenn Sie die Konten in der Anwendung nicht manuell pflegen wollen, dann sollten Sie einen irgendwie gearteten Verzeichnisabgleich überlegen, damit sie Änderungen bei Benutzern und Gruppen zeitnah mitbekommen. Ihre Applikation kann dazu z.B. per Graph auf ihr Entra ID oder per SCIM von Entra ID über Änderungen informiert werden. Umgekehrt könnten Sie natürlich auch mit eigenen Tools die Informationen in die jeweilige Applikation übertragen

Als "Lite"-Lösung kann ihre Applikation natürlich auch einfach die Informationen aus dem SAML-Token verwenden. Dort steht zumindest schon einmal der Anmeldename und Displayname drin. Über Custom Claim Rules und entsprechende Anforderungen kann ihr App natürlich noch weitere Felder anfordern, die sie dann verwenden können. Ohne Verzeichnisabgleich bekommen Sie aber nie zuverlässig mit, wann ein Benutzer z.B. nicht mehr aktiv ist oder sich der UPN geändert hat. Ihre App würde dann Altlasten weiter mit sich führen und bei Umbenennungen (Heirat/Scheidung) einen neuen doppelten Benutzer anlegen, wenn Sie nicht die Werte aus TenantID und ObjectID des SAML-Token als primären Schlüssel nutzen.

Aber es ist denkbar und nutzbar und als Dienstleister, der nach angelegten Benutzern bezahlt, hätten Sie zumindest kurzfristig sogar einen Vorteil. Zumindest bis der Kunde den Benutzer löscht oder das Konto aufgrund Inaktivität gelöscht wird. Das ist aber knifflig, wenn am Benutzer auch Daten hängen. 

Selbst ausprobieren

Ich lasse die PHP-Testseite aktuell noch bereitgestellt. Ihr könnte gerne die URLs entsprechend aufrufen und nach Gewährung des Consent sehr ihr auch euer decodiertes Token.

Hinweis:
Ich behalte mir vor, bei Missbrauch die Seiten ohne Vorankündigung vom Netz zu nehmen. Ich protokolliere die Aufrufe und auch die UPNs der SAML-Tokens. Wenn Sie dies nicht möchten, dann nutzen Sie den Download der Quellen auf dieser Seite und richten diese in ihrem eigenen Webhosting ein

Folgende URLs funktionieren wie folgt. Sie nicht "anklickbar". Kopieren Sie die URL und tragen diese in die Adressleiste in ihrem Browser ein.

https://www.msxfaq.de/test/saml/index1.php - Single Tenant und sollte nur mit Konten aus msxfaqdev.onmicrosoft.com funktionieren
https://www.msxfaq.de/test/saml/index2.php - Multitenant und Microsoft Konto (LiveID/Passport).
https://www.msxfaq.de/test/saml/index3.php - Multitenant ohne Microsoft Konto (LiveID/Passport)
https://www.msxfaq.de/test/saml/index4.php - nur Microsoft Konto (LiveID/Passport) und sollte mit jedem Entra ID Konto funktionieren

Die erste URL mit index1.php ist für ihren Test wertlos aber die weiteren drei URLs sollten funktionieren.

Weitere Links