Handle Azure AD authentication in offline - c#

Called Microsoft's azure ad api to get the configuration in the below code snippet,
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options =>
{
options.Instance = AzureADDetail.AuthenticationEndpoint;
options.Domain = ServerAppConfig.IdaTenant;
options.TenantId = "common";
});
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
AzureADDefaults.BearerAuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
}
During offline startup some API requests are failing, so changed like below and removed AddAuthentication of azure authentication,
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
}
How to make the first sample to work in both online (network connected) and offline (network disconnected) of the machine during startup of c# api application in .netcore?

Related

IdentityServer4 with ASP.NET Identity and client credentials flow

I have IdentityServer4 (IDS4) working with multiple apps (Blazor WASM, ASP.NET Core API) using authorization_code. I'm using ASP.NET Identity for all apps.
I added one more API controller to Identityserver4 app (Controllers/Api/AccountController) and added one client for client_credentials flow. Purpose of this client is to access AccountController from API projects with authorization.
When I call IDS/api/Account/Register, it redirects to login page. Without [Authorize] attribute, it works fine.
How can I configure authorization for a client_credentials flow client?
Here are my IDS configs:
var services = builder.Services;
var configuration = builder.Configuration;
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.Lax;
});
builder.Services.AddControllersWithViews()
.AddSessionStateTempDataProvider()
.AddRazorPagesOptions(options =>
{
//options.AllowAreas = true;
//options.Conventions.AuthorizeAreaFolder("Identity", "/Account/Manage");
});
var connectionString = configuration.GetConnectionString("MSSQLConnection");
var applicationName = builder.Environment.ApplicationName;
services.AddDbContext<AppDbContext>(builder => builder.UseSqlServer(connectionString,
options => options.MigrationsAssembly(applicationName)));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
//.AddDefaultUI();
services.AddLogging(options =>
{
options.AddConsole();
});
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
// ..or configures IIS out-of-proc settings
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
// ..or configures IIS in-proc settings
services.Configure<IISServerOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseSuccessEvents = true;
options.Discovery.ShowIdentityScopes = false;
options.Discovery.ShowApiScopes = false;
options.Discovery.ShowClaims = false;
options.Discovery.ShowExtensionGrantTypes = false;
options.UserInteraction.LoginUrl = "/Account/Login";
options.UserInteraction.LogoutUrl = "/Account/Logout";
options.Authentication = new IdentityServer4.Configuration.AuthenticationOptions()
{
CookieLifetime = TimeSpan.FromHours(10), // ID server cookie timeout set to 10 hours
CookieSlidingExpiration = true
};
})
.AddOperationalStore(options => options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(applicationName)))
.AddConfigurationStore(options => options.ConfigureDbContext = builder =>
builder.UseSqlServer(connectionString, options => options.MigrationsAssembly(applicationName)))
.AddAspNetIdentity<ApplicationUser>()
.AddInMemoryCaching()
.AddResourceOwnerValidator<ResourceOwnerPasswordValidatorService>()
.AddSigningCredential(certificate);
services.AddAuthentication();
services.AddLocalApiAuthentication();
services.AddAuthorization(options =>
{
options.AddPolicy(Roles.Admin,
policy => policy.RequireClaim(JwtClaimTypes.Role, Roles.GetRoleName(UserRole.Admin)));
options.AddPolicy(Roles.SuperAdmin, policy =>
policy.RequireAssertion(context =>
context.User.HasClaim(JwtClaimTypes.Role, Roles.SuperAdmin) || context.User.HasClaim(JwtClaimTypes.Role, Roles.Admin)));
});
services.AddSession(options => {
options.IdleTimeout = TimeSpan.FromMinutes(10);
});
Update (Resolution)
Finally resolved using Bearer AuthenticationScheme support to IDS for Api Endpoints
In Program.cs
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options =>
{
options.Authority = IdentityServerUtility.Authority;
options.ApiName = "IDS_API";
});
In Controller Authorization including policy works fine.
[Route("api/[controller]")]
[ApiController]
[Authorize(AuthenticationSchemes = IdentityServerAuthenticationDefaults.AuthenticationScheme)]
public class AccountController : ControllerBase
{
[Authorize(Policy=Roles.Admin)]
[HttpPost("Register")]
public async Task<AppActionResult> Register([FromBody] UserDto model)
{
//...
}
}
Thank you.

System.InvalidOperationException: IDX20803: Unable to obtain configuration from (OKTA)

I'm trying to implement the Okta.AspNetCore middleware based on their docs on their GitHub page https://github.com/okta/okta-aspnet/blob/master/docs/aspnetcore-webapi.md but to no avail.
This is the stack trace:
Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[3]
Exception occurred while processing message.
System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://company.oktapreview.com/oauth2/aus17ct73zmZrZjbh0h8/.well-known/openid-configuration'.
at Microsoft.IdentityModel.Protocols.ConfigurationManager`1.GetConfigurationAsync(CancellationToken cancel)
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Here is my config in program.cs
using Lamar;
using Lamar.Microsoft.DependencyInjection;
using Microsoft.IdentityModel.Logging;
using Microsoft.OpenApi.Models;
using NCL.StockSupplyChain.Common.Configuration;
using NCL.StockSupplyChain.PurchaseOrder.Web.API;
using NCL.StockSupplyChain.PurchaseOrder.Web.API.Classes;
using NCL.StockSupplyChain.ServiceInventory.Token;
using NCL.StockSupplyChain.ServiceInventory.Workflow;
using Okta.AspNetCore;
var corsPolicyName = "EnableCors";
var builder = WebApplication.CreateBuilder(args);
var logger = builder.Logging.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
IdentityModelEventSource.ShowPII = true;
// use Lamar as DI.
builder.Host.UseLamar()
.ConfigureContainer<ServiceRegistry>(builder =>
{
builder.Scan(x =>
{
x.AssembliesFromApplicationBaseDirectory(filter => filter.FullName is not null && filter.FullName.StartsWith("NCL."));
x.TheCallingAssembly();
x.RegisterConcreteTypesAgainstTheFirstInterface();
});
});
builder.Services.AddControllers()
.AddJsonOptions(jsonOptions => jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null);
builder.Services
.AddRouting(options => options.LowercaseUrls = true)
.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "NCL.StockSupplyChain.PurchaseOrder.Web.API", Version = "v1" });
})
.AddCors(options =>
{
options.AddPolicy(name: corsPolicyName,
corsBuilder =>
{
var allowedOrigins = builder.Configuration.Get<Configuration>().Cors?.AllowedOrigins;
if (allowedOrigins?.Any() ?? false)
{
corsBuilder.WithOrigins(allowedOrigins.ToArray())
.AllowAnyHeader()
.AllowAnyMethod();
}
});
})
.AddHttpClient()
.Configure<Configuration>(builder.Configuration)
.Configure<ConnectionStringsConfiguration>(builder.Configuration.GetSection("ConnectionStrings"))
.Configure<WorkflowConfiguration>(builder.Configuration.GetSection("WorkflowService"))
.Configure<TokenConfiguration>(builder.Configuration.GetSection("TokenService"))
.AddLogging()
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
})
.AddOktaWebApi(new OktaWebApiOptions()
{
OktaDomain = "https://company.oktapreview.com",
AuthorizationServerId = "aus17ct73zmZrZjbh0h8",
BackchannelHttpClientHandler = new MyLoggingHandler(logger),
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage()
.UseSwagger()
.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NCL.StockSupplyChain.PurchaseOrder.Web.API"));
}
app.UseHttpsRedirection()
.UseRouting()
.UseAuthorization()
.UseCors(corsPolicyName)
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
await app.RunAsync();
Does anyone know what I could be missing? This has left me scratching my head.
In the end I got it working with this code:
using Lamar;
using Lamar.Microsoft.DependencyInjection;
using Microsoft.IdentityModel.Logging;
using Microsoft.OpenApi.Models;
using NCL.StockSupplyChain.Common.Configuration;
using NCL.StockSupplyChain.PurchaseOrder.Web.API.Classes;
using NCL.StockSupplyChain.ServiceInventory.Token;
using NCL.StockSupplyChain.ServiceInventory.Workflow;
using Okta.AspNetCore;
var corsPolicyName = "EnableCors";
var builder = WebApplication.CreateBuilder(args);
var logger = builder.Logging.Services.BuildServiceProvider().GetRequiredService<ILogger<Program>>();
// use Lamar as DI.
builder.Host.UseLamar()
.ConfigureContainer<ServiceRegistry>(builder =>
{
builder.Scan(x =>
{
x.AssembliesFromApplicationBaseDirectory(filter => filter.FullName is not null && filter.FullName.StartsWith("NCL."));
x.TheCallingAssembly();
x.RegisterConcreteTypesAgainstTheFirstInterface();
});
});
builder.Services.AddControllers()
.AddJsonOptions(jsonOptions => jsonOptions.JsonSerializerOptions.PropertyNamingPolicy = null);
builder.Services
.AddRouting(options => options.LowercaseUrls = true)
.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "NCL.StockSupplyChain.PurchaseOrder.Web.API", Version = "v1" });
})
.AddCors(options =>
{
options.AddPolicy(name: corsPolicyName,
corsBuilder =>
{
var allowedOrigins = builder.Configuration.Get<Configuration>().Cors?.AllowedOrigins;
if (allowedOrigins?.Any() ?? false)
{
corsBuilder.WithOrigins(allowedOrigins.ToArray())
.AllowAnyHeader()
.AllowAnyMethod();
}
});
})
.AddHttpClient()
.Configure<Configuration>(builder.Configuration)
.Configure<ConnectionStringsConfiguration>(builder.Configuration.GetSection("ConnectionStrings"))
.Configure<WorkflowConfiguration>(builder.Configuration.GetSection("WorkflowService"))
.Configure<TokenConfiguration>(builder.Configuration.GetSection("TokenService"))
.AddLogging()
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
})
.AddOktaWebApi(new OktaWebApiOptions()
{
OktaDomain = "https://org.oktapreview.com",
AuthorizationServerId = "aus17ct73zmZrZjbh0h8",
Audience = "0oa173fmolqChJbj00h8"
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage()
.UseSwagger()
.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "NCL.StockSupplyChain.PurchaseOrder.Web.API"));
// This line enables more detailed stacktrace for auth code errors, Idendity framework, auth middleware etc
IdentityModelEventSource.ShowPII = true;
}
app.UseHttpsRedirection()
.UseRouting()
.UseAuthentication()
.UseAuthorization()
.UseCors(corsPolicyName)
.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
await app.RunAsync();
These two lines of code have to be in this order otherwise it will error:
.UseAuthentication()
.UseAuthorization()
Hopefully this helps someone who gets stuck with the error: System.InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://org.oktapreview.com/oauth2/aus17ct73zmZrZjbh0h8/.well-known/openid-configuration'. in the future because I thought I had configured Okta incorrectly.

Invalid Authentication method, invalid client error when trying to connect with OIDC

We're trying to develop an ASP Net application, and we have to use an oidc authentication system.
we've got our own OIDC server. When we try to connect we've got an error message :
"System.Exception: An error was encountered while handling the remote login.
---> Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectProtocolException: Message contains error: 'invalid_client', error_description: 'Invalid authentication method for accessing this endpoint.', error_uri: 'error_uri is null'."
here is our ConfigureService function and Configure function:
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
services.AddMvc();
services.AddOptions();
SetGlobalConfig();
services.AddHttpContextAccessor();
services.AddResponseCompression(options =>
{
options.Providers.Add<BrotliCompressionProvider>();
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
services.AddAntiforgery(options =>
{
options.Cookie.Name = "X-CSRF-TOKEN-OurAppli";
options.HeaderName = "X-CSRF-TOKEN-OurAppli";
options.FormFieldName = "X-CSRF-TOKEN-OurAppli";
});
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(5);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
options.Cookie.SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.Always;
});
services.AddDetection();
services.AddControllersWithViews();
var identityUrl = Configuration.GetValue<string>("IdentityUrl");
var bearerUrl = Configuration.GetValue<string>("BearerUrl");
var callBackUrl = Configuration.GetValue<string>("CallBackUrl");
var sessionCookieLifetime = Configuration.GetValue("SessionCookieLifetimeMinutes", 60);
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(setup => setup.ExpireTimeSpan = TimeSpan.FromMinutes(sessionCookieLifetime))
.AddOpenIdConnect(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.ResponseType = "code";
options.ClientId = "*******";
options.ClientSecret = "********";
options.Authority = "https://ourOIDCServer/ourOIDCServerwebsso/oauth2/multiauth";
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Clear();
options.Scope.Add("openid uid isMemberOf");
options.SaveTokens = true;
options.Configuration = new OpenIdConnectConfiguration
{
AuthorizationEndpoint = "https://ourOIDCServer/ourOIDCServerwebsso/oauth2/multiauth/authorize",
TokenEndpoint = "https://ourOIDCServer/ourOIDCServerwebsso/oauth2/multiauth/access_token",
UserInfoEndpoint = "https://ourOIDCServer/ourOIDCServerwebsso/oauth2/multiauth/userinfo",
EndSessionEndpoint = "https://ourOIDCServer/ourOIDCServerwebsso/oauth2/multiauth/connect/endSession",
RegistrationEndpoint = "https://ourOIDCServer/ourOIDCServer/oauth2/multiauth/connect/register",
JwksUri = "ourOIDCServer/ourOIDCServer/oauth2/multiauth/connect/jwk_uri",
};
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("acr_values","ourACR");
context.ProtocolMessage.SetParameter("authlevel", "3");
var byteArray = Encoding.ASCII.GetBytes(options.ClientId + ":" + options.ClientSecret);
context.Request.Headers.Add("Authorization", "Post " + byteArray);
return Task.FromResult(0);
}
};
});
services.AddHsts(options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromDays(365);
});
}
public void Configure(IApplicationBuilder app, IHostEnvironment 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.UseResponseCompression();
app.UseDetection();
app.UseSession();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.Use(async (context, next) =>
{
context.Response.Headers.Add("X-Frame-Options", "DENY");
context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; font-src 'self';script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; img-src 'self'; style-src 'self' 'unsafe-inline'");
await next();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "Index",
pattern: "{controller=Home}/{action=Index}");
});
app.UseStaticFiles();
}`
And there is our HomeController :
[Authorize]
public ActionResult Index()
{
Console.WriteLine(_user);
Console.WriteLine(_habilitation);
dynamic models = new ExpandoObject();
models.detection = _detectionService;
if (!string.IsNullOrEmpty(_user) || _user != "")
{
models.Authorization = _habilitation;
models.Name = _user;
Console.WriteLine(models.Name + " Auth= " + models.Authorization);
models.Habilitation = 1;
}
return View("Index", models);
}
I never used an OIDC authentication system with ASP Net so i don't know if there is something wrong with my code...
Hope someone can help me!
Can you add to the question how the client is defined? What OIDC provider are you using?
The problem seems that the client asks for "authorization code flow" here:
options.ResponseType = "code";
but the OIDC provider does not have that enabled. I would check that first.

Exception: Correlation failed. Unknown location (Azure AD B2C)

I have an ASP.NET Core 2.2 application with the following configuration:
And this is my Startup.cs class
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = $"{ Configuration.GetValue<string>("AzureAdB2C:Instance") }/{ Configuration.GetValue<string>("AzureAdB2C:Tenant") }/{ Configuration.GetValue<string>("AzureAdB2C:SignUpSignInPolicyId") }/v2.0";
options.ClientId = Configuration.GetValue<string>("AzureAdB2C:ClientId");
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.ClientSecret = Configuration.GetValue<string>("AzureAdB2C:ClientSecret");
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
When I run this the B2C login page pops-up, however once I enter the credentials I get the error below. URL is redirected to https://localhost:xxxx/signin-oidc
How can I solve this problem?
This is the similar issue, the same error occurred when external login is canceled and redirects to the source application. You could use OnRemoteFailure to handle the exception.
AddOpenIdConnect("oidc", options => {
// ...
options.Events = new OpenIdConnectEvents
{
OnRemoteFailure = ctx =>
{
ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message));
ctx.HandleResponse();
return Task.FromResult(0);
}
};
});

How to manually validate JWT based on username in the url asp.net core

I am trying to use asp.net identity framework for mvc and JWT for APIs. Requirement is that api accessing username/device is in the url, for example, api/v1/username/accounts. The user or the device that JWT was issues has username in it. Can I do it in the startup.cs file. The following code was working fine until recently then it started doing strange thing by allowing asp.net identity to use JWT protected APIs. I want to check if username in the url api/v1/username/accounts matches the token one .Following is my code. Thanks for your insights.
public class Startup
{
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
Log.Logger = new LoggerConfiguration()
.MinimumLevel
.Warning()
.WriteTo.RollingFile("Logs/GateKeeperLog-{Date}.txt")
.CreateLogger();
}
public static IConfigurationRoot Configuration { get; set; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration);
services.AddDbContext<GkEnterpriseContext>(options =>
options.UseSqlServer(Configuration["Database:Connection"]));
services.AddTransient<IUnitOfWork, UnitOfWork>();
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<GkEnterpriseContext>()
.AddDefaultTokenProviders();
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.Formatting = Formatting.Indented;
}).AddJsonOptions(options =>
{
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
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.AddSerilog();
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseWhen(context => context.Request.Path.Value.Contains("/api")
, builder =>
{
builder.UseJwtBearerAuthentication(new JwtBearerOptions
{
AutomaticAuthenticate = true,
AutomaticChallenge = true,
TokenValidationParameters = new TokenValidationParameters
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Audidence"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes
(JwtTokenIssuer.PrivateKey)),
ValidateLifetime = true,
NameClaimType = JwtRegisteredClaimNames.FamilyName
}
});
app.UseWhen(context => context.Request.Path.Value.StartsWith("/api/v2/computers/")
, builder1 =>
builder1.MapWhen((ctx) =>
{
var deviceName = ctx.User.Claims.SingleOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Name)?.Value ?? "";
var testPath = new Microsoft.AspNetCore.Http.PathString($"/api/v2/computers/{deviceName}");
var pathMatch = ctx.Request.Path.StartsWithSegments(testPath);
return String.IsNullOrWhiteSpace(deviceName) || !pathMatch;
}, cfg =>
{
cfg.Run((req) =>
{
req.Response.StatusCode = 403;
return req.Response.WriteAsync("Sorry , you cant access this resource...");
});
}));
});
app.UseIdentity();
app.UseStatusCodePagesWithReExecute("/StatusCodes/{0}");
app.UseStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
routes.MapRoute(
name: "defaultApi",
template: "api/v2/{controller}/{id?}");
});
}
}
// JWT issung code block, it is now issuing tokens as expected, only validating is the problem.
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub,computer),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
new Claim(JwtRegisteredClaimNames.FamilyName,"GkDevice")
};
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(PrivateKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: Startup.Configuration["Tokens:Issuer"],
audience: Startup.Configuration["Tokens:Audidence"],
claims: claims,
notBefore: DateTime.Now,
expires: DateTime.Now.AddYears(10),
signingCredentials: creds
);
var data = new Token
{
Message = "New Token was issued",
Jwt = new JwtSecurityTokenHandler().WriteToken(token),
Iat = GkHelpers.ConvertTimeToEpoch(token.ValidFrom) ,
Exp = GkHelpers.ConvertTimeToEpoch(token.ValidTo)
};
return data;
Something like this might help you --
app.UseWhen(context => context.Request.Path.Value.StartsWith("/api"), builder =>
{
...jwt code...
builder.MapWhen((ctx) =>
{
var userName = ctx.User.Claims.SingleOrDefault(c => c.Type == System.Security.Claims.ClaimTypes.Name)?.Value ?? "";
var testPath = new Microsoft.AspNetCore.Http.PathString($"/api/v2/computers/{userName}/");
var pathMatch = ctx.Request.Path.StartsWithSegments(testPath);
return String.IsNullOrWhiteSpace(userName) || !pathMatch;
}, cfg =>
{
cfg.Run((req) =>
{
req.Response.StatusCode = 403;
return req.Response.WriteAsync("");
});
});
});
The inner MapWhen will trigger when the username in the "Name" claim (configure how to get the username here) does not match the given Path. It will then immediately execute the following request pipeline which will return a 403 code with an empty response body.
I am unsure, however, if you can process Identity-related items in the same request pipeline in which you actually add identity. You might have to extract that MapWhen code outside of the UseWhen.

Categories

Resources