[ASP.NET Identity] HttpContext.Current is null

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
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:

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.
Oplossingen die ik zelf heb bedacht (maar die niet werken)
  • 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.
Wie helpt mij verder?

Acties:
  • 0 Henk 'm!

  • GrooV
  • Registratie: September 2004
  • Laatst online: 07-08 15:13
Volgens mij zit HttpContext.Current niet meer in de nieuwe HttpContext

Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
Huh? Waar heb je het over?

Acties:
  • 0 Henk 'm!

  • GrooV
  • Registratie: September 2004
  • Laatst online: 07-08 15:13
Welke MVC versie gebruik je?

[ Voor 14% gewijzigd door GrooV op 03-12-2014 20:39 ]


Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
MVC 5 (of 5.1 / 5.2) met .NET 4.5.1.

Acties:
  • 0 Henk 'm!

  • GrooV
  • Registratie: September 2004
  • Laatst online: 07-08 15:13
Ik heb niet helemaal door wat je nu precies probeert te doen maar goed. Je HttpContext is niet meer beschikbaar zodra je buiten de asp pipeline gaat. Dit komt omdat je een nieuwe async thread aanmaakt die niet meer bij de httpcontext kan.

Kijk hier eens naar http://benfoster.io/blog/...-stripped-bare-mvc-part-1

En mijn eerste post sloeg op de nieuwe ASP.NET die geen HttpContext.Current meer heeft, zie https://github.com/aspnet...spNet.Http/HttpContext.cs

Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
GrooV schreef op woensdag 03 december 2014 @ 20:48:
Ik heb niet helemaal door wat je nu precies probeert te doen maar goed. Je HttpContext is niet meer beschikbaar zodra je buiten de asp pipeline gaat. Dit komt omdat je een nieuwe async thread aanmaakt die niet meer bij de httpcontext kan.
Ik probeer gewoon de claims in mijn identity object over te hengelen naar een nieuw identity object. Je identity object wordt namelijk elke x minuten ververst door het framework.
Ik zie daar niks wat te maken heeft met mijn probleem.
En mijn eerste post sloeg op de nieuwe ASP.NET die geen HttpContext.Current meer heeft, zie https://github.com/aspnet...spNet.Http/HttpContext.cs
Daar ben ik van op de hoogte, maar ik gebruik geen vNext. Dit gaat om een productie-applicatie (die ik natuurlijk niet ga baseren op een betaproduct). Dat viel ook wel af te leiden uit m'n topicstart aangezien ik spreek over Identity 2.0. vNext gebruikt 3.0 :)

Acties:
  • 0 Henk 'm!

  • DoDo
  • Registratie: Juli 2001
  • Laatst online: 20:54
Avalaxy schreef op woensdag 03 december 2014 @ 20:51:
[...]


Ik probeer gewoon de claims in mijn identity object over te hengelen naar een nieuw identity object. Je identity object wordt namelijk elke x minuten ververst door het framework.


[...]


Ik zie daar niks wat te maken heeft met mijn probleem.


[...]


Daar ben ik van op de hoogte, maar ik gebruik geen vNext. Dit gaat om een productie-applicatie (die ik natuurlijk niet ga baseren op een betaproduct). Dat viel ook wel af te leiden uit m'n topicstart aangezien ik spreek over Identity 2.0. vNext gebruikt 3.0 :)
toon volledige bericht
Zoals GrooV al heeft aangegeven heb je geen HttpContext meer in je async methode. Je kan de HttpContext meegegeven als parameter aan deze methode, maar beter nog maak je gebruik van SynchronizationContext.

Zie ook de volgende links:
http://stackoverflow.com/...angerous-because-of-async
MSDN: MSDN Magazine: Parallel Computing - It's All About the SynchronizationContext

[ Voor 10% gewijzigd door DoDo op 04-12-2014 12:28 ]


Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
Hmm, wat moet ik concreet veranderen aan mijn code dan?

Acties:
  • 0 Henk 'm!

  • Avalaxy
  • Registratie: Juni 2006
  • Laatst online: 23:11
Ik heb het nu even anders opgelost:

Ik heb mijn user object (ApplicationUser) een collectie van claims gegeven. Vervolgens ben ik in de code van OnValidateIdentity gedoken en hier een kopie van gemaakt (helaas, is een static method). In die OnValidateIdentity method kun je via context.Identity je bestaande claimsidentity ophalen, waarna een user object wordt aangemaakt. Daar kopieer ik dus mijn claims naar mijn user object, die weer naar de CreateIdentityAsync method worden gepassed. Daar kan ik ze vervolgens gewoon overkopiëren naar m'n nieuwe claimsidentity object.

Niet echt netjes, aangezien ik een copy+pasta+aanpassing heb gedaan van de interne Identity code, maar het is vooralsnog de enige oplossing die werkt.
Pagina: 1