OAuth2.0 Cookie Authentication in .NET 8/9

Web applications and APIs that would rather utilize server-side session management than frontend OAuth token disclosure, opt for Cookie Authentication. Cookie Authentication A method in an OAuth-based system that preserves an authenticated session after a user is signed in through an external OAuth provider. The authentication state will be maintained by cookies. Standard default settings offered by a static class called CookieAuthenticationDefaults helps configure cookie-based authentication in ASP .NET Core. CookieAuthenticationDefaults.AuthenticationScheme is a constant which stands for the value "Cookies". Authentication Flow After successful OAuth authentication To persist the authentication state across multiple requests utilize OnCreatingTicket to preserve the identity claims (data in key value pairs that hold authorized user's identity information, roles & permissions across systems) in a secure cookie which is further used by the server to identify the user. Access Token The access token retrieved from context object can be decrypted using the validation key, decryption key, encryption algorithm (this information is given by the OAuth provider).To retrieve user identity information a custom decryption helper class with relevant decryption algorithm must be implemented which returns an _AuthenticationTicket _ object which is designed in .NET Core to capture user identification Access current user record from current Http request To make user data available across controllers and middleware, assign _ClaimsPrincipal _ to the HttpContext.User property inside onTicketRecieved triggered after the Authentication Ticket is created as below. context.HttpContext.SignInAsync(context.Options.SignInScheme, context.Principal) appSettings.json “OAuth”: { "ClientId": "xxxx", "ClientSecret": "xxxxx", "AuthorizationEndpoint": "xxxx...", "TokenEndpoint": "xxxx..." }, "SecretKey":{ "ValidationKey":"...", "DecryptionKey":"...." } Program.cs builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = "MyOAuthProvider"; //"MyOAuthProvider" could be your custom OAuth provider }) .AddCookie(options => { // Cookie authentication event - after successful OAuth authentication followed by creation of valid authentication ticket options.Events.OnValidatePrincipal = async (context) => { await Task.FromResult(0); }; }) .AddOAuth("MyOAuthProvider", options => { options.ClientId = builder.Configuration.GetSection("OAuth").GetValue("ClientId"); options.ClientSecret = builder.Configuration.GetSection("OAuth").GetValue("ClientSecret"); options.CallbackPath = new PathString("/signin-callback"); // URL to handle OAuth callback - after login options.AuthorizationEndpoint = builder.Configuration.GetSection("OAuth").GetValue("AuthorizationEndpoint"); options.TokenEndpoint = builder.Configuration.GetSection("OAuth").GetValue("TokenEndpoint "); options.SaveTokens = true; // Save tokens in the authentication cookie for future requests //Event - Action options.Events = new OAuthEvents { //after the OAuth provider has issued the access token but before the authentication ticket is assigned OnCreatingTicket = context => { if (context.AccessToken != null) { //Access token decryption var ticket = DecryptionHelper.Decrypt(context.AccessToken, secretKey.GetValue("DecryptionKey"), secretKey.GetValue("ValidationKey")); // placeholder code - this must be replaced with your own defined decryption helper class context.Principal = new ClaimsPrincipal(ticket.Identity); } return Task.CompletedTask; }, //triggered after the Authentication Ticket is created OnTicketReceived = context => { if (context.Principal != null) { context.HttpContext.SignInAsync(context.Options.SignInScheme, context.Principal); } return Task.FromResult(0); } }; }); Once the authentication process is successful with current user login status and data is accessible through HttpContext.User class, it can be used for user authorization i.e., manage access permissions in sign in call back action method defined in the application before redirecting the user to any of the protected resources. For instance, in this case: HomeController.cs public ActionResult SignInCa

May 13, 2025 - 00:52
 0
OAuth2.0 Cookie Authentication in .NET 8/9

Web applications and APIs that would rather utilize server-side session management than frontend OAuth token disclosure, opt for Cookie Authentication.

Cookie Authentication

A method in an OAuth-based system that preserves an authenticated session after a user is signed in through an external OAuth provider.

  • The authentication state will be maintained by cookies.
  • Standard default settings offered by a static class called CookieAuthenticationDefaults helps configure cookie-based authentication in ASP .NET Core. CookieAuthenticationDefaults.AuthenticationScheme is a constant which stands for the value "Cookies".

Authentication Flow

Image description

After successful OAuth authentication

To persist the authentication state across multiple requests utilize OnCreatingTicket to preserve the identity claims (data in key value pairs that hold authorized user's identity information, roles & permissions across systems) in a secure cookie which is further used by the server to identify the user.

Access Token

The access token retrieved from context object can be decrypted using the validation key, decryption key, encryption algorithm (this information is given by the OAuth provider).To retrieve user identity information a custom decryption helper class with relevant decryption algorithm must be implemented which returns an _AuthenticationTicket _ object which is designed in .NET Core to capture user identification

Access current user record from current Http request

To make user data available across controllers and middleware, assign _ClaimsPrincipal _ to the HttpContext.User property inside onTicketRecieved triggered after the Authentication Ticket is created as below.

context.HttpContext.SignInAsync(context.Options.SignInScheme, context.Principal)

appSettings.json

“OAuth”: {
    "ClientId": "xxxx",
    "ClientSecret": "xxxxx",
    "AuthorizationEndpoint": "xxxx...",
    "TokenEndpoint": "xxxx..."
},
"SecretKey":{
        "ValidationKey":"...",
        "DecryptionKey":"...."
}

Program.cs

builder.Services.AddAuthentication(options =>
{
    options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
    options.DefaultChallengeScheme = "MyOAuthProvider"; //"MyOAuthProvider" could be your custom OAuth provider
})
.AddCookie(options =>
        {
            // Cookie authentication event - after successful OAuth authentication followed by creation of valid authentication ticket
            options.Events.OnValidatePrincipal = async (context) =>
            {
                await Task.FromResult(0);
            };
        })
  .AddOAuth("MyOAuthProvider", options =>
        {
            options.ClientId = builder.Configuration.GetSection("OAuth").GetValue("ClientId");
            options.ClientSecret = builder.Configuration.GetSection("OAuth").GetValue("ClientSecret");
           options.CallbackPath = new PathString("/signin-callback");  // URL to handle OAuth callback - after login

            options.AuthorizationEndpoint = builder.Configuration.GetSection("OAuth").GetValue("AuthorizationEndpoint");

            options.TokenEndpoint = builder.Configuration.GetSection("OAuth").GetValue("TokenEndpoint ");

            options.SaveTokens = true;  // Save tokens in the authentication cookie for future requests

//Event - Action 
            options.Events = new OAuthEvents
            {
//after the OAuth provider has issued the access token but before the authentication ticket is assigned
                OnCreatingTicket = context =>
                {
                    if (context.AccessToken != null)
                    {
//Access token decryption
  var ticket = DecryptionHelper.Decrypt(context.AccessToken,
                       secretKey.GetValue("DecryptionKey"), secretKey.GetValue("ValidationKey")); // placeholder code - this must be replaced with your own defined decryption helper class
                        context.Principal = new ClaimsPrincipal(ticket.Identity);
                    }
                    return Task.CompletedTask;
                },
             //triggered after the Authentication Ticket is created 
                OnTicketReceived = context =>
                {
                    if (context.Principal != null) {                       
                        context.HttpContext.SignInAsync(context.Options.SignInScheme, context.Principal);
                    }
                    return Task.FromResult(0);
                }
            };          
        });

Once the authentication process is successful with current user login status and data is accessible through HttpContext.User class, it can be used for user authorization i.e., manage access permissions in sign in call back action method defined in the application before redirecting the user to any of the protected resources.

For instance, in this case:

HomeController.cs

public ActionResult SignInCallBack(){
//check if user is authenticated
  if(HttpContext.User.Identity.IsAuthenticated){
    return Redirect("Home/Index");
  }
  else{
    return RedirectToAction("Access Denied");
  }
}

public ActionResult Index()
{
  return View();
}