Swashbuckle.AspNetCore.SwaggerGen v5 does not send the headers - c#

Trying to set up a swagger, and I need to pass two header values. One is the bearer token, and the other one is a tenant id. For some reason, I don't get back the values I put in the Authentication form.
Here is my code but not sure if it is not properly configured or something not working as expected from Swagger.
internal static IServiceCollection AddSwagger(this IServiceCollection collection)
{
collection.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Title",
Version = "v1",
Description = "Description v1."
});
options.AddSecurityDefinition("Authorization", new OpenApiSecurityScheme
{
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
});
options.AddSecurityDefinition("x-client-accessId", new OpenApiSecurityScheme
{
Name = "x-client-accessId",
In = ParameterLocation.Header,
Scheme = "apiKey",
Type = SecuritySchemeType.ApiKey
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
Scheme = "bearer",
BearerFormat = "JWT"
}] = new List<string>()
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Name = "x-client-accessId",
In = ParameterLocation.Header,
Scheme = "apiKey",
Type = SecuritySchemeType.ApiKey
}] = new List<string>()
});
});
return collection;
}
Configure method:
app.UseRouting();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Swagger Movies Demo V1");
c.DisplayRequestDuration();
});
app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); });

You need to add the below code in the Configure method also.
// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger(c => { c.RouteTemplate = "swagger/{documentName}/swagger.json"; });
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUI(options =>
{
// specifying the Swagger JSON endpoint.
options.SwaggerEndpoint($"../swagger/{_apiVersion}/swagger.json", $"MyProject API v1");
options.DisplayRequestDuration(); // Controls the display of the request duration (in milliseconds) for "Try it out" requests.
});

Related

Include culture in razor page url - custom routes

I currently have 3 different cultures.
Actually, my url looks like www.website.com/services, no mater what culture is selected (the culture value is stored in a cookie).
What I would like to do is display the culture directly in the url like this: www.website.com/en/services.
How can I achieve that in .NET6 and with only Razor pages?
Program class
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddMvc().AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix).AddDataAnnotationsLocalization();
builder.Services.Configure<RequestLocalizationOptions>(opt =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("nl")
};
opt.DefaultRequestCulture = new RequestCulture("en");
opt.SupportedCultures = supportedCultures;
opt.SupportedUICultures = supportedCultures;
});
builder.Services.AddHttpContextAccessor();
builder.Services.AddLocalization(opt => { opt.ResourcesPath = "Resources"; });
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/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.UseRequestLocalization(((IApplicationBuilder)app).ApplicationServices.GetRequiredService<IOptions<RequestLocalizationOptions>>().Value);
app.MapRazorPages();
app.Run();
You need to create a custom IPageRouteModelConvention:
public class CultureTemplatePageRouteModelConvention : IPageRouteModelConvention
{
public void Apply(PageRouteModel model)
{
var selectorCount = model.Selectors.Count;
for (var i = 0; i < selectorCount; i++)
{
var selector = model.Selectors[i];
model.Selectors.Add(new SelectorModel
{
AttributeRouteModel = new AttributeRouteModel
{
Order = -1,
Template = AttributeRouteModel.CombineTemplates("{culture?}", selector.AttributeRouteModel.Template),
}
});
}
}
}
Then register it using:
builder.Services.AddRazorPages()
.AddRazorPagesOptions(options =>
{
options.Conventions.Add(new CultureTemplatePageRouteModelConvention());
});
Finally configure RequestLocalizationOptions:
builder.Services.Configure<RequestLocalizationOptions>(opt =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en"),
new CultureInfo("fr"),
new CultureInfo("nl")
};
opt.DefaultRequestCulture = new RequestCulture("en");
opt.SupportedCultures = supportedCultures;
opt.SupportedUICultures = supportedCultures;
// add this line
opt.RequestCultureProviders.Insert(0, new RouteDataRequestCultureProvider());
});
To add the culture to your links you can use the asp-route-culture attribute:
<a asp-route-culture="#Request.RouteValues["culture"]" asp-page="/Contact">Contact</a>
More info about the anchor tag helper and the asp-route-{value} attribute: https://learn.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/built-in/anchor-tag-helper?view=aspnetcore-6.0#asp-route-value
Source: https://www.mikesdotnetting.com/article/348/razor-pages-localisation-seo-friendly-urls

How to handle double requests from the client in ASP.NET Core 5.0?

The Client application makes double queries to a single resource on the server. The first frame has no authorization header and the second frame does. Unfortunately, after reading the first frame, the server does not get the second frame. How to handle it on the ASP.NET CORE 5 server?
Endpoint for testing.
value always = {} when i call from client, from postman everything is working
[ApiExplorerSettings(IgnoreApi = true)]
[HttpPost("Service")]
public IActionResult GetHeader()
{
var value = HttpContext.Request.Headers["Authorization"];
return Ok();
}
app.UseMiddleware<SerilogMiddleware>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/api/socket");
endpoints.UseSoapEndpoint<SVPService.SVPServiceSoap>((options) =>
{
options.Path = "/Service.asmx";
options.Binding = new BasicHttpBinding()
{
TextEncoding = new UTF8Encoding(false),
Security = new BasicHttpSecurity()
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Basic }
}
};
options.SoapSerializer = SoapSerializer.XmlSerializer;
}).RequireAuthorization();
});
app.UseMvc();
Logged Request from client on node.js server to get headers.
First Request Headers
{
'user-agent': 'Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.42000)',
'content-type': 'text/xml; charset=utf-8',
'content-length': '806',
expect: '100-continue',
connection: 'Keep-Alive'
}
Second Request Headers
{
'user-agent': 'Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 4.0.30319.42000)',
'content-type': 'text/xml; charset=utf-8',
authorization: 'Basic dGVzdG93ZV91c2VybmFtZTp0ZXN0b3dlX3Bhc3N3b3Jk',
'content-length': '806',
expect: '100-continue'
}
Its a my startup.cs file
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("CorsPolicy", builder =>
{
builder
//.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials().SetIsOriginAllowed(hostName => true);
}));
services.AddQuartz();
services.Configure<JwtAuthentication>(Configuration.GetSection("JwtAuthentication"));
services.AddAuthentication("BasicAuthentication")
.AddScheme<AuthenticationSchemeOptions, BasicAuthenticationHandler>("BasicAuthentication", null);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "xxx",
Title = "xxx",
Description = "xxx",
Contact = new OpenApiContact
{
Name = "xxx",
Email = "xxx",
Url = new Uri("xxx"),
},
});
// Set the comments path for the Swagger JSON and UI.
string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
MapperConfiguration mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddSignalR().AddNewtonsoftJsonProtocol();
services.AddSingleton<ITokenService, TokenService>();
services.AddSingleton<IPasswordService, PasswordService>();
services.AddSingleton<IUserProfile, UserProfile>();
services.AddSingleton<IReceiptService, ReceiptService>();
services.AddSingleton<ISend, Send>();
services.AddSingleton<IEncryption, Encryption>();
services.AddSingleton<ParkingTicketManagementServiceV3, TicketManagement>();
services.AddScoped<SVPService.SVPServiceSoap, SVPServiceSoap>();
services.AddScoped<IManageSVP, ManageSVP>();
services.AddScoped<IStripeMethods, StripeMethods>();
services.AddScoped<IManageSchedullerRecurringPayment, ManageSchedullerRecurringPayment>();
services.AddRepository();
services.AddSingleton<IAuthorizationHandler, DenyAnonymousAuthorizationRequirement>();
services.AddMvc(options =>
{
options.InputFormatters.Insert(0, new RawJsonBodyInputFormatter());
options.EnableEndpointRouting = false;
})
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddNewtonsoftJson(opt =>
{
opt.SerializerSettings.ContractResolver = new DefaultContractResolver() { NamingStrategy = new LowerCaseNamingStrategy() };
opt.SerializerSettings.StringEscapeHandling = Newtonsoft.Json.StringEscapeHandling.Default;
opt.SerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter());
opt.SerializerSettings.DateTimeZoneHandling = Newtonsoft.Json.DateTimeZoneHandling.Utc;
opt.SerializerSettings.MaxDepth = null;
opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
});
services.AddSwaggerGenNewtonsoftSupport();
services.AddControllers();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "StaticFile")),
RequestPath = "/staticfile"
});
app.UseCors("CorsPolicy");
app.UseHttpsRedirection();
app.UseSwagger();
app.UseReDoc(c =>
{
c.SpecUrl = "xxx";
c.DocumentTitle = "xxx";
});
app.UseMiddleware<SerilogMiddleware>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHub<NotificationHub>("/api/socket");
endpoints.UseSoapEndpoint<SVPService.SVPServiceSoap>((options) =>
{
options.Path = "/Service.asmx";
options.Binding = new BasicHttpBinding()
{
TextEncoding = new UTF8Encoding(false),
Security = new BasicHttpSecurity()
{
Mode = BasicHttpSecurityMode.TransportCredentialOnly,
Transport = new HttpTransportSecurity() { ClientCredentialType = HttpClientCredentialType.Basic }
}
};
options.SoapSerializer = SoapSerializer.XmlSerializer;
}).RequireAuthorization();
});
app.UseMvc();
}
}
Just check if the response has the correct headers
Yes,
To answer my question the header was actually missing the WWW-Authenticate: Basic realm = header.

.NET core JWT authorization failed?

I'm trying to get JWT working here, the token is successfully received on my client end after login but when I request user info at the /info route, the authorization fails. Any help would be much appreciated, thanks in advance.
I get the error:
Route matched with {action = "GetInfo", controller = "Accounts", page = ""}. Executing controller action with signature System.Threading.Tasks.Task`1[ProjectConker.Controllers.AccountsInfo] GetInfo() on controller ProjectConker.Controllers.AccountsController (ProjectConker).
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed.
This is where the token is issued.
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]LoginInfo credentials)
{
if (credentials == null)
{
return BadRequest("Invalid client request");
}
var user = await UserManager.FindByNameAsync(credentials.Username);
await SignInManager.SignInAsync(user, isPersistent: false);
var result = await SignInManager.PasswordSignInAsync(user,
credentials.Password, isPersistent: false, lockoutOnFailure: false);
if (result.Succeeded)
{
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
var signinCredentials = new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256);
var tokeOptions = new JwtSecurityToken(
issuer: "http://localhost:5000",
audience: "http://localhost:5000",
claims: new List<Claim>(){
new Claim("username", credentials.Username)
},
expires: DateTime.Now.AddMinutes(5),
signingCredentials: signinCredentials
);
var tokenString = new JwtSecurityTokenHandler().WriteToken(tokeOptions);
return Ok(new { Token = tokenString, UserName = user.UserName });
}
else
{
return Unauthorized();
}
}
Save token to local storage
public Login(loginForm : ILoginForm) : Observable<ILoginForm>
{
return this.http.post<ILoginForm>(this.accountsUrl + "/login", loginForm, httpOptions)
.pipe(map<any, any>((data, index) => {
localStorage.setItem("auth_token", data.token);
this.username = data.username;
this.loggedIn = true;
console.log(data);
return data;
}));
}
Gets user information
public GetAccountInfo() : Observable<any>
{
httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));
return this.http.get(this.accountsUrl + "/info", httpOptions);
}
returns user info, but authorization fails here
[HttpGet]
[Route("info")]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public async Task<AccountsInfo> GetInfo()
{
var usernameClaim = User.Claims.SingleOrDefault(c => c.Type == "username");
Console.WriteLine(usernameClaim.Value, ConsoleColor.Red);
var user = await UserManager.FindByNameAsync(usernameClaim.Value);
return new AccountsInfo{ DisplayName = user.UserName };
}
My 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.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "http://localhost:5000",
ValidAudience = "http://localhost:5000",
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey#345"))
};
});
//services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
services.AddHttpClient();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddCors(options => options.AddPolicy("CorsPolicy",
builder =>
{
builder.AllowAnyMethod().AllowAnyHeader()
.WithOrigins("*")
.AllowCredentials();
}));
services.AddSignalR();
services.AddEntityFrameworkSqlServer();
services.AddDbContext<ConkerDbContext>(
options => options.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll));
services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<ConkerDbContext>();
services.AddScoped<SearchEngine>();
services.AddTransient<RoadmapService>();
}
// 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("/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.UseSpaStaticFiles();
app.UseAuthentication();
app.UseSignalR(routes =>
{
routes.MapHub<ChatHub>("/api/chat");
});
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
}
});
}
Since Angular's HttpHeaders are immutable, you can't just use httpOptions.headers.set('Authorization', localStorage.getItem('auth_token'));, because it would have no effect on the original object.
First thing is the header is invalid, use the Bearer method provided by Ashique, then your GetAccountInfo call would look like this:
public GetAccountInfo() : Observable<any> {
const headers = new HttpHeaders({'Authorization': 'Bearer ' + localStorage.getItem('auth_token')});
return this.http.get(this.accountsUrl + "/info", {headers});
}
Here I assumed that you don't have other HttpOptions set, so I'm just passing the header to the HttpClient. Try it this way and let us know if it's still not working.
The default way to add authorization header in HTTP request for ASP.NET core token authentication is to add Bearer before the token. So the code should be like this-
httpOptions.headers.set('Authorization', "Bearer " + localStorage.getItem('auth_token'));
You can override the default behavior to remove the need of Bearer.
Please read the below post to help understand the reason for using bearer before token.
https://www.quora.com/Why-is-Bearer-required-before-the-token-in-Authorization-header-in-a-HTTP-request
Also try this,
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
Expires = DateTime.Now.AddDays(1),
SigningCredentials = creds,
IssuedAt = DateTime.Now,
NotBefore = DateTime.Now
};
var tokenHandler = new JwtSecurityTokenHandler();
var token = tokenHandler.CreateToken(tokenDescriptor);
What i mean is instead of creating a new JWTSecurityToken, Create a SecuritTokenDescriptor, instead of using function WriteToken, use CreateToken. I have used JWT in this way and it worked.
In the login end point where you create the token you are using key "**********" and in the Setup class you are using key "superSecretKey#345" , this is the problem , the authentication middleware is trying to validate the incoming JWT tokens with key different from the key used to issue the token , you have to use the same key for both issuing and validating , also put the key in somewhere else like "appsettings" to avoid this conflict
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("**********"));
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.UTF8.GetBytes("superSecretKey#345"))

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.

Identity Server 4 Swagger Authentication

I'm currently having issues using swagger to authorize an api call to identity server 4.
My swagger dependency is using swashbuckle version
-beta client object in the identity server 4 looks like
new Client
{
ClientId="swagger",
Enabled = true,
ClientName="Swagger",
AllowedGrantTypes = GrantTypes.Implicit,
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AllowedScopes = new List<string>
{
"apil"
},
RedirectUris = new List<string>
{
"http://localhost:15138/swagger/ui/popup.html"
},
AllowedCorsOrigins = new List<string>
{
"http://localhost:15138",
"http://localhost:15138"
},
AllowAccessTokensViaBrowser = true,
AllowAccessToAllScopes= true
}
The client object is the identity server 4 model
in the configure method for authentication I have this
app.UseIdentityServer();
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap = new Dictionary<string, string>();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "http://localhost:15138/",
ScopeName = "apil",
RequireHttpsMetadata = false,
});
Using fiddler my get request looks like this
GET
/connect/authorize?state=9321480892748389&nonce=5279296493808222&client_id=swagger&redirect_uri=http%3A%2F%2Flocalhost%3A15138%2Fswagger%2Fui%2Fpopup.html&response_type=id_token%20token&scope=apil
HTTP/1.1
All the necessary parameters are there, the client has the corresponding client id, but The response I get back is a redirect to an error page with the message of an invalid request. I was anticipating a login page to pass in credentials or something similar to get authorized I was wondering what I did wrong for that to happen.
I ran into this same problem, and it was related to a few different things.
Swagger needs a Security Definition configured.
IdentityServerAuthentication AutomaticAuthenticate needs to be true.
Swagger's client-id and client name need to be configured in Startup.cs.
See below:
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Info
{
Version = "v1",
Title = "my api title",
Description = "my api desc",
TermsOfService = "None",
Contact = new Contact { Name = "contact" }
});
var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "api.xml");
c.IncludeXmlComments(filePath);
c.AddSecurityDefinition("oauth2", new OAuth2Scheme
{
Type = "oauth2",
Flow = "implicit",
AuthorizationUrl = "https://url",
Scopes = new Dictionary<string, string>
{
{ "api-name", "my api" }
}
});
});
}
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseIdentity();
app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions
{
Authority = "https://url",
RequireHttpsMetadata = true,
ApiName = "api-name",
ApiSecret = "api-secret",
AllowedScopes = { "api-name", "openid", "email", "profile" },
ClaimsIssuer = "https://url",
AutomaticAuthenticate = true,
});
app.UseStaticFiles();
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint
app.UseSwagger();
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
app.UseSwaggerUi(c =>
{
c.ConfigureOAuth2("swagger-name", "swagger-secret", "swagger-realm", "Swagger");
});
}

Categories

Resources