Laden...

Claims-based Authentifizierung, wie WindowsIdentity mittels Claims übertragen?

Erstellt von carom vor 12 Jahren Letzter Beitrag vor 12 Jahren 2.527 Views
C
carom Themenstarter:in
90 Beiträge seit 2008
vor 12 Jahren
Claims-based Authentifizierung, wie WindowsIdentity mittels Claims übertragen?

Hallo!

Einerseits will ich nicht lange um den Brei herum reden, euch andererseits aber auch nicht im Dunkeln lassen - deswegen eine kurze Vorgeschichte:

Eine Intranet-Anwendung (.NET), bei welcher bisher Single Sign-On möglich war durch auslesen von Nutzername und den dazugehörigen Gruppen, soll in die Azure-Cloud portiert werden. Da die Cloud entfernt ist und der Domänencontroller somit wegfällt, kann man ja nicht mehr direkt das Active Directory zur Single Sign-On Authentifizierung benutzen.

Die Rechteverwaltung läuft bisher innerhalb der Anwendung ab, das war die Vorgabe. Die Anwendung holt sich ein WindowsIdentity-Objekt, holt sich daraus den Namen des angemeldeten Users inklusive seiner Gruppen, prüft gegen eine XML-Datei ob es sich um User X handelt oder der User zumindest in Gruppe Y ist, und falls alles passt gibt's grünes Licht.
Wenn ich in der Cloud weiterhin auf das WindowsIdentity-Objekt setzen könnte, dann müsste ich das Programm nur an einer Stelle ändern, nämlich dort wo das Objekt "beschafft" wird.

Diese Beschaffung ist jetzt aber nicht mehr ganz so einfach. Microsoft empfiehlt für Authentifizierung mittels Active-Directory für entfernte Anwendungen ja die Claims-based Authentifizierung. Der AD FS 2.0 holt sich die benötigten Infos aus dem AD und verpackt sie in ein Token, genauer in Claims. Das wird dann verschickt.

Und jetzt zu meinem Anliegen:

Diese Claims sehen für mich wie simple Key-Value-Paare aus. Da kann man keine "richtigen" Objekte reinstecken, sondern nur Strings, welche auch noch einem vordefinierten Typ entsprechen müssen (Role, Name, Email etc.).

In der Cloud kommt man jetzt ja ziemlich einfach an die Claims:


IClaimsPrincipal icp = Thread.CurrentPrincipal as IClaimsPrincipal;
IClaimsIdentity ici = icp.Identity as IClaimsIdentity;
foreach (Claim c in ici.Claims) //...

Frage: muss ich jetzt den Usernamen und seine Gruppen manuell in Claims verpacken (naja gut, das würde der AD FS für mich machen) und auf der anderen Seite in der Cloud wieder manuell zusammenbauen? Kann ich überhaupt ein WindowsIdentity-Objekt einfach so zusammenbauen aus den Claims?

Da IClaimsPrincipal ja einfach nur IPrincipal erweitert habe ich gehofft, man könnte auf der Cloud-Seite auf die Claims verzichten und folgendes machen:


System.Security.Principal.IPrincipal ip = icp;
WindowsIdentity wi = icp.Identity as WindowsIdentity;

Das Objekt ist dann aber null, scheint nicht zu gehen.

Das ganze nochmal in kurz: ich will per Claims-based authentification Nutzername+Gruppen in die Azure-Cloud verschicken, hätte dort am liebsten ein WindowsIdentity-Objekt, hänge jetzt aber fest.

Zu allem kommt noch hinzu, dass ich im Intranet momentan noch keinen AD FS 2.0 zur Verfügung habe. Folglich müsste ich mir zum Testen einen lokalen STS aus dem Visual Studio zusammen bauen. Da ist ja nicht viel mit claims zusammenklicken, sondern es muss gecodet werden:


outputIdentity.Claims.Add(new Claim(ClaimTypes.X, Y));
outputIdentity.Claims.Add(new Claim(Claims.ClaimTypes.A, B));

Was müsste ich an dieser Stelle alles angeben um 1:1 das Verhalten des AD FS 2.0 nachzubilden, wenn dieser User + Gruppen verschicken soll?

Ich hoffe dass man mein Geschreibsel einigermaßen versteht, wenn nicht, dann natürlich fragen 😃

Vielen Dank!

G
538 Beiträge seit 2008
vor 12 Jahren

Du kannst "Name" und "Role" übermitteln, dann macht dir die WIF Runtime ohne weiteres zutun ein Principal daraus, welches du direkt über Thread.CurrentPrincipal abfragen kannst.

Dort kannst du dann auch IsInRole nutzen - bzw. alles was sonst so aus Principals und Permissions besteht (Also auch PrincipalPermission.Demand).

Zur eindeutigen Identifizierung eignen sich UPN (als Name) und TokenGroups (als Role). Für letzteres muss der ADFS übrigens TokenGroupsGlobalAndUniversal auswerten dürfen.
Du kannst auch andere Claims verwenden und der WIF Runtime mitteilen in welchen Claims deine Rollen und Namen verpackt sind.

Versuche deine Claims klein zu halten (also möglichst wenig Daten zu übermitteln), da sonst Safari (zumindest) auf Mac Probleme macht.
Die Cookies sind nämlich pro Domain schnell mehr als 4KB und Apple hat da leider eine Grenze gesetzt.

Wenn du eine Windows Identity haben wollen würdest, dann hilft die übermittlung der UPN und der ClaimsToWindowsTokenService (C2WTS) - das klappt aber nur dann, wenn die Anwendung deinen DC kontaktieren kann. (Stichwort: Constrained Delegation und UPN Logon).
Wenn du impersonieren musst, kommt ein "ActAs" Token für dich in Frage.

Im übrigen ist "StarterSTS" ein super Tool, um mal flott ein paar Claims zu testen.

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)

C
carom Themenstarter:in
90 Beiträge seit 2008
vor 12 Jahren

Vielen Dank für die Antwort 😃

Du kannst "Name" und "Role" übermitteln, dann macht dir die WIF Runtime ohne weiteres zutun ein Principal daraus, welches du direkt über Thread.CurrentPrincipal abfragen kannst.

Das sieht auf den ersten Blick sehr gut aus, funktioniert soweit.
Allerdings: sehe ich das richtig, dass man keine Möglichkeit hat, an die Gruppen des Benutzers zu kommen? Also klar kann man mit isInRole() die Mitgliedschaft in der jeweils als Parameter übergeben Gruppe prüfen, aber man kommt nicht an eine "Liste" mit allen Gruppen auf einen Schlag.

Da dies vorher mit dem WindowsIdenitity-Objekt möglich war, ist mein restlicher Code blöderweise daran angelehnt, die Gruppen zu kennen.
Überlege jetzt, eine eigene User-Klasse zu bauen, welcher der IClaimsPrincipal zu Beginn übergeben wird. Die Klasse könnte sich die Gruppen und den Usernamen dann aus den Claims holen.

Was ist davon zu halten? Hätte es irgendwie so gemacht:

public AzurePrincipal(ClaimCollection claims) 
        {
            groups = new List<string>();
            foreach (Claim c in claims)
            {
                if (c.ClaimType == ClaimTypes.Name)
                {
                    name = c.Value;
                }
                else if (c.ClaimType == ClaimTypes.Role)
                {
                    groups.Add(c.Value);
                }
            }
        }

Natürlich nur schnell hingeschrieben ohne Prüfung auf Inhalt etc.
Was mich bei isInRole auch noch stört: in meinen Versuchen hat es sich als case-sensitive herausgestellt, was früher oder später sicher Ärger macht.

Versuche deine Claims klein zu halten (also möglichst wenig Daten zu übermitteln), da sonst Safari (zumindest) auf Mac Probleme macht.
Die Cookies sind nämlich pro Domain schnell mehr als 4KB und Apple hat da leider eine Grenze gesetzt.

Danke für den Hinweis!
Das StarterSTS-Tool werde ich testen.

G
538 Beiträge seit 2008
vor 12 Jahren

Thread.CurrentPrincipal müsste dir eigentlich alles liefern, was du benötigst.
Alternativ zu IsInRole kannst du auch die PrincipalPermission-Klasse benutzen - die kann auch auf Rollen prüfen (und einiges mehr).

Alle Rollen in einem Claim wirst du im übrigen nicht so direkt bekommen, aber


static class MyExtensionClass {
  public static IEnumerable<String> Roles(this ClaimsCollection claims)
  {
    claims.Where(c => c.ClaimType == ClaimTypes.Role).Select(c => c.Value);
  }
}

(ungeprüft) dürfte so funktionieren.

In ADFS kannst du Windows-Gruppen "umschreiben" auf neue Namen, so übermittelst du wenig Claims und das mit dem Case-Sensitive kannst du einfach mit dir selbst ausmachen 😉

Der Vorteil der Klugheit liegt darin, dass man sich dumm stellen kann - umgekehrt ist das schon schwieriger (K. Tucholsky)
Das Problem mit Internet-Zitaten ist, dass sie oftmals zu unrecht als authentisch angenommen werden. (K. Adenauer)