I am using Swashbuckle.AspNet.Core 6.23 with .NET 6; generating an OpenAPI file and viewing it using SwaggerUI. My API uses the client credential flow. When attempting to authenticate in the Swagger UI I get a "404 not found" when the Swagger UI attempts to post back to Swagger UIs index.html. My expectation was that it would redirect to the OAuth login page, but that is not what is happening. Not sure what I am getting wrong here.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {Title = "Digital Room Shipping Rates API", Version = "v1"});
c.SchemaFilter<RequireGroupFilter>();
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "DigitalRoom.ShippingRatesAPI.xml"));
c.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "DigitalRoom.ShippingCarriers.xml"));
c.EnableAnnotations(true, false);
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows()
{
ClientCredentials = new OpenApiOAuthFlow()
{
AuthorizationUrl = new Uri("https://[myoauthserver]/oauth")
}
}
});
});
}
// and in my Configure...
app.UseHttpsRedirection();
app.UseExceptionHandler("/error");
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
// Tried with and without having this commented out...
// c.OAuthClientId("abc");
// c.OAuthClientSecret("def");
// c.OAuthAppName("swagger");
// c.OAuthScopeSeparator(" ");
// c.OAuth2RedirectUrl("https://[myoauthserver]/oauth");
// c.OAuthScopes("test-api");
// c.OAuthUseBasicAuthenticationWithAccessCodeGrant();
});
}
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
Related
I have a Web API based on dot net 6.
This Web API runs on Azure App Service. Azure App Service runs on Linux.
I'm using Open API (Swagger) for test and documentation.
I created 2 appsettings files. (Development and Stage.)
I'm adding ASPNETCORE_ENVIRONMENT variable to the configuration of Azure App Service like below. Functions work for two variables of ASPNETCORE_ENVIRONMENT (Development and Stage).
when I set the 'Stage' value to ASPNETCORE_ENVIRONMENT, the Swagger page is not working. It gives a 404 Not Found Error. But It works for Development.
Here is my startup code to swagger configuration;
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("AllowAnyOrigin", builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); }));
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "AllStore.Api", Version = "v1" });
var securitySchema = new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "bearer",
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
};
c.AddSecurityDefinition("Bearer", securitySchema);
var securityRequirement = new OpenApiSecurityRequirement
{
{ securitySchema, new[] { "Bearer" } }
};
c.AddSecurityRequirement(securityRequirement);
});
}
I really don't understand what is the problem? can anyone help me?
Changed the Configure method in the startup.cs like below
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AllStore.Api v1"));
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
instead of
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "AllStore.Api v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I configured Twitch authentication for my website using this tutorial: https://blog.elmah.io/cookie-authentication-with-social-providers-in-asp-net-core/
In the Twitch dev console I added the https://localhost:44348/signin-twitch url to the callback urls. I've implemented oauth for other providers before, but having troubles with Twitch.
When I try to login, I get the Twitch authorization screen, but after clicking 'Authorize' it redirects me to /signin-twitch + params which returns a Correlation Failed exception.
Exception: An error was encountered while handling the remote login.
I have a feeling it might have to do with the routing. It's setup like this because I have a frontend application with it's own routing (hence the Fallback)
Here is all relevant code.
public void ConfigureServices(IServiceCollection services)
{
...
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/signin";
options.LogoutPath = "/signout";
})
.AddTwitch(TwitchAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ClientId = "xxx";
options.ClientSecret = "xxx";
options.Scope.Add("user:read:email");
options.SaveTokens = true;
options.AccessDeniedPath = "/";
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
endpoints.MapFallbackToController("Index", "Home");
});
}
public class AuthenticationController : Controller
{
[HttpGet("~/signin")]
public IActionResult SignIn(string returnUrl = "")
{
return Challenge(TwitchAuthenticationDefaults.AuthenticationScheme);
}
}
I think that the error occurs because you're trying to access the URL which is assigned as Callback Path.
Try some variant of this:
[HttpGet("~/signin")]
public IActionResult SignIn()
{
var authProperties = _signInManager
.ConfigureExternalAuthenticationProperties("Twitch",
Url.Action("LoggingIn", "Account", null, Request.Scheme));
return Challenge(authProperties, "Twitch");
}
Source: this answer and this one.
Other stuff to check:
Multiple clients with the same Callback Path
CookiePolicyOptions
HTTPS redirect
I deployed my .NET Core application to a Windows server and I have now an issue about opening the Swagger-UI in browser with the public ip-address.
I have no issue when I run Swagger-UI on localhost. However the UI doesn't load when I try to access through server's ip-address.
The error is:
Unable to render this definition The provided definition does not
specify a valid version field.
Please indicate a valid Swagger or OpenAPI version field. Supported
version fields are swagger: "2.0" and those that match openapi: 3.0.n
(for example, openapi: 3.0.0).
As it can be seen in the beginning of the Swagger.json file (The one that is running on the server), then the OpenAPI version field is set to 3.0.1. What I don't get is then why there's still an issue, when the version is clearly set and fulfilled.
I can even copy-paste the json-file into Editor Swagger and see the endpoints through that site.
My code:
namespace ParkShare
{
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.AddDbContextPool<ParkShareDbContext>(options => options.UseSqlServer(Configuration
.GetValue<string>("ConnectionStrings:ParkShareDB")).UseLazyLoadingProxies());
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "ParkShare API",
Description = "BackEnd GET/POST/PATCH/DELETE"
});
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header using the Bearer scheme."
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
});
services.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = false,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtToken:Issuer"],
ValidAudience = Configuration["JwtToken:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtToken:SecretKey"])) //Configuration["JwtToken:SecretKey"]
};
});
//For identity, options changes the default password requirements
services.AddIdentity<AppUser, IdentityRole>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
})
.AddEntityFrameworkStores<ParkShareDbContext>()
.AddDefaultTokenProviders();
//Adding Authentication
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "JwtBearer";
options.DefaultChallengeScheme = "JwtBearer";
})
//Adding JWT Bearer
.AddJwtBearer("JwtBearer", JwtBearerOptions =>
{
JwtBearerOptions.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration.GetValue<string>("AuthSettings:Key"))),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.FromMinutes(5)
};
});
services.AddScoped<IUserModelingService, UserModeling>();
services.AddScoped<IUserPasswordService, UserPasswordService>();
services.AddScoped<IUserService, UserService>();
services.AddScoped<IContactService, ContactService>();
services.AddTransient<IMailService, SendMailService>();
// Email configurations
var emailConfig = Configuration.GetSection("EmailConfiguration").Get<EmailConfigurationModel>();
services.AddSingleton(emailConfig);
services
.AddCors()
.AddControllersWithViews()
.AddNewtonsoftJson();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
app.UseHttpsRedirection();
app.UseRouting();
// global cors policy
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
);
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
}
Many thanks.
I'm using the new ASP.NET JavaScriptServices template and trying to add basic B2C authentication.
I simply want if the user doesn't have an ASP.NET Core Auth Cookie for them to be directed instantly to login to B2C.
I feel I'm close in attempting to force Challenge, but it keeps redirecting back and never actually feeding me to a login page. This is using localhost.
Full code sample can be found here:
https://github.com/aherrick/AToMS.Config.Web
What do I need to change in order to force auth on initial request?
Below is the Startup Configure method in question:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.Use(async (context, next) =>
{
if (!context.User.Identity.IsAuthenticated)
{
// force login here? but it keeps redirecting back infinite loop.
await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme);
return;
}
await next.Invoke();
});
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");
}
});
}
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAdB2C(options => Configuration.Bind("AzureAdB2C", options))
.AddCookie(configureOptions =>
{
});
services.AddMvc();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
}
I want to use JWT bearer token authorization using Swagger in my application.
when I use Postman tool the authorization works fine. But when I try authorize using Swagger the controller method always return unauthorized even after passing the token.
Am I missing some line of code for accepting token? Code implemented is as follows.
public void ConfigureServices(IServiceCollection services)
{
// Register the Swagger generator, defining one or more Swagger documents
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "XYZ API", Version = "v1", Description = "This is a API for XYZ client applications.", });
c.IncludeXmlComments(GetXmlCommentsPath());
c.AddSecurityDefinition("Bearer", new ApiKeyScheme { In = "header", Description = "Please paste JWT Token with Bearer + White Space + Token into field", Name = "Authorization", Type = "apiKey" });
//c.AddSecurityDefinition("basic", new BasicAuthScheme { Type = "basic" });
});
services.AddCors(config =>
{
var policy = new CorsPolicy();
policy.Headers.Add("*");
policy.Methods.Add("*");
policy.Origins.Add("*");
policy.SupportsCredentials = true;
config.AddPolicy("policy", policy);
});
services.AddAuthorization(auth =>
{
auth.AddPolicy("Bearer", new AuthorizationPolicyBuilder()
.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser().Build());
});
services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
ValidAudience = "Audience",
ValidIssuer = "Issuer",
// ClockSkew = TimeSpan.FromMinutes(0)
ClockSkew = TimeSpan.Zero
};
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseCors("policy");
app.UseAuthentication();
app.UseMvc();
// Enable middleware to serve generated Swagger as a JSON endpoint.
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", "XYZ API V1");
});
}
Controller
[Authorize("Bearer")]
[Route("api/[controller]")]
public class ValuesController : Controller
{
}
You also need to add the AddSecurityRequirement in swagger schema definition to indicate that the scheme is applicable to all operations in your API.
like below:
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
So, the complete definition will be:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "XYZ API", Version = "v1", Description = "This is a API for XYZ client applications."});
c.IncludeXmlComments(GetXmlCommentsPath());
c.AddSecurityDefinition("Bearer", new ApiKeyScheme
{
In = "header",
Description = "Please paste JWT Token with Bearer + White Space + Token into field",
Name = "Authorization",
Type = "apiKey"
});
c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
{
{ "Bearer", new string[] { } }
});
});