OAuth2 Callback not hitting handler - asp.net-mvc

When the server I'm authenticating with responds with the redirect back to my callback URL, my code doesn't seem to know how to handle the request - as if I'm missing a handler.
I thought the CallbackPath property would wire this up and pass the incoming request to my handler, is that not correct?
I just get a 404 from my app, the URL has the code=XXXX etc.
Here's my code
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)
{
string authScheme = CookieAuthenticationDefaults.AuthenticationScheme;
services.AddAuthentication(o =>
{
o.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultAuthenticateScheme = "MyAuthScheme";
o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddOAuth(authScheme, options =>
{
options.ClientId = "xxxxxxxxxxxxxxxxxxxxxxxx";
options.AuthorizationEndpoint = "https://example.com/adfs/oauth2/authorize?resource=https://example.net/";
options.TokenEndpoint = "https://example.com/adfs/oauth2/token";
options.CallbackPath = new Microsoft.AspNetCore.Http.PathString("/signin-myauthserver");
options.ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxx";
});
services.AddMvc().AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver());
}
// 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();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseAuthentication();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
}

Related

Asp.Net Core Twitch OAuth: Correlation Failed

I configured Twitch authentication for my website using this tutorial: https://blog.elmah.io/cookie-authentication-with-social-providers-in-asp-net-core/
In the Twitch dev console I added the https://localhost:44348/signin-twitch url to the callback urls. I've implemented oauth for other providers before, but having troubles with Twitch.
When I try to login, I get the Twitch authorization screen, but after clicking 'Authorize' it redirects me to /signin-twitch + params which returns a Correlation Failed exception.
Exception: An error was encountered while handling the remote login.
I have a feeling it might have to do with the routing. It's setup like this because I have a frontend application with it's own routing (hence the Fallback)
Here is all relevant code.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
})
.AddTwitch(TwitchAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ClientId = "xxx";
options.ClientSecret = "xxx";
options.Scope.Add("user:read:email");
options.SaveTokens = true;
options.AccessDeniedPath = "/";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapFallbackToController("Index", "Home");
});
}
public class AuthenticationController : Controller
{
[HttpGet("~/signin")]
public IActionResult SignIn(string returnUrl = "")
{
return Challenge(TwitchAuthenticationDefaults.AuthenticationScheme);
}
}
I think that the error occurs because you're trying to access the URL which is assigned as Callback Path.
Try some variant of this:
[HttpGet("~/signin")]
public IActionResult SignIn()
{
var authProperties = _signInManager
.ConfigureExternalAuthenticationProperties("Twitch",
Url.Action("LoggingIn", "Account", null, Request.Scheme));
return Challenge(authProperties, "Twitch");
}
Source: this answer and this one.
Other stuff to check:
Multiple clients with the same Callback Path
CookiePolicyOptions
HTTPS redirect

ASP.Net Core Identity Pages throws 404 error after deploying to server but work fine hosting in local IIS

Once after hosting the project both in local IIS and in server, I found that in server all the identity related pages throws 404 error, whereas in local IIS it works fine. All the other pages apart from Identity Pages are accessible. My StartUp class looks like this:
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 => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddSingleton<IFileProvider>(
new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "wwwroot")));
var connectionString = this.Configuration.GetConnectionString("DefaultConnection");
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(connectionString);
options.UseLazyLoadingProxies(true);
});
//services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddDefaultIdentity<XonetPlus_V3.Entities.Domain.User>(options => options.SignIn.RequireConfirmedAccount = false)
.AddRoles<IdentityRole>().AddDefaultUI()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(#"c:\shared-auth-ticket-keys\"))
.SetApplicationName("SharedCookieApp");
services.ConfigureApplicationCookie(options => {
options.Cookie.Name = ".AspNet.SharedCookie";
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddTransient<IRepositoryWrapper, RepositoryWrapper>();
services.AddScoped<IEmailSender, EmailSender>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<User> userManager, RoleManager<IdentityRole> roleManager)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
IdentityDataInitializer.SeedData(userManager, roleManager);
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute("default", "{controller=admindashboard}/{action=Index}/{id?}");
// endpoints.MapControllerRoute("default", "{controller=Home}/{action=Index}/{id?}");
endpoints.MapControllerRoute("queryParams", "{controller=admindashboard}/{action=Index}/{param}/{id}");
endpoints.MapControllerRoute("areas", "{area:exists}/{controller=admindashboard}/{action=Index}");
endpoints.MapRazorPages();
});
}
}

how to set identity page by default and and after login it redirect to perticular page

I have added identity in asp.net core 3.1, How can i set identity login page as default and after successfully login it should redirect to particular page ?
In the Public void ConfigureServices(IServiceCollection services) method add the following codes at the bottom:
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
Note the LoginPath and AccessDeniedPath properties. Change the other properties to fit your requirements.
For the redirection it is actually coded in the login action which will redirect the user to a specific page. On successful login write the followings to redirect to the root page:
returnUrl = returnUrl ?? Url.Content("~/");
return LocalRedirect(returnUrl);
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment 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.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
}
}

Cannot get user identity set in Web Core API v1

I have a Web Core API version 1 project, where when I call a method via Postman I find the [Authorize] tag does not work.
In my Web API my startup looks like this (edited for readability)
public void ConfigureServices(IServiceCollection services)
{
var manager = new ApplicationPartManager();
manager.ApplicationParts.Add(new AssemblyPart(typeof(Startup).Assembly));
services.AddSingleton(manager);
services.AddCors();
services.AddMvcCore().AddJsonFormatters();
services.Configure<IISOptions>(options => new IISOptions
{
AutomaticAuthentication = true,
ForwardClientCertificate = false,
ForwardWindowsAuthentication = false
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
RequireHttpsMetadata = false,
Authority = Settings.AuthorityUrl,
ApiName = Settings.ApiName
});
app.UseStaticFiles();
var url = Configuration["originUrl"];
app.UseCors(
options => options.WithOrigins(url).AllowAnyHeader().AllowAnyMethod().AllowCredentials()
);
app.UseMiddleware<StackifyMiddleware.RequestTracerMiddleware>();
app.UseMvc();
}
In my OAuth server I am using the Quickstart for IdentityServer4
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer()
.AddSigningCredential(new X509Certificate2(Settings.CertPath, Settings.Password))
.AddTestUsers(InMemoryConfiguration.Users().ToList())
.AddInMemoryClients(InMemoryConfiguration.Clients())
.AddInMemoryApiResources(InMemoryConfiguration.ApiResources());
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false;
options.Authority = Settings.AuthorityUrl;
options.ApiName = Settings.ApiName;
});
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseMiddleware<StackifyMiddleware.RequestTracerMiddleware>();
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
And this is the method I am calling in Postman;
[HttpGet("get")]
public async Task<IActionResult> Get()
{
var claims = User.Claims;
var username = User.Identity.Name;
this.NLogger.Info("api/comboboxdata/get".ToPrefix());
try
{
var container = new ComboBoxData(this.SirUoW);
return Ok(container);
}
catch (Exception e)
{
var message = "Error getting combo box data";
await ReportException(e, message);
var status = OperationStatus.CreateFromException(message, e);
return BadRequest(status);
}
}
In Postman I get the bearer token and put it in the header. The method is successfully called and returns the data. The claims are also set as expected, and when the token expires the claims are empty. However [Authorize] does not block the request if the token is invalid or is not sent.
The Authorize attribute is at the beginning of the controller;
[Authorize]
[Route("api/comboboxdata")]
public class ComboBoxDataController : BaseSirController
{
How do I put this right?
You should add AddAuthorizationmethod to enable authorization services in your web api :
services.AddMvcCore()
.AddAuthorization()
.AddJsonFormatters();

Change Global Settings Config in SignalR Core

I am using SignalR Core with ASP.Net Core.
I want to override GlobalHost settings for signalR.
I am getting this-
protected void Application_Start(object sender, EventArgs e)
{
// Make long polling connections wait a maximum of 110 seconds for a
// response. When that time expires, trigger a timeout command and
// make the client reconnect.
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
// Wait a maximum of 30 seconds after a transport connection is lost
// before raising the Disconnected event to terminate the SignalR connection.
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
// For transports other than long polling, send a keepalive packet every
// 10 seconds.
// This value must be no more than 1/3 of the DisconnectTimeout value.
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
RouteTable.Routes.MapHubs();
}
But I can't configure it with my application.
It is on ASP.Net Core v1.0.
My Startup.cs is like this-
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Cors.Infrastructure;
using Microsoft.AspNetCore.Hosting;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
namespace McpSmyrilLine
{
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
if (env.IsDevelopment())
{
// This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
builder.AddApplicationInsightsSettings(developerMode: true);
}
Configuration = builder.Build();
}
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddApplicationInsightsTelemetry(Configuration);
//Add DB Context
var connectionStringBuilder = new Microsoft.Data.Sqlite.SqliteConnectionStringBuilder { DataSource = "mcp.db" };
var connectionString = connectionStringBuilder.ToString();
///////////////Add Cors
var corsBuilder = new CorsPolicyBuilder();
corsBuilder.AllowAnyHeader();
corsBuilder.AllowAnyMethod();
corsBuilder.AllowAnyOrigin();
corsBuilder.AllowCredentials();
services.AddCors(options =>
{
options.AddPolicy("AllowAll", corsBuilder.Build());
});
///////////////End Cors
services.AddDbContext<McpDbContext>(options =>
options.UseSqlite(connectionString));
services.AddMvc();
services.AddSignalR(options => { options.Hubs.EnableDetailedErrors = true; });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
//Configure Cors
app.UseCors("AllowAll");
app.UseSignalR();
app.UseApplicationInsightsRequestTelemetry();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
//Adding Seeder Data
AddTestData(app.ApplicationServices.GetService<McpDbContext>());
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseApplicationInsightsExceptionTelemetry();
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
private static void AddTestData(McpDbContext context)
{
//var testUser1 = new DbModels.Try.User
//{
// Id = "abc123",
// FirstName = "Luke",
// LastName = "Skywalker"
//};
//context.Users.Add(testUser1);
//var testPost1 = new DbModels.Try.Post
//{
// Id = "def234",
// UserId = testUser1.Id,
// Content = "What a piece of junk!"
//};
//context.Posts.Add(testPost1);
//context.SaveChanges();
}
}
}
Can anyone please help?
There's a Transports property in SignalROptions
You can setup the SignalR middleware like this :
services.AddSignalR(options => {
options.Hubs.EnableDetailedErrors = true;
var transports = options.Transports;
transports.DisconnectTimeout = TimeSpan.FromSeconds(30);
transports.KeepAlive = TimeSpan.FromSeconds(10);
transports.TransportConnectTimeout = TimeSpan.FromSeconds(110);
});
UPDATE alpha2-final
Transport options can be configured throught MapHub:
app.UseSignalR(configure =>
{
configure.MapHub<Hub>("hub", options =>
{
options.Transports = TransportType.All;
options.LongPolling.PollTimeout = TimeSpan.FromSeconds(10);
options.WebSockets.CloseTimeout = TimeSpan.FromSeconds(10);
});
})
and on client side:
let logger: ILogger;
let transportType: TransportType;
const hubConnetion = new HubConnection(
new HttpConnection(
url,
{
transport: transportType,
logging: logger
}));

Resources