The goal is to implement OAuth2 against Google in a C# MVC app using .NET6. The result I get shows failed in the result.Succeeded field and all values are null.
I'm sending the correct google client id and google client secret.
The app builds and sends off the request without an error. I believe that is all correct.
Here's my Program.cs
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.Google;
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: false)
.Build();
var googleClientId = config.GetValue<string>("GoogleClientId");
var googleClientSecret = config.GetValue<string>("GoogleClientSecret");
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddGoogle(options =>
{
options.ClientId = googleClientId;
options.ClientSecret = googleClientSecret;
options.CallbackPath = "/account/google-response";
}).AddCookie(options =>
{
options.LoginPath = "/account/google-login"; // Must be lowercase
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
Here's the account controller that sends and receives the result:
[AllowAnonymous, Route("account")]
public class AccountController : Controller
{
[Route("google-login")]
public IActionResult GoogleLogin()
{
var properties = new AuthenticationProperties { RedirectUri = Url.Action("GoogleResponse") };
return new ChallengeResult(GoogleDefaults.AuthenticationScheme, properties);
}
[Route("google-response")]
public async Task<IActionResult> GoogleResponse()
{
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
// This is coming back with result.Succeeded == false, and result.Principal == null
var succeeded = result.Succeeded;
if (result.Principal == null) throw new Exception("Principal is null");
if (result.Principal.Identities == null) throw new Exception("Identities is null");
var claims = result.Principal.Identities
.FirstOrDefault().Claims.Select(claim => new
{
claim.Issuer,
claim.OriginalIssuer,
claim.Type,
claim.Value
});
return Json(claims);
}
}
Not sure what is wrong.
Thanks for the help.
Added: Not sure if this is helpful, but the payload that I'm getting back from Google:
state: CfDJ8Dcp9PtNslFFr9WdoNMnaxZuUE3bX_7go4zy8_XDg2ZIar8NvdxzZlhJ9mM9c8-E3cp9TchRcjvMwbX4XaMmTC79aKO7IuI39yHgZ6nrOEqORPDU9kfHGH-bgJB5S1bXIQcJ3y3wKMD39N6IDa-ygAxqoiFkd05Lf05d9RoA8bZ0h8DcbYbqpbK73rQraTQhBAdxVJlz2CLGXkzOhIoJmhdOn38poxjNILyGPOGRqA0t
code: 4/0AX4XfWjHBoCnvig2U2JG8tuPnqIvjQeC5VN_u_pSOcqIFFOjw7UadqI04qJ3iw4QyF2ngg
scope: email profile openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email
authuser: 0
prompt: consent
Found the issue. I was not configuring authorization and authentication to the application. (I was doubly adding authorization.) Specifically, this line when placed in Configure method in StartUp.cs fixed the problem:
app.UseAuthentication();
Related
Im trying to set up authentication using cookies on my ASP.NET core project and for some reason whenever I attempt to access any route with the [Authorize] attribute, it attempts to redirect me to /Accounts/Login, even when the user is authenticated.
I have checked to make sure that the cookies are set on the client side and they are.
Here is my code:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var services = builder.Services;
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.HttpOnly = true;
options.Cookie.Name = "auth_cookie";
options.Cookie.IsEssential = true;
options.LoginPath = "/auth/login";
options.LogoutPath = "/auth/logout";
#if DEBUG
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
#else
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
#endif
options.Cookie.SameSite = SameSiteMode.Lax;
options.Events = new CookieAuthenticationEvents
{
OnRedirectToLogin = context =>
{
Console.WriteLine(context.RedirectUri);
context.Response.StatusCode = 401;
return Task.CompletedTask;
},
OnSignedIn = context =>
{
Console.WriteLine("User signed in");
return Task.CompletedTask;
}
};
});
services.AddAuthorization();
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.WithOrigins("http://localhost:4200")
.SetIsOriginAllowedToAllowWildcardSubdomains()
.AllowCredentials()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
services.AddMvc(options => options.EnableEndpointRouting = false);
services.AddSession();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("default");
app.UseCookiePolicy();
app.UseSession();
app.UseMvc();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapDefaultControllerRoute();
app.Run();
The things that I have tried to do is change the order of UseAuthroization and UseAuthentication and change the order of other things.
I also suspect that It might be caused because of the way I authenticate users. Heres how the flow works.
User goes to the auth/login page
User gets redirected to googles login form
Google redirects users back to API. Which creates a new claims identity which then logs
the user in. This claim sends cookie back.
User gets redirected to frontend page
Here is the code with my login function that runs when google redirects back to it
//Above is a function to get a token from google which I know works correctly
var claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.NameIdentifier, userInfo.Id),
//...
}, "Cookies");
await HttpContext.SignInAsync( CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
new AuthenticationProperties
{
IsPersistent = true,
AllowRefresh = true,
ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(60),
});
#if DEBUG
return LocalStorage.AddOrganization(org) ? Redirect($"http://localhost:4200/org/{HttpUtility.UrlEncode(org.Domain)}/settings/setup") : Redirect($"http://localhost:4200/org/{HttpUtility.UrlEncode(org.Domain)}/dashboard");
#else
return LocalStorage.AddOrganization(org) ? Redirect($"http://localhost:4200/org/{HttpUtility.UrlEncode(org.Domain)}/settings/setup") : Problem($"http://localhost:4200/org/{HttpUtility.UrlEncode(org.Domain)}/dashboard");
#endif
This is about an application aspnetcore 3.0 with EF core
In which login security and authorization was added using openID.
The problem although users do ends up in the EF core DB, and validation does happens at openID. That after login the partial view does not recognize that the user is logged in.
So I cant make use of the username or email at other pages, kinda weird.
loginpartial.cs :
#using Microsoft.AspNetCore.Identity
#using Microsoft.AspNetCore.Mvc.TagHelpers
#inject SignInManager<IdentityUser> SignInManager
#inject UserManager<IdentityUser> UserManager
<ul class="navbar-nav">
#if (SignInManager.IsSignedIn(User))
{ ... it never knows that the user is signed in
... despite i did get loged on by external service, and endup at the default page
I got the idea there is something wrong with my program.cs in which the whole configuration is givven, that i somehow have to transfer the current user towards the previous injected user manager or so.
program.cs :
using System.IdentityModel.Tokens.Jwt;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Newtonsoft.Json.Serialization;
using MyApplication.Data;
bool useAuthentication = true;
MyApplication.Common.AppConfig.AddOrUpdate("config:args",args);
var builder = WebApplication.CreateBuilder(args);
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddControllersWithViews();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddControllers()
.AddNewtonsoftJson(options =>
{
options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});
if (useAuthentication)
{
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(options=>options.ExpireTimeSpan = TimeSpan.FromMinutes(1))
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ClientId = builder.Configuration["OpenID:ClientID"];
options.ClientSecret = builder.Configuration["OpenID:ClientSecret"];
options.Authority = builder.Configuration["OpenID:Authority"];
options.CallbackPath = builder.Configuration["OpenID:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.Code;
options.ClaimActions.MapUniqueJsonKey("username", "username");
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = tokencontext =>
{
// I could do something here with current user,
// though user does get into EF core thus ehm do i need this ??
return Task.CompletedTask;
},
OnTicketReceived = context =>
{
// If authentication logic is based on users then add your logic here
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/Home/Error");
context.HandleResponse(); // Suppress the exception
return Task.CompletedTask;
},
};
});
}
var app = builder.Build();
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<ApplicationDbContext>();
context.Database.Migrate();
}
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=MyApplication}/{action=Index}/{id?}").RequireAuthorization();
});
app.MapControllerRoute(
name: "default",
pattern: "{controller=MyApplication}/{action=Index}/{id?}");
app.MapControllerRoute(name: "api", pattern: "api/{controller=Api}/{Action=Test}/{id?}/{country?}");
app.MapRazorPages();
app.Run();
(PS its not about older MVC5 applications, its MVC6 here)
Hard to say without running your code locally (which I have not done).
I am, however, suspicious of how you are mapping controllers and injecting authorization to them.
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=MyApplication}/{action=Index}/{id?}").RequireAuthorization();
});
app.MapControllerRoute(
name: "default",
pattern: "{controller=MyApplication}/{action=Index}/{id?}");
app.MapControllerRoute(name: "api", pattern: "api/{controller=Api}/{Action=Test}/{id?}/{country?}");
Why the difference between the two? I.e. why does the first require authorization while the second does not?
Why not use something simple like the below?
app.MapControllers().RequireAuthorization();
I've just created a new ASP.NET Core application using Visual Studio, it has the authentication type set to "Individual Accounts":
I've added some code to handle OIDC:
builder.Services.AddAuthentication().AddOpenIdConnect(openIdOptions =>
{
openIdOptions.ClientId = "myClientId";
openIdOptions.Authority = "myAuthority";
openIdOptions.ResponseType = OpenIdConnectResponseType.Code;
openIdOptions.GetClaimsFromUserInfoEndpoint = false;
openIdOptions.CallbackPath = "/signin-oidc";
openIdOptions.SaveTokens = true;
Which is all well and good, I can click to login with OpenIdConnect:
When I log in with the provided credentials it pops up asking me to Associate my OpenIdConnect account:
I don't want to have to do this. Retrieving the access token and id token is sufficient for what I want to do, I don't require a local copy of the user. Being new to ASP.net and having not used Razor before it's not immediately obvious what code I should delete — if that's even the correct approach. I've played around with deleting various bits such as the Db setup as I don't really require it, although I suspect Razor does in some regard.
The Program.cs looks as follows:
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddDefaultIdentity<IdentityUser>(options =>
{
})
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddRazorPages();
builder.Services.AddAuthentication().AddGoogle(googleOptions =>
{
googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"];
googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
});
builder.Services.AddAuthentication().AddOpenIdConnect(openIdOptions =>
{
openIdOptions.ClientId = "clientId";
openIdOptions.Authority = "authority";
openIdOptions.ResponseType = OpenIdConnectResponseType.Code;
openIdOptions.GetClaimsFromUserInfoEndpoint = false;
openIdOptions.CallbackPath = "/signin-oidc";
openIdOptions.SaveTokens = true;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapRazorPages();
app.UseEndpoints(endpoints => { endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}"); });
app.Run();
I am creating CookieAutentecation signin for my Web API.
I have read and followed the official article here and I have done everything correctly as far as I am concerned.
But when I put breakpoints in my controllers and inspect HttpContext.User, everything is always null, no Username, no claims, nothing.
What else do I need to make this work? Are additional steps needed for Web API vs MVC app?
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, act => {
act.LoginPath = "/api/login";
act.AccessDeniedPath = "/api/login";
act.SlidingExpiration = true;
});
services.AddControllers();
services.AddServices(); // <- Own app domain services
services.AddDataAccess(); // <- Own app domain data access
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseCors(
options => options.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
api/login
var user = new SecurityUser()
{
UserID = 123,
CompleteName = "Test user",
FirstName = "Test",
Email = "test.user#123.com"
};
var identity = user.ToClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme, 123);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity), new AuthenticationProperties()
{
AllowRefresh = true,
ExpiresUtc = DateTime.UtcNow.AddDays(7),
IsPersistent = true,
});
ToClaimsIdentity extension method:
public static ClaimsIdentity ToClaimsIdentity(this SecurityUser user, string authenticantionType, int auditUserID)
{
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, user.UserID.ToString()),
new Claim(ClaimTypes.Email, user.Email),
new Claim(ClaimTypes.Name, user.FirstName),
new Claim(SecurityUserClaimTypes.AuditUserID, auditUserID.ToString())
};
var identity = new ClaimsIdentity(claims, authenticantionType);
return identity;
}
Any help would be greatly appreciated.
Edit - This is what I am taking about 👇
Thanks for your help guys!
I finally realised it was a client thing, I did three things:
CORS was an issue, in my .UseCors method call my my Api I allowed credentials:
.AllowCredentials()
My client app in using Blazor, I found this article here which told me I needed to set the http request configuration to include credentials, so in my client side app startup.cs:
WebAssemblyHttpMessageHandlerOptions.DefaultCredentials = FetchCredentialsOption.Include;
I am using Http not Https on my local, and Chrome was complaining about SameSite, so im my Api StartUp.cs, where I call AddAuthentication...AddCookie I added this:
options.Cookie.SameSite = SameSiteMode.Unspecified;
I don't fully understand the SameSite... and I have also come across JSON Web Tokens (JWT).
But I'm not interested, as long as it's working. ;-)
recently we have migrated our project from asp.net to asp.net core, the project was working fine in asp.net, We have followed the migration document of Microsoft for migrating "asp.net to asp.net core" and modified accordingly, unfortunately, OnRedirectToIdentityProvider method in startup class is not working on challenge call (from the controller). It would be great if someone helps me to figure out where my code is wrong. I have been looping on the same issue for a while. thanks in advance.
StartUp.cs
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//Add a strongly-typed options class to DI
services.Configure<AuthOptions>(Configuration.GetSection("Authentication"));
services.AddAuthentication(opt => {
opt.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
opt.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie("MiddleWareCookie")
.AddOpenIdConnect(options => Configuration.Bind("Authentication", options));
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Authority = Configuration["Authentication:Authority"];
options.ClientId= Configuration["Authentication:ClientId"];
options.ClientSecret= Configuration["Authentication:ClientSecret"];
options.TokenValidationParameters = new TokenValidationParameters
{
// Instead of using the default validation (validating against a single issuer value, as we do in
// line of business apps), we inject our own multitenant validation logic
ValidateIssuer = false,
// If the app is meant to be accessed by entire organizations, add your issuer validation logic here.
//IssuerValidator = (issuer, securityToken, validationParameters) => {
// if (myIssuerValidationLogic(issuer)) return issuer;
//}
};
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = (context) =>
{
object obj = null;
var request = context.Request;
if (context.HttpContext.Items.TryGetValue("Authority", out obj))
{
string authority = obj as string;
if (authority != null)
{
context.ProtocolMessage.IssuerAddress = authority;
}
}
//string appBaseUrl = context.Request.Scheme + "://" + context.Request.Host + context.Request.PathBase;
string appBaseUrl = #"https://localhost:44359/";//UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase);
Debug.WriteLine($"appBaseUrl: {appBaseUrl}");
context.ProtocolMessage.PostLogoutRedirectUri = appBaseUrl;
context.ProtocolMessage.Prompt = "select_account";
context.ProtocolMessage.Resource = Configuration["Authentication:AzureResourceManagerIdentifier"];
return Task.FromResult(0);
},
OnAuthorizationCodeReceived = async (context) =>
{
var request = context.HttpContext.Request;
var currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);
var credential = new ClientCredential(context.Options.ClientId, context.Options.ClientSecret);
string tenantId = context.Principal.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
//Comment
Debug.WriteLine($"tenantID: {tenantId}");
// Revisit
string signedInUserUniqueName = context.Principal.FindFirst(ClaimTypes.Name).Value.Split('#')[context.Principal.FindFirst(ClaimTypes.Name).Value.Split('#').Length - 1];
//Comment
Debug.WriteLine($"tenantID: {signedInUserUniqueName}");
var tokenCache = new ADALTokenCache(signedInUserUniqueName);
tokenCache.Clear();
// revisit
AuthenticationContext authContext = new AuthenticationContext(string.Format("https://login.microsoftonline.com/{0}", tenantId), tokenCache);
// var items = authContext.TokenCache.ReadItems().ToList();
// revisit
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
context.ProtocolMessage.Code, new Uri(currentUri), credential);
//Tell the OIDC middleware we got the tokens, it doesn't need to do anything
context.HandleCodeRedemption(result.AccessToken, result.IdToken);
},
OnTokenValidated = (context) => {
string issuer = context.Principal.FindFirst("iss").Value;
if (issuer != null)
{
if (!issuer.StartsWith("https://sts.windows.net/"))
throw new SecurityTokenValidationException();
}
return Task.FromResult(0);
},
OnTicketReceived = context =>
{
// If your authentication logic is based on users then add your logic here
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/Error");
context.HandleResponse(); // Suppress the exception
return Task.CompletedTask;
},
// If your application needs to do authenticate single users, add your user validation below.
//OnTokenValidated = context =>
//{
// return myUserValidationLogic(context.Ticket.Principal);
//}
};
});
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
Subscription Controller Method
HttpContext.Items.Add("Authority", string.Format(_authOptions.Authority + "OAuth2/Authorize", _directoryId));
Dictionary<string, string> dict = new Dictionary<string, string>();
dict["prompt"] = "select_account";
var userIdentity = new ClaimsIdentity(User.Claims, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.AuthenticateAsync("MiddleWareCookie");
I can see two things that explain why OnRedirectToIdentityProvider is not called based on your controller code:
you're calling the AuthenticateAsync method where your question states you're calling Challenge;
you're referring to the cookie authentication scheme (MiddleWareCookie), where I think your intent is to trigger an OpenID Connect login.
I believe you need to replace HttpContext.AuthenticateAsync("MiddleWareCookie") with HttpContext.ChallengeAsync() to have the OIDC login request be triggered.
Another potential issue I can see is that you set, in the AddAuthentication method, the DefaultScheme to CookieAuthenticationDefaults.AuthenticationScheme, but the name of your cookie authentication scheme is MiddleWareCookie. These two need to be in sync.