Microsoft-identity-web: Can't set cookie events

Created on 8 May 2020  路  12Comments  路  Source: AzureAD/microsoft-identity-web

Which Version of Microsoft Identity Web are you using ?
Note that to get help, you need to run the latest version.
0.1.2-preview

Where is the issue?

  • Web App

    • [ ] Sign-in users

    • [X] Sign-in users and call web APIs

  • Web API

    • [ ] Protected web APIs (Validating tokens)

    • [ ] Protected web APIs (Validating scopes)

    • [ ] Protected web APIs call downstream web APIs

  • Token cache serialization

    • [ ] In Memory caches

    • [ ] Session caches

    • [ ] Distributed caches

Other? - please describe;

Is this a new or existing app?

I'm attempting to upgrade an existing app that uses MicrosoftAccount API (i.e. AddMicrosoftAccount()). I've repro'd the issue in the sample 2-1-Call-MSGraph.

Repro

Add this code in ConfigureServices before AddSignIn():

            services.ConfigureApplicationCookie(options =>
            {
                options.ExpireTimeSpan = TimeSpan.FromHours(1);
                // TODO: MAKE COOKIE EXPIRE BEFORE ACCESS TOKEN
                options.Events = new CookieAuthenticationEvents
                {
                    OnSigningIn = async (context) =>
                    {
                        var identity = context.Principal.Identity as ClaimsIdentity;

                        var userId = context.Principal.Claims.FirstOrDefault(claim => claim.Type == "preferred_username" || claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value;
                    }
                };
            });

            services.AddSignIn(Configuration);

Expected behavior
The OnSigningIn delegate should be called when a user logs in..

Actual behavior
In this case, the delegate is never called. If I use AddCookie() (as with MicrosoftAccount API), an exception "Scheme already exists: Cookies" is thrown at startup.

Additional context/ Logs / Screenshots
dotnet.txt

Answered In progress more info needed question web app

All 12 comments

@jennyf19 @pmaytak : here we'd want to chain the events as usual (keep the previous events, and call them before or after our own implementation.

@toddheckel I'm unable to repro this. Could you share all your code in ConfigureServices? Would like to see how you set up .AddMicrosoftAccount(...) Thank you.

I modified active-directory-aspnetcore-webapp-openidconnect-v2\2-WebApp-graph-user\2-1-Call-MSGraph\Startup.cs, which does not call AddMicrosoftAccount().

@toddheckel But you don't need to use AddMicrosoftAccount. MSA are supported directly by the Microsoft identity platform
just set the tenant to "common" and make sure the app registration allows for Work or school accounts and microsoft personal accounts.

@jennyf19 asked me how I setup .AddMicrosoftAccount() and I was pointing that the sample I used to repro the issue doesn't call that. The app registration I am using allows for Work or school accounts and personal accounts. I am able to log into the sample with my work account just fine, it just never calls the cookie callback. I am using a similar technique in my production service with MSAL just fine.

I am no longer calling AddMicrosoftAccount() in my service.

Thanks for the additional information @toddheckel. I think I misread the above info where you mention .AddMIcrosoftAccount(..) and thought you were adding it as well. Can you share the code in ConfigureServices? I did try the same sample with the code you provided above and was not able to repro.

Yes sorry I wasn't clear about that. Below is the complete function. The only other changes I have made are to appsettings.json to use my AAD info.

So you are seeing the callback get called?

```c#
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
// Handling SameSite cookie according to https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});

        services.AddOptions();

        services.ConfigureApplicationCookie(options =>
        {
            options.ExpireTimeSpan = TimeSpan.FromHours(1);
            // TODO: MAKE COOKIE EXPIRE BEFORE ACCESS TOKEN
            options.Events = new CookieAuthenticationEvents
            {
                OnSigningIn = async (context) =>
                {
                    var identity = context.Principal.Identity as ClaimsIdentity;

                    var userId = context.Principal.Claims.FirstOrDefault(claim => claim.Type == "preferred_username" || claim.Type == "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress")?.Value;
                }
            };
        });

        services.AddSignIn(Configuration);

        // Token acquisition service based on MSAL.NET
        // and chosen token cache implementation
        services.AddWebAppCallsProtectedWebApi(Configuration, new string[] { Constants.ScopeUserRead })
           .AddInMemoryTokenCaches();

        /*
           // or use a distributed Token Cache by adding 
                       .AddDistributedTokenCaches();

           // and then choose your implementation. 
           // See https://docs.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-2.2#distributed-memory-cache

           // For instance the distributed in memory cache
            services.AddDistributedMemoryCache()

           // Or a Redis cache
           services.AddStackExchangeRedisCache(options =>
                {
                    options.Configuration = "localhost";
                    options.InstanceName = "SampleInstance";
                });

           // Or even a SQL Server token cache
           services.AddDistributedSqlServerCache(options =>
            {
                options.ConnectionString = 
                    _config["DistCache_ConnectionString"];
                options.SchemaName = "dbo";
                options.TableName = "TestCache";
            });
        */
        // Add Graph
        services.AddGraphService(Configuration);

        services.AddControllersWithViews(options =>
        {
            var policy = new AuthorizationPolicyBuilder()
                .RequireAuthenticatedUser()
                .Build();
            options.Filters.Add(new AuthorizeFilter(policy));
        }).AddMicrosoftIdentityUI();

        services.AddRazorPages();
    }

```

@toddheckel Perhaps, can you try to replace
services.ConfigureApplicationCookie(options =>
with
services.Configure<CookieAuthenticationOptions>(CookieAuthenticationDefaults.AuthenticationScheme, options =>

Thanks @pmaytak that calls the event. Does @pmaytak's suggestion above work for you @toddheckel ?

Yes, it works! Thanks, I appreciate the support!

Thanks for the quick response @toddheckel

Was this page helpful?
0 / 5 - 0 ratings