ASP .NET Core 2.0 - JWT External Auth - c#

I am trying to get started with authentication on an ASP.NET Core 2.0 web app.
My company is using Ping Federate and I am trying to authenticate my users using the company login page and in return validating the returned token using my signing key (X509SecurityKey down here).
The login page link looks like:
https://companyname.com/authorization.oauth2?response_type=code&redirect_uri=https%3a%2f%2fJWTAuthExample%2fAccount%2fLogin&client_id=CompanyName.Web.JWTAuthExample&scope=&state=<...state...>
Out of the box, I configured the Startup.cs to be able to log in and challenge against this site.
I decorated my HomeController with a [Authorize(Policy="Mvc")] but when I access one of the pages, I just get a blank page.
Debug is not hitting the OnChallenge or OnAuthenticationFailed methods when I add it to options.Events (I think because user needs to be authenticated first).
So, what am I missing in order for a redirect to my authentication website to happen? Is it built in or do I have to do some manual configuration?
(Note: In other web apps, using asp net framework, I use a redirect in an Authorize attribute when authentication fails)
Related post: Authorize attribute does not redirect to Login page when using .NET Core 2's AddJwtBearer
- From this post, does it mean I am not using the right authentication method? I am building a web app, not an API.
namespace JWTAuthExample
{
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
Configuration = configuration;
HostingEnvironment = hostingEnvironment;
string certificatepath = Path.Combine(HostingEnvironment.ContentRootPath, $"App_Data\\key.cer");
KEY = new X509SecurityKey(new X509Certificate2(certificatepath));
}
public IConfiguration Configuration { get; }
public IHostingEnvironment HostingEnvironment { get; }
private string AUTH_LOGINPATH { get; } = Configuration["DefaultAuth:AuthorizationEndpoint"];
private X509SecurityKey KEY { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.IncludeErrorDetails = true;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
// Ensure token expiry
RequireExpirationTime = true,
ValidateLifetime = true,
// Ensure token audience matches site audience value
ValidateAudience = false,
ValidAudience = AUTH_LOGINPATH,
// Ensure token was issued by a trusted authorization server
ValidateIssuer = true,
ValidIssuer = AUTH_LOGINPATH,
// Specify key used by token
RequireSignedTokens = true,
IssuerSigningKey = KEY
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("Mvc", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseBrowserLink();
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
}

Following Brad's suggestion,
Here is a sample of code to perform an OpenId Connect confirguration on ASP NET 2.0
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["AuthoritySite"];
options.ClientId = Configuration["ClientId"];
options.ClientSecret = Configuration["ClientSecret"];
options.Scope.Clear();
// options.Scope.Add("Any:Scope");
options.ResponseType = OpenIdConnectResponseType.CodeIdTokenToken;
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.TokenValidationParameters = new TokenValidationParameters
{
// Compensate server drift
ClockSkew = TimeSpan.FromHours(12),
// Ensure key
IssuerSigningKey = CERTIFICATE,
// Ensure expiry
RequireExpirationTime = true,
ValidateLifetime = true,
// Save token
SaveSigninToken = true
};
});
services.AddAuthorization(options =>
{
options.AddPolicy("Mvc", policy =>
{
policy.AuthenticationSchemes.Add(OpenIdConnectDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
});
});
}
More details here: https://learn.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x?view=aspnetcore-2.1

Related

.NET Core API Problem using multiple authentication schemes

I am having some difficulty getting multiple authenticaion schemes to work with my .NET Core API.
Each works perfectly when set as the Default Scheme to be used, but I cannot get them to chain authenticaion, so if one fails authentication it moves onto the next one in the chain to try and authenticate using that scheme.
I'm using
JWT Bearer Token Authentication
API Key Authentication
As can be seen in the code below I've setup an AuthorizeFilter policy with the multiple schemes, but obviously I'm missing something somewhere along the way and I'm hoping you may be able to spot what I am missing.
Below is the complete start up code I am using.
Kind Regards
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(authOptions =>
{
authOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
authOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(bearerOptions =>
{
bearerOptions.RequireHttpsMetadata = true;
bearerOptions.SaveToken = true;
bearerOptions.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = JWTProvider.CLAIM_ISSUER,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(this.Configuration[JWTSecurityKeyProvider.DEFAULT_JWT_SECURITY_KEYNAME])) { KeyId = "416" },
ValidAudience = JWTProvider.API_AUDIENCE,
ValidateAudience = true,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(1)
};
})
.AddApiKeySupport(options =>
{
});
services.AddControllers(options =>
{
var defaultSchemes = new[] { JwtBearerDefaults.AuthenticationScheme, ApiKeyAuthenticationOptions.DefaultScheme };
var defaultPolicy = new AuthorizationPolicyBuilder(defaultSchemes)
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(defaultPolicy));
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Auth API", Version = "v1" });
});
services.AddSingleton<JWTSecurityKeyProvider>();
services.AddSingleton<JWTProvider>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Auth API v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
According to documentation:
Only one JWT bearer authentication is registered with the default authentication scheme JwtBearerDefaults.AuthenticationScheme. Additional authentication has to be registered with a unique authentication scheme.
Also it rewrites default policy in services.AddAuthorization() insted of services.AddControllers()
In case anyone else experiences this issue, to sort out the problem you need to include the Authorized Schemes as an attribute on each controller. Then everything is works tickety-boo.
E.g.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme + "," + ApiKeyAuthenticationOptions.DefaultScheme)]
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : BaseAuthenticatedController
{

AspNetCore JWT Authentication Without "iat" in token header

I'm trying to configure an authentication in some application that will consume the login validation from another system. Basically, I get a token with the users information, and I have to map it to my database, because the user it was already authenticated. We're doing it with using jwt tokens.
So here's the problem:
The token I'm getting doesn't have "iat" field, and AspNetCore seems to reject the token without that field.
Is there a way to configure the authentication to ignore that field?
Here is the token structure:
TOKEN - HEADER
{
"alg": "HS512"
}
TOKEN - BODY
{
"sub": [mysub],
"user": { ... },
"exp": [timestamp]
}
And here's the configuration:
...
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
...
Edit: An earlier version of this answer mixed up iat and nbf. The answer has been rewritten accordingly.
I tried reproducing your issue in an empty project, and was successfully able to validate a token with the structure you outlined in your question (so without the iat claim).
Setup I used to get this to work:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(o =>
{
o.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
ValidateIssuer = false,
ValidateAudience = false,
SignatureValidator = (t, p) => new JwtSecurityToken(t)
};
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
[Authorize]
[Route("debug")]
public class DebugAuthController : ControllerBase
{
[HttpGet]
public IActionResult Get() => Ok(User.Claims.Single(c => c.Type == "user").Value);
}
I used the following token for testing: eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJteXN1YmplY3QiLCJ1c2VyIjp7Im5hbWUiOiJTdGFja092ZXJmbG93In0sImV4cCI6MTU5MDk5NzMyNn0.FVFl6gDYOrmzj7_6OqHPTxU3mfQWs864u7fBLM5ThuM
It's worth double-checking to see if you're calling both UseAuthentication and UseAuthorization in ConfigureServices, and that you're calling them in that order.
Note: Sample code provided disables virtually all validation checks on the JWT for testing purposes. Do not use this code as-is unless you know what you're doing!

.Net Core Firebase Google Email Authentication With JWT - 401

I'm trying to use firebase email authentication on my .net core web-backend via JWT. I couldn't find detailed and clear example.
I get successful login on my android app then i get the IdToken.
I'm sending IdToken with prefix "Bearer" on PostMan(or app) to my Controller. But it gives 401
Whatever i tried i couldn't get the 200. Only 401.
My service Configuration:
services
.AddAuthentication(o =>
{
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://securetoken.google.com/myapp-c1e32";
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = "https://securetoken.google.com/myapp-c1e32",
ValidateAudience = true,
ValidAudience = "myapp-c1e32",
ValidateLifetime = true
};
});
My Controller:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public ActionResult GetAllRoomsWithOtherInfos(int id)
{
var rooms = _roomService.GetAllRoomsWithOtherInfos(id);
return Ok(rooms);
}
My Request:
http://localhost:5000/Room/GetAllRoomsWithOtherInfos/1
Exception Message On Log:
Bearer was not authenticated. Failure message: IDX10501: Signature
validation failed. Unable to match keys:
Also my app and service configurations
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<TaraftarServerContext>(options => options.UseMySql(Configuration.GetConnectionString(GlobalVariables.DbConnectionName)));
services.AddMvc(options =>
{
options.Filters.Add<GlobalExceptionFilter>();
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2)
.AddJsonOptions(options => options.SerializerSettings.ContractResolver = new DefaultContractResolver())
.AddFluentValidation();
services.AddKendo();
services.AddSignalR(hubOptions =>
{
hubOptions.EnableDetailedErrors = true;
})
.AddJsonProtocol(jsonOptions =>
{
jsonOptions.PayloadSerializerSettings.ContractResolver = new DefaultContractResolver();
jsonOptions.PayloadSerializerSettings.NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore;
});
services.AddWebClientServices();
// configure strongly typed settings objects
var appSettingsSection = Configuration.GetSection("AppSettings");
services.Configure<AppSettings>(appSettingsSection);
// this method is above configuration where the jwt is ( My service Configuration:)
services.AddFirebaseAuthentication(Configuration);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
var supportedCultures = new[]
{
new CultureInfo("tr")
};
app.UseRequestLocalization(
new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("tr"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
FallBackToParentCultures = true,
FallBackToParentUICultures = true,
RequestCultureProviders = null
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Room}/{action=Index}/{id?}");
});
}
.Net Core Version : 2.2

ASP.NET Core 2.1 API JWT token Session.id changes on every request

I have a asp.net core 2.1 API that I connect to using and Angular 4 app and authenticate thru a JWT token. I also have 2 SignalR hubs there as well.
The authentication works nice and I am sure it works because after login I have access to the methods and classes I have set [Authorize] on.
The problem is the injected _accessor.HttpContext.Session.Id changes every time with every request. So the real issues is I cannot use session variables.
I am at a loss and it seems to me I am missing something here.
Can someone please help me with some ideas? Any help is much appreciated.
This is my startup.cs so far:
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment env)
{
Configuration = configuration;
if (env.IsDevelopment())
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath+"/logs/", "log-{Date}.txt"))
//, outputTemplate: "{MachineName} {EnvironmentUserName}: {Message:lj}{NewLine}{Exception}"
.WriteTo.Seq("http://192.168.1.164:5341")
.Enrich.WithMachineName()
.Enrich.WithEnvironmentUserName()
.CreateLogger();
}
else
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.RollingFile(Path.Combine(env.ContentRootPath + "/logs/", "log-{Date}.txt"))
.Enrich.WithMachineName()
.Enrich.WithEnvironmentUserName()
.CreateLogger();
}
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
var key = Encoding.ASCII.GetBytes(Configuration.GetSection("AppSettings:Token").Value);
services.AddDbContext<PaymentServicesContext>(options => options.UseSqlServer(Configuration.GetConnectionString("PaymentDatabase")));
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(10);
options.Cookie.HttpOnly = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1).AddSessionStateTempDataProvider();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters =
new TokenValidationParameters
{
LifetimeValidator = (before, expires, token, param) =>
{
return expires > DateTime.UtcNow;
},
ValidateAudience = false,
ValidateIssuerSigningKey = true,
ValidateIssuer = false,
ValidateActor = false,
ValidateLifetime = true,
IssuerSigningKey = new SymmetricSecurityKey(key)
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
var accessToken = context.Request.Query["access_token"];
var path = context.HttpContext.Request.Path;
if (!string.IsNullOrEmpty(accessToken))
{
context.Token = accessToken;
}
return Task.CompletedTask;
}
};
});
services.AddAutoMapper();
services.AddCors();
services.AddSignalR(options => options.EnableDetailedErrors = true);
///services
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IBrainzService, BrainzService>();
services.AddTransient<ISecurityService, SecurityService>();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
loggerFactory.AddSerilog();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler(builder =>
{
builder.Run(async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
await context.Response.WriteAsync(error.Error.Message);
}
});
});
}
app.UseCors(builder => builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod().AllowCredentials())
.UseStaticFiles()
.UseWebSockets();
app.UseAuthentication();
if (!env.IsDevelopment())
{
app.UseHttpsRedirection();
}
app.UseWebSockets();
app.UseSignalR(
routes =>
{
routes.MapHub<MessagingHub>("/messagingHub");
routes.MapHub<UpdatesHub>("/updatesHub");
});
app.UseSession();
app.UseMvc();
}
}
The configuration works.
The problem was that Angular's HttpClient does not store/get/send cookies by default as I have found out the hard way.
All I had to do was add {withCredentials: true } option to the login request and the session cookie popped into the browser!
To get the session back you have to add this option to all requests and the API will know the session!
One solution would be to create an Interceptor to intercept all requests before it leaves the client application. This tutorial explains it well. You can also do this on a per-request level by setting the withCredentials to true.
const requestOptions = {
headers: new HttpHeaders({
'Authorization': "my-request-token"
}),
withCredentials: true
};

ASP.NET Core Authorize attribute not working with JWT

I want to implement JWT-based security in ASP.Net Core. All I want it to do, for now, is to read bearer tokens in the Authorization header and validate them against my criteria. I don't need (and don't want) to include ASP.Net Identity. In fact, I'm trying to avoid using as many of the things that MVC adds in as possible unless I really need them.
I've created a minimal project, which demonstrates the problem. To see the original code, just look through the edit history. I was expecting this sample to reject all requests for /api/icons unless they provide the Authorization HTTP header with a corresponding bearer token. The sample actually allows all requests.
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Configuration;
using Microsoft.AspNetCore.Routing;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using System;
using Newtonsoft.Json.Serialization;
namespace JWTSecurity
{
public class Startup
{
public IConfigurationRoot Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
IConfigurationBuilder builder = new ConfigurationBuilder().SetBasePath(env.ContentRootPath);
Configuration = builder.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.AddAuthentication();
services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole();
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("supersecretkey")),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
}
});
app.UseMvc(routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}"));
}
}
}
Controllers/IconsController.cs
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace JWTSecurity.Controllers
{
[Route("api/[controller]")]
public class IconsController : Controller
{
[Authorize]
public IActionResult Get()
{
return Ok("Some content");
}
}
}
Found it!
The main problem is in this line:
services.AddMvcCore().AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
I noticed that by switching from AddMvcCore() to AddMvc(), the authorization suddenly started working! After digging through the ASP.NET source code, to see what AddMvc() does, I realized that I need a second call, to IMvcBuilder.AddAuthorization().
services.AddMvcCore()
.AddAuthorization() // Note - this is on the IMvcBuilder, not the service collection
.AddJsonFormatters(options => options.ContractResolver = new CamelCasePropertyNamesContractResolver());
You are also using identity authentication and it contains cookie authentication implicitly. Probably you logged in with identity scheme and it caused successful authentication.
Remove identity authentication if it is not required(if you want only jwt authentication), otherwise specify Bearer scheme for Authorize attribute like below:
[Authorize(AuthenticationSchemes = "Bearer")]
For those who even tried the previews answers and did not get the problem solved, below it is how the problem was solved in my case.
[Authorize(AuthenticationSchemes="Bearer")]
Found the perfect solution to this problem
Your configure services class should look like below
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;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<ApplicationUser, IdentityRole>
(options => options.Stores.MaxLengthForKeys = 128)
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
//options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
//options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
//options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddCookie(cfg => cfg.SlidingExpiration = true)
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
ClockSkew = TimeSpan.Zero // remove delay of token when expire
};
});
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 6;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
});
services.AddAuthentication().AddFacebook(facebookOptions =>
{
facebookOptions.AppId = Configuration["Authentication:Facebook:AppId"];
facebookOptions.AppSecret = Configuration["Authentication:Facebook:AppSecret"];
});
//Seting the Account Login page
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.LoginPath = "/Account/Login"; // If the LoginPath is not set here, ASP.NET Core will default to /Account/Login
options.LogoutPath = "/Account/Logout"; // If the LogoutPath is not set here, ASP.NET Core will default to /Account/Logout
options.AccessDeniedPath = "/Account/AccessDenied"; // If the AccessDeniedPath is not set here, ASP.NET Core will default to /Account/AccessDenied
options.SlidingExpiration = true;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
you can authenticate Web API Controller like below
[Route("api/[controller]")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[ApiController]
public class TaskerController : ControllerBase
{
[HttpGet("[action]")]
//[AllowAnonymous]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
}
and You can use Identity based Authorize attribute as usual like below for MVC controller
public class TaskController : Controller
{
[Authorize]
public IActionResult Create()
{
}
}
Key solution is .AddCookie(cfg => cfg.SlidingExpiration = true)
adding before JWT authentication i.e .AddJwtBearer(//removed for brevity) sets Cookie based authorization as default and so [Authorize] works as usual and whenever you need JWT you have to invoke it explicitly using [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Hope it will help someone who wants Website as front end and clubbing mobile ready Web API as back end .
I just had a similar problem, and turns out that [AllowAnonymous] attribute at controller level overrides any [Authorize] attributes applied to any action within that controller. This is something I didn't know before.
If you are using a custom scheme, you must use
[Authorize(AuthenticationSchemes="your custom scheme")]

Categories