JWT authentication issue with web api plugin in nopCommerce 4.30 - asp.net-mvc

I implement JWT authentication for authorization using access token or bearer token in web api plugin with nopCommerce version 4.30. It is working fine with default source of nopCommerce 4.30. But when i used my API plugin with any Seven spikes plugins (SevenSpikes.Nop.Plugins.AjaxCart, SevenSpikes.Nop.Plugins.QuickView, etc), its not working and threw exception like "System.ArgumentException: 'Duplicate Controller with DuplicateControllerName:NopAjaxCartShoppingCart is already added'".
Here/below is my Code for configure method in startup file:
public void Configure(IApplicationBuilder app)
{
var dataSettings = DataSettingsManager.LoadSettings();
if (!dataSettings?.IsValid ?? true)
return;
var rewriteOptions = new RewriteOptions()
.AddRewrite("oauth/(.*)", "connect/$1", true)
.AddRewrite("api/token", "connect/token", true);
app.UseRewriter(rewriteOptions);
app.UseRouting();
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
//register all routes
EngineContext.Current.Resolve<IRoutePublisher>().RegisterRoutes(endpoints);
});
}

Related

/sign-in-oidc is returning 404 when sigining in using Azure AD B2C

This question is similar to Why is /signin-oidc returning 404 after using Azure AD sign-in? and Identity Server 404 after login (stuck on signin-oidc) except I'm already doing the solution for that (calling app.UseAuthentication();).
In my scenario, I'm using Azure AD B2C with implicit flow. I'm able to submit the consent screen and then I get a POST to /sign-oidc which returns 404. The payload of the post includes a state field and an id_token field. I seem to not have set up the middleware that handles that but I haven't found how that is done.
One thing to note is that I'm using GitHub Codespaces, but I've worked out an issue with the proxying of the site to localhost.
Here's my program.cs:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
using Microsoft.IdentityModel.Logging;
var builder = WebApplication.CreateBuilder(args);
var services = builder.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.Unspecified;
// Handling SameSite cookie according to https://learn.microsoft.com/aspnet/core/security/samesite?view=aspnetcore-3.1
options.HandleSameSiteCookieCompatibility();
});
// Configuration to sign-in users with Azure AD B2C
services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, Constants.AzureAdB2C);
services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
services.AddRazorPages();
//Configuring appsettings section AzureAdB2C, into IOptions
services.AddOptions();
services.Configure<OpenIdConnectOptions>(builder.Configuration.GetSection(Constants.AzureAdB2C));
// need to set the RedirectUri here because I'm using GitHub codespaces.
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Events.OnRedirectToIdentityProvider = async n =>
{
n.ProtocolMessage.RedirectUri = "https://codespacename.preview.app.github.dev/signin-oidc"; //todo: move to config or generate dynamically
await Task.CompletedTask;
};
});
// Add services to the container.
builder.Services.AddControllersWithViews();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
IdentityModelEventSource.ShowPII = true;
}
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.UseForwardedHeaders();
//app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapControllers();
app.Run();
I tried to reproduce the scenario from my end.
Tried with callback path "/sign-in-oidc
Appsettings.json:
{
"AzureAdb2c": {
"Instance": "https://login.microsoftonline.com/",
"Domain": "testthetenantdomain ",
"ClientId": "xxxx",
"TenantId": "xxxx",
"ClientSecret": "xxx",
"ClientCertificates": [
],
"CallbackPath": "/sign-in-oidc"
},
Received error as below:
Here the application url or account to be logged in is not found , hence we got No account or login hint was passed error.
This error occurred as call back path is given /sign-in-oidc in my case
make sure "CallbackPath" is "/signin-oidc"
Could get the output successfully after login

Getting 401 Unauthorized with MVC Pages while Identity Razor pages work as expected

Background
I am doing a POC to find out if Angular, Razor and MVC pages work seamlessly in a web application. I started with Visual Studio template named "ASP.NET Core with Angular". I have selected "Individual Accounts" to include default authentication functionality. This creates an Angular app with a secure web API endpoint (WeatherForecast) and provides basic user registration, login, logout, user profile pages etc features built in. So far all works well, when I try to fetch data from the protected API (WeatherForecast) I get redirected to the Identiy/Account/Login razor page where I can login and then get redirected back to Angular and I can see that data is returned and grid is populated. Till this point everything works fine.
The Problem
I added a DemoController class with a basic "Hello World" HTML view. When I try to access this new page with /demo, it works as expected. However, when I apply [Authorize] attribute to the controller, I get 401 Unauthorized. I checked on server side that User.IsAuthenticated property is set to false despite having successfully logged in before. Now interesting observation is that the user profile page (which is protected and works only if there an active login) works fine.
Please note that all API calls issues from Angular use JWT bearer token and work fine. When I try to access user profile page, it does NOT use JWT, it uses cookies to authenticate. The GET request to /demo page also has all these cookies in headers, still it is met with 401.
I spent a lot of time going thru articles, searching web with no success. The closing thing we found is this : ASP.NET Core 5.0 JWT authentication is throws 401 code
But that didn't help either.
The project is created using Visual Studio 2022, .net core 6.0. Here is the Program.cs file for your reference:
using CoreAngular.Data;
using CoreAngular.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.UI;
using Microsoft.EntityFrameworkCore;
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<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddEntityFrameworkStores<ApplicationDbContext>();
builder.Services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
builder.Services.AddAuthentication()
.AddIdentityServerJwt();
builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseMigrationsEndPoint();
}
else
{
// 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.UseIdentityServer();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
app.MapRazorPages();
app.MapFallbackToFile("index.html"); ;
app.Run();
This has been answered here: https://stackoverflow.com/a/62090053/3317709
It turned out that using IdentityServer extension methods add a policy scheme such that only /Identity pages have cookie authentication. The rest default to JWT.
We can customize this by adding our own policy like so:
builder.Services.AddAuthentication()
.AddIdentityServerJwt()
.AddPolicyScheme("ApplicationDefinedAuthentication", null, options =>
{
options.ForwardDefaultSelector = (context) =>
{
if (context.Request.Path.StartsWithSegments(new PathString("/Identity"), StringComparison.OrdinalIgnoreCase) ||
context.Request.Path.StartsWithSegments(new PathString("/demo"), StringComparison.OrdinalIgnoreCase))
return IdentityConstants.ApplicationScheme;
else
return IdentityServerJwtConstants.IdentityServerJwtBearerScheme;
};
});
// Use own policy scheme instead of default policy scheme that was set in method AddIdentityServerJwt
builder.Services.Configure<AuthenticationOptions>(options => options.DefaultScheme = "ApplicationDefinedAuthentication");

Facing issue with configure method in .Net core application

I have developed .Net core API project and deployed on the server.
I added it as site on IIS(remote windows server)and tried to browse the application.
The application is not working properly facing issue at Configure method
Here is my configure method in >net core
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
var appName = "";
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseMvc();
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api V1");
});
}
Please let me know how can to add swagger endpoint.
I guess you directly publish your web application to a nested site in the default website on IIS.
So the "/swagger/v1/swagger.json" root path will become localhost/swagger/v1/swagger.json not localhost/yourwebsitename/swagger/v1/swagger.json.
To solve this issue, I suggest you could try to modify the SwaggerEndpoint path as this `c.SwaggerEndpoint("../swagger/v1/swagger.json", "Api V1");.
More details, you could refer to below startup.cs
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.),
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("../swagger/v1/swagger.json", "Api V1");
});
`

How to register OData extension methods in .NetCore 3.1 outside of UseMvc middleware

After migrating my API from .net core 2.2 to 3.1, I am facing some issues to decide which is the best approach I should follow to register OData extension methods for my API. Currently, I have this code
public void ConfigureServices(IServiceCollection services)
{
....
#region OData
services.AddOData();
#endregion
....
}
On the Configure method
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
...
app.UseAuthentication();
app.UseMvc(routeBuilder =>
{
routeBuilder.Select().OrderBy().Filter().MaxTop(1000).Count();
routeBuilder.EnableDependencyInjection();
});
...
}
How can I register Select() OrderBy() Filter() .... using the following approach? Is this the right way to do it, without registering UseMvc?
public void Configure(IApplicationBuilder app, IHostEnvironment env)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
...
}
Check this article out: Experimenting with OData in ASP.NET Core 3.1.
Apparently .NET Core 3.0 and 3.1 don't support OData yet. You can, however, use the beta version, the steps for which is explained in the article.
Update:
They do support OData as of version 7.3.0. However, they cannot be used with endpoint routing yet. You can follow this Github thread for updates, in particular, this answer

ASP.NET Core 1.0 OAuth Server using Openiddict

I would like to use Openiddict OAuth to protect api endpoints in my ASP.NET Core 1.0 Web Application. The api endpoints will be called by a phone app and users must login with username and password.
The flow goes like this:
User can register and login via web application: https://www.domain.com
User install phone app, and they can login and register using the phone app. Login, registration and data access is done via api endpoints: Example: https://www.domain.com/api/service/getsomedata
How can I configure Openiddict OAuth so I can protect the API endpoints using OAuth?
How can I configure Openiddict OAuth so I can protect the API endpoints using OAuth?
Your scenario sounds like a good candidate for the simple "resource owner password credentials" grant, which is basically the OAuth2 equivalent of basic or forms authentication.
Here's what I'd recommend:
Create a new AccountController/RegistrationController API controller responsible of creating new accounts:
Since the user account doesn't exist at this stage, you can't use token authentication here (just like the default AccountController.Register template cannot require cookies authentication before the user is registered).
Configure OpenIddict to enable the token endpoint and allow the resource owner password credentials grant:
services.AddOpenIddict<ApplicationDbContext>()
// Disable the HTTPS requirement during development.
.DisableHttpsRequirement()
// Enable the token endpoint, required to use
// the resource owner password credentials grant.
.EnableTokenEndpoint("/connect/token")
// Enable the password and the refresh token flows.
.AllowPasswordFlow()
.AllowRefreshTokenFlow();
Use the OAuth2 validation middleware to protect your APIs:
To enable token authentication, reference AspNet.Security.OAuth.Validation 1.0.0-alpha2-final package and add app.UseOAuthValidation() before app.UseMvc(). To make authentication mandatory, simply use the [Authorize] attribute like you'd do with cookies authentication.
Don't hesitate to play with this sample. It doesn't use a mobile app for the client-side part, but you should easily understand how it works.
For more information, you can also read this blog post, written by Mike Rousos for the Microsoft .NET Web Development and Tools blog: Bearer Token Authentication in ASP.NET Core
Ok, Thanks #Pinpoint for pointing me to the right direction.
However here is my Startup.cs configuration:
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);
if (env.IsDevelopment())
{
// For more details on using the user secret store see http://go.microsoft.com/fwlink/?LinkID=532709
builder.AddUserSecrets();
}
builder.AddEnvironmentVariables();
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.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddOpenIddict<ApplicationUser, ApplicationRole, ApplicationDbContext>()
.DisableHttpsRequirement()
.EnableTokenEndpoint("/connect/token")
.AllowPasswordFlow()
.AllowRefreshTokenFlow()
.UseJsonWebTokens();
services.AddMvc();
// Add application services.
services.AddTransient<IEmailSender, AuthMessageSender>();
services.AddTransient<ISmsSender, AuthMessageSender>();
}
// 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();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentity();
app.UseOpenIddict();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
RequireHttpsMetadata = false,
Audience = "http://localhost:24624/",
Authority = "http://localhost:24624/"
});
// Add external authentication middleware below. To configure them please see http://go.microsoft.com/fwlink/?LinkID=532715
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
ApplicationDbContext.cs:
public class ApplicationDbContext : OpenIddictDbContext<ApplicationUser, ApplicationRole>
{
public ApplicationDbContext(DbContextOptions options)
: base(options)
{
Database.EnsureCreated();
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
ApplicationRole.cs:
public class ApplicationRole : IdentityRole
{
}
ApplicationUser.cs:
public class ApplicationUser : OpenIddictUser
{
}
ServiceController.cs:
[Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
[Route("api/service")]
public class ServiceController : Controller
{
private readonly UserManager<ApplicationUser> _userManager;
public ServiceController(UserManager<ApplicationUser> userManager)
{
_userManager = userManager;
}
[HttpGet]
[Route("getdata")]
public async Task<IActionResult> GetData()
{
var user = await _userManager.GetUserAsync(User);
if (user == null) return Ok("No user / not logged in");// if Authorize is not applied
return Ok(user);
}
}
The key in here is the ServiceController.cs: [Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)]
#Pinpoint: I didn't use app.UseOAuthValidation() because it was returning 302 and redirect to Account/Login.
So now it works like this:
accessing the http://domain.com, user can register, login, see data, etc.
user can download mobile app, register, login and get data
Implementing the user registration login in the api side is preatty easy and straight forward.
The problem was that using fiddler and issuing a GET to http://domain.com/api/service/getdata was returning a 302 and redirect to Account/Login. If I remove app.UseIdentity(), then if will have returned 401 Unauthorized but user would have not been able to login anymore using the UI http://domain.com. Adding this [Authorize(ActiveAuthenticationSchemes = OAuthValidationDefaults.AuthenticationScheme)] to my ServiceController solved the problem.
#Pinpoint what was the benefit of app.UseOAuthValidation() ?

Resources