user1854458
user1854458

Reputation: 665

.NET Core cookie authentication with AWS Lambda not persisting

I am unable to get cookie authentication working with an AWS Lambda function using .NET Core 2.1 MVC.

I have tried lots of variations of the cookie options. I am able to login and see the asp cookie being created in the response, but I am returned to the login screen usually after I refresh or click on any link, e.g. the next request to the server. *Update: It seems I have it in a state where I only have to login twice initially now and it stays logged in. This is also the second Lambda function using .net 2.1 where I've noticed this behavior.

I have API gateway configured with the defaults that get set up when deploying using the AWS extension for visual studio.

My current startup.cs code, which works on localhost:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
    .AddCookie(options =>
    {
        // Cookie settings
        options.Cookie.SameSite = SameSiteMode.Lax;
        options.Cookie.SecurePolicy = CookieSecurePolicy.SameAsRequest;
        options.LoginPath = "/Login";
        options.LogoutPath = "/Logout";
        options.AccessDeniedPath = "/Login";
        options.Cookie.Name = "myapp.auth";
        options.Cookie.HttpOnly = true;
        options.Cookie.Expiration = TimeSpan.FromDays(1);
    });

    services.ConfigureApplicationCookie(options =>
    {
        // Cookie settings, only this changes expiration
        options.SlidingExpiration = true;
        options.ExpireTimeSpan = TimeSpan.FromDays(1);
    });
services.AddAntiforgery(options => { options.Cookie.Expiration = TimeSpan.Zero; });

and

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    else
    {
        app.UseExceptionHandler("/Error");
        app.UseHsts();
    }

    app.UseHttpsRedirection();
    app.UseStaticFiles();
    app.UseCookiePolicy();
    app.UseAuthentication();

    app.UseMvc();
}

I've tried using the following when logging in as well:

    await HttpContext.SignInAsync(principal, new AuthenticationProperties
    {
        ExpiresUtc = DateTime.UtcNow.AddHours(12),
        IsPersistent = true
    });

Simply using the following will keep me logged in, but I have to log in twice before the cookie persists (once more after clicking on anything and being redirected to login again):

await HttpContext.SignInAsync(principal);

Upvotes: 7

Views: 2266

Answers (2)

JamesQMurphy
JamesQMurphy

Reputation: 4428

@Alex Nazarevych's answer is correct in that you need to set up Data Protection storage. However, the linked article describes how to store the Data Protection keys in the AWS Systems Manager Parameter Store, not the file system. And it makes sense, since the Lambda function will continuously exit and lose its file storage.

I use the AWS Parameter store on my own website, and it works perfectly well. Here's what you need to do:

  1. Add a NuGet package reference to Amazon.AspNetCore.DataProtection.SSM to your project.
  2. In your Startup.ConfigureServices method, add the following code. You can change the "/DataProtection" to whatever you like; this just defines what your key names will start with in the Parameter Store:
services.AddDataProtection()
   .PersistKeysToAWSSystemsManager("/DataProtection");
  1. Make sure that your Lambda function has the AddTagsToResource, PutParameter, and GetParametersByPath permissions to the AWS Parameter Store. As an IAM Policy, it would look like the following. Note that if you used something other than "/DataProtection" in the code above, then you need to change the resource in the policy below to match. (And don't forget the asterisk at the end.)
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "ssm:AddTagsToResource",
                "ssm:GetParametersByPath",
                "ssm:PutParameter"
            ],
            "Resource": "arn:aws:ssm:*:*:parameter/DataProtection*",
            "Effect": "Allow"
        }
    ]
}

After the code has run once, you can use the AWS Console to view the keys yourself. Just log into the AWS Console and navigate to the Systems Manager Parameter Store.

Upvotes: 6

Alex Nazarevych
Alex Nazarevych

Reputation: 464

This happens because an app hosted in AWS Lambda periodically EXITS, causing DataProtection service to forget your cookie key. So, even if your app client does send the cookie, the server will reject it after the restart because the data protection key has changed.

To solve the problem you need to set up the Data Protection storage, e.g. by (see the article on ms docs):

services.AddDataProtection()
    .PersistKeysToFileSystem("{PATH TO COMMON KEY RING FOLDER}")
    .SetApplicationName("SharedCookieApp");

I have had a similar issue running asp.net core 3.1 on AWS lambda. When I checked the logs, there were several very descriptive warning messages:

[Warning] Microsoft.AspNetCore.DataProtection.Repositories.EphemeralXmlRepository: Using an in-memory repository. Keys will not be persisted to storage.

[Warning] Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager: Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.

See this article on how to configure DataProtection in AWS Lambda for asp.net core

Upvotes: 3

Related Questions