Introductie
Ik bouw een custom implementatie van Identity 2.0. Standaard refresht het framework de identity van de ingelogde user elke 30 minuten, door een nieuwe ClaimsIdentity aan te maken. Omdat ik een aantal custom claims heb (die ik tijdens het inloggen set) wil ik deze claims overhevelen naar de nieuwe ClaimsIdentity wanneer die gerefreshed wordt. Daarvoor heb ik een manier bedacht die de huidige ClaimsIdentity uit de HttpContext leest en die teruggeeft als een nieuwe ClaimsIdentity in the RegenerateIdentity method. Het probleem is echter dat HttpContext.Current null is tijdens dat proces, dus ik kan m'n huidige claims niet overhevelen.
De code
In mijn startup.cs file heb ik deze code die de gebruiker zijn identity elke 0.5 minuut refresht:
De RegenerateIdentity Func roept deze method aan in mijn UserManager:
Het probleem
De eerste keer dat ik inlog wordt CreateIdentityAsync aangeroepen. HttpContext.Current heeft dan gewoon een waarde en de identity wordt netjes aangemaakt. Wanneer CreateIdentityAsync echter nog een keer wordt aangeroepen (omdat de identity wordt gerefreshed) is HttpContext.Current null. Ik snap niet echt waar het fout gaat.
Mijn theorieën
Ik bouw een custom implementatie van Identity 2.0. Standaard refresht het framework de identity van de ingelogde user elke 30 minuten, door een nieuwe ClaimsIdentity aan te maken. Omdat ik een aantal custom claims heb (die ik tijdens het inloggen set) wil ik deze claims overhevelen naar de nieuwe ClaimsIdentity wanneer die gerefreshed wordt. Daarvoor heb ik een manier bedacht die de huidige ClaimsIdentity uit de HttpContext leest en die teruggeeft als een nieuwe ClaimsIdentity in the RegenerateIdentity method. Het probleem is echter dat HttpContext.Current null is tijdens dat proces, dus ik kan m'n huidige claims niet overhevelen.
De code
In mijn startup.cs file heb ik deze code die de gebruiker zijn identity elke 0.5 minuut refresht:
C#:
1
2
3
4
5
6
7
8
9
10
| app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, Provider = new CookieAuthenticationProvider { OnValidateIdentity = SecurityStampValidatorExtensions.OnValidateIdentity( validateInterval: TimeSpan.FromMinutes(0.5), regenerateIdentity: (manager, user) => manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie)) } }); |
De RegenerateIdentity Func roept deze method aan in mijn UserManager:
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| public async override Task<ClaimsIdentity> CreateIdentityAsync(ApplicationUser user, string authenticationType) { ClaimsIdentity claimsIdentity; if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity != null && HttpContext.Current.User.Identity.IsAuthenticated) { // Just return the existing ClaimsIdentity so we don't lose our custom claims claimsIdentity = (ClaimsIdentity)HttpContext.Current.User.Identity; // TODO refresh some claims from the database return claimsIdentity; } // Create a new ClaimsIdentity if none exists claimsIdentity = await ClaimsIdentityFactory.CreateAsync(this, user, authenticationType); claimsIdentity.AddClaim(new Claim(Constants.DefaultSecurityStampClaimType, await GetSecurityStampAsync(user.Id))); return claimsIdentity; } |
Het probleem
De eerste keer dat ik inlog wordt CreateIdentityAsync aangeroepen. HttpContext.Current heeft dan gewoon een waarde en de identity wordt netjes aangemaakt. Wanneer CreateIdentityAsync echter nog een keer wordt aangeroepen (omdat de identity wordt gerefreshed) is HttpContext.Current null. Ik snap niet echt waar het fout gaat.
Mijn theorieën
- Misschien wordt er door async iets op een andere thread uitgevoerd die geen beschikking heeft over de HttpContext? Ik heb een eigen awaiter geschreven om de HttpContext te kopiëren maar dat hielp niet, omdat de HttpContext kennelijk al leeg is in UseCookieAuthentication method van de startup.cs.
- De HttpContext bestaat nog niet of bestond wel maar is vernietigd door ASP.NET MVC of iets dergelijks? Niks kunnen vinden wat daar op duidt.
- De custom claims in de database op slaan. Ik sla ze echter liever niet op in de database. Ook gaat dat denk ik niet goed met expiration? In principe moeten de custom claims gebonden zijn aan een sessie en daarna expiren.
- De claims cachen in een in-memory cache. Het probleem daarbij is hetzelfde als bij het vorige punt: de claim moet niet alleen geassocieerd zijn met een user, maar ook met een sessie. Omdat je sessie ook opgevraagd wordt via de HttpContext gaat dat ook niet werken.
- De HttpContext over proberen te hevelen met een custom awaiter, maar dat werkt niet omdat de HttpContext in eerste instantie al leeg was.
- Aangezien van de ClaimIdentity een User object wordt gemaakt (je IUser<T> implementatie die door je store/manager wordt gebruikt) lijkt me dat je je claims ook over kunt hevelen naar dat User object. Ik heb echter geen idee hoe/waar ik dat zou moeten doen en of het gaat werken. Dat zou wel een mooie oplossing zijn; dan heb je overal waar je een User object hebt ook meteen je claims, zonder dat je die elke keer uit je ClaimsIdentity hoeft te halen.