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

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 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();
}