Czasami pisząc aplikację claims-aware musimy zaktualizować dane, które są przechowywane w Claims. Takich przypadków może być wiele, na przykład użytkownik zaktualizował imię czy też nazwisko, a może adres. Jeżeli nie zaktualizujemy Claims a polegamy na nich to przy odwołaniu się do wartości danego claim otrzymamy błędne informacje – stare. Może to prowadzić do pewnych komplikacji i błędów.

Normalnie, przy prostej aplikacji wystarczy tak naprawdę zaktualizować cookie przechowujące federation authentication (FedAuth) – czyli informacje o tym kim jesteśmy i jakie dane za sobą niesiemy.

Aktualizację możemy wykonać poprzez usunięcie session cookie:

FederatedAuthentication.SessionAuthenticationModule.SignOut();

Przy usunięciu cookie, odświeżenie strony lub kolejny request do strony spowoduje odwołanie się do naszego STSa w celu pobrania danych. Nie zostaliśmy wylogowani z STS gdyż usunęliśmy tylko lokalne cookie, a nie cookie od STS, więc użytkownikowi będzie miał po prostu page refresh z jumpem pomiędzy dwoma stronami. Może niezbyt fajne rozwiązanie, ale… działa poprawnie, i dane są odświeżone.

Wszystko jest ok, do póki mamy jedną aplikację. A co jeżeli nasz użytkownik otwiera dwie aplikacje które hostujemy, w jednej zmienia dane, a w drugiej wypełnia różne formularze które automatycznie ustawiają dane z Claims? Nasze lokalne odświeżenie session cookie nie będzie miało wpływu na session cookie w drugiej aplikacji. Czyli natrafiamy na problem który jest ciężki do przejścia.

Co więc możemy zrobić? Czy da się coś zrobić?

Mamy kilka opcji, wszystko zależy od tego jaką mamy sytuację i na co się biznes zgodzi.

Opcja Pierwsza

Jedna opcja to po prostu przy zmianie danych claims spowodować federated signout – spowoduje to wylogowanie kompletne użytkownika przez co pierwszy request poprosi go o ponowną identyfikację. To nie jest takie złe przy Google provider, czy też AD – w tych przypadkach to w jednym użytkownik musi kliknąć w ikonkę googla, a w drugim automatycznie przez kerberos token jest logowany. Jeżeli jednak używamy na przykład Thinktecture IdentityServer (v2) jako idp to już mamy problem, gdyż użytkownik będzie musiał na nowo podać swoją nazwę użytkownika i hasło. Więc rozwiązanie takie piękne nie jest i biznes się na to może nie zgodzić.

PS taki federated signout można zrobić w ten sposób:

var fam = FederatedAuthentication.WSFederationAuthenticationModule;

// kasuje cookie
fam.SignOut(isIPRequest: false);
var signout = new SignOutRequestMessage(new Uri(fam.Issuer), returnUrl);
return Redirect(signout.WriteQueryString());

Opcja Druga

Druga opcja uzależniona jest od tego z jakiego STSa korzystamy, jeżeli z AD FS to opcja nie istnieje ;) jeżeli z IdentityServer to możemy go lekko zmodyfikować. Tak by on obsługiwał opcję numer trzy o czym zaraz będzie. Chodzi mianowicie o to, że STS zgodnie z WS-Federation powinien przechowywać informacje na temat realms do których on wydał auth token. Czyli jeżeli korzystamy z Identity Server jako główny STS to on będzie miał informację, że user aktualnie korzysta z dwóch aplikacji plus linki do tych aplikacji. To samo robi AD FS, ale problem z AD FS jest taki, że nie możemy zbytnio zaktualizować tam kodu ;) a w Identity Server nie mamy takiego problemu. No ok, to opcja trzecia która jest z drugą częściowo połączona – i pewnie do drugiej wrócę ;)

Opcja Trzecia

Opcja trzecia to wykonanie przez naszą aplikację wylogowania użytkownika ze wszystkich portali do których użytkownik może mieć dostęp. Może to się wydawać skomplikowane ale takie nie jest. To co robi AD FS/IdentityServer kiedy wykonujemy federated signout, to tworzony on iframes dla każdego realm do którego user jest zalogowany (STS ma wiedzie gdzie jest).

Czyli jeżeli wykonamy request do na przykład AD FS:

https://server/adfs/ls/?wa=wsignoutcleanup1.0

AD FS stworzy nam ukryte iframes (iframes by rquest był wykonywany w kontekście użytkownika i by nie próbować tworzyć server side requestów w kontekście danego użytkownika, bo to już jest bardziej skomplikowane a i tak by sesji pewnie nie usuneło) do naszych portali:

<iframe style="visibility: hidden; width: 1px; height: 1px;" src="https://app_one/?wa=wsignoutcleanup1.0" id="signoutFrame" class="signoutFrame"></iframe>

<iframe style="visibility: hidden; width: 1px; height: 1px;" src="https://app_two/?wa=wsignoutcleanup1.0" id="signoutFrame" class="signoutFrame"></iframe>

Te requesty (zawierające query string: wa=wsignoutcleanup1.0 lub wa=wsignout1.0) do naszych aplikacji zostaną przechwycone przez bibliotekę System.IdentityModel.Services, która zrobi dosłownie to samo co zrobiliśmy wcześniej – usunie FedAuth cookie i zwróci obrazek:

WA Signout request response

Czyli my możemy zasymulować podobną funkcjonalność u nas. Wymaga ona jednak wiedzy od aplikacjach. Nie jest to eleganckie ale czasami jest to niezbędne :( Taką wiedzę możemy przekazać w Claims lub po prostu za pomocą jakiegoś nuget (ekhm, wiem wiem, ale jest to jakaś opcja).

Więc wystarczy, że w aplikacji która jest odpowiedzialna za odświeżanie danych użytkownika, spowodujemy wygenerowanie takich linków do iframes jak wyżej powyższy przykład i po załadowaniu się wszystkich iframes wykonamy page refresh, który zrobi skok do STSa, pobierze nowe dane i wróci do aplikacji. Jeżeli mieliśmy dwie web apki otwarte, to kolejny request lub odświeżenie strony w drugiej apce spowoduje skok do STSa w celu pobranie auth token – jednym słowem mamy refresh bez wymuszenia podawania hasła użytkownika.

Opcja Druga po raz kolejny

Teraz wracając do opcji drugiej i IdentityServer, to co możemy zrobić to dosłownie napisać handler który przechwyci nasz request do STSa i na ?my=refresh zrobi dosłownie to samo co my ale wykorzystując wiedzę STS na temat realm do których token został przekazany.

Podsumowanie

Tak jak opcja z modyfikacją STSa mi się podoba, tak też nie zawsze można ją wykonać :( my skończyliśmy na tym, że aplikacja odpowiedzialna za zmianę wartości claims, tworzy iframes do wszystkich portali do których user ma dostęp. Listę portali znamy, bo ta sama aplikacja służy do zarządzania dostępem do tych protali. Wieć mieliśmy ułatwioną robotę. Jednak dziwie się, że specyfikacja WS-Federation nie brała pod uwagę możliwości odświeżenia claims we wszystkich aplikacjach na raz inaczej niż przez signout.

Może to się zmieni, bo nie zależnie która opcja mi się podoba, nie podoba mi się, że muszę odwoływać się do takich sztuczek :(

A jak wy obsługujecie takie przypadki? Mieliście w ogóle taki problem?

1 KOMENTARZ

Comments are closed.