CORS issue in IdentityServer 4 - asp.net

I'm using IdentityServer 4 as oauth for my application ( Reactjs ) I'm running Identityserver on port http://localhost:5000 and reactjs app on http://localhost:3000. I have tried using CORS for my idenityserver4 with the following code.
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentityServer(options =>
{
options.Events.RaiseSuccessEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseErrorEvents = true;
})
.AddClientStore<ClientStore>()
//.AddInMemoryApiResources(Config.GetApiResources())
.AddResourceStore<ResourceStore>()
//.AddInMemoryClients(Config.GetClients())
.AddCustomUserStore()
.AddCertificateFromFile();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins( "http://localhost:3000/")
.AllowAnyMethod()
.AllowAnyHeader());
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
{
app.UseForwardedHeaders();
if (environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors("CorsPolicy");
//app.UseCors("default");
app.UseIdentityServer();
app.UseStaticFiles();
// uncomment, if you want to add an MVC-based UI
app.UseMvcWithDefaultRoute();
}
}
Even though I have added localhost:3000 in WithOrigins(), when I try to make a request from react app with axios I'm getting the following blocked error.
Can someone help me to know where I'm doing wrong. I need my application to only allow some list of origins (apps)
Thanks

It's likely this could be because of the trailing slash, this is mentioned in the documentation.
Note: The specified URL must not contain a trailing slash (/). If the URL terminates with /, the comparison returns false and no header is returned.
Try http://localhost:3000 instead of http://localhost:3000/.
I'd also question the usage of both .AllowAnyOrigin() and .WithOrigins(). What you're looking to achieve should be possible using only .WithOrigins().

If you are sending a request to another domain, try sending a http request from your identity server not react.js app. I encountered a similar issue but i just used my API as a proxy and it worked fine.

Related

Blocked by CORS policy despite having policy added to allow any and middleware set

I have been stuck on this issue for days. I'm attempting to add a CORS policy so my application does not require a CORS plugin (extension) to run. I've went through multiple tutorials of how to correctly implement the add policy and how to order the middleware. My application backend should send map data to the front end but without the plugin I receive the infamous
Access to XMLHttpRequest at 'http://localhost:5001/maps/NaturalEarthII/tilemapresource.xml' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. error. From my understanding everything is setup as it should be but the results are not agreeing, Please help! There is no controllers
ConfigureServices method:
public void ConfigureServices(IServiceCollection services)
{
// Enable Gzip Response Compression for SRTM terrain data
services.AddResponseCompression(options =>
{
options.EnableForHttps = true;
options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/vnd.quantized-mesh" });
options.Providers.Add<GzipCompressionProvider>();
});
// Add CORS Service so Tile Server works
services.AddCors(options =>
{
//Here ive attepted implementing default and specific policy
//I've also tried only allowing specific origins and allowing any method + header
//no luck. I will change this to be more specific once i get maps to show
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
);
options.AddPolicy("allowAny",
builder => builder.WithOrigins("http://localhost:5001")
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod().AllowAnyHeader()
);
});
services.AddControllers();
//services.AddSpaStaticFiles(config => config.RootPath = "wwwroot");
services.AddSingleton(typeof(MessageBus), new MessageBus());
}
Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, Microsoft.Extensions.Hosting.IHostApplicationLifetime applicationLifetime)
{
applicationLifetime.ApplicationStopping.Register(OnShutdown);
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// Use Gzip Response Compression for SRTM terrain data
app.UseResponseCompression();
// We must set the Content-Type and Content-Encoding for SRTM terrain files,
// so the Client's Web Browser can display them.
app.Map("/terrain/srtm", fileApp =>
{
fileApp.Run(context =>
{
if (context.Request.Path.Value.EndsWith(".terrain")) {
context.Response.Headers["Content-Type"] = "application/vnd.quantized- mesh";
context.Response.Headers["Content-Encoding"] = "gzip";
}
return context.Response.SendFileAsync(
Path.Combine(Directory.GetCurrentDirectory(), ("data/terrain/srtm/" + context.Request.Path.Value)));
});
});
Console.WriteLine(Path.Combine(Directory.GetCurrentDirectory() + "data"));
// Make the data/maps directory available to clients
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), "data")),
});
app.UseRouting();
//Add the default policy thats create in the conf services method
app.UseCors();
app.UseAuthorization();
app.UseWebSockets();
app.UseEndpoints(endpoints => endpoints.MapControllers().RequireCors("allowAny"));
bus = (MessageBus)app.ApplicationServices.GetService(typeof(MessageBus));
...
In the Add cors Ive attempted implementing default and specific policy
I've also tried only allowing specific origins and allowing any method + header. No luck. I will change this to be more specific once i get maps to show
services.AddCors(options =>
{
options.AddDefaultPolicy(
builder => builder.AllowAnyOrigin()
);
options.AddPolicy("allowAny",
builder => builder.WithOrigins("http://localhost:5001")
.SetIsOriginAllowed((host) => true)
.AllowAnyMethod().AllowAnyHeader()
);
});
After trying endless attempts at making the back end work I gave up and implemented a reverse proxy on the front end. I can now use my web application without a CORS plugin.
proxy.conf.json:
{
"/maps":{
"target": "http://localhost:5001",
"secure": false
}
}
angular.json:
...
"serve": {
"builder": "#angular-devkit/build- angular:dev-server",
"options": {
"browserTarget": "cesium-angular:build",
"proxyConfig": "src/proxy.conf.json"
},
...
You are setting your allowed origin to be the service itself rather than address of your UI.
In your case your origin should be http://localhost:4200 not 5001
Add this to your program.cs
var app = builder.Build();
...
app.UseCors(policy => policy.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()
.WithOrigins("https://localhost:4200"));
Do note that the UseCors() needs to be called before UseAuthentication() and UseAuthorization()
I also can't see where you are calling your ConfigureServices method

.NET API returns 404 response to preflight requests

I want to upload a .NET web API to a domain and then set the frontend to connect to it. The problem is I can't send requests to the API domain because of the same-origin policy. I tried to use CORS and allow all origins but because credentials are being sent through responses, I have to specify the exact domain that can connect to the API.
Here is the code I used in the my backend project:
app.UseCors(x => x
.WithOrigins("https://localhost:3000")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
and I get this error in my console when I try to log in:
Access to fetch at 'https://api.paykanpars.com/api/user/login' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
This works fine when I run the API on localhost but when I run it on my production host, it returns a 404 status in response to the preflight requests. The production host uses Plesk as its web host.
Try to use this syntax
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("AllowAnyOrigin",
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
.....
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
.....
// app.UseRouting();
app.UseCors("AllowAnyOrigin");
// app.UseAuthorization();
// app.UseEndpoints(..
}
Make sure that UseCors should be in the end of Configure method but before UseAuthorizaton. AddCors should be at the top of Configure services.
if only it works then you can replace
builder.AllowAnyOrigin()
with
builder.WithOrigins("https://localhost:3000")

asp.net core identity and identityserver

I'm following this walkthrough on integrating asp.net core identity with IdentityServer but have hit a few roadblocks.
Where I'm updating the ConfigureServices method, if I follow the guide and use
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
I can no longer access any of the account related functions. The routing for the register link changes from
~/Identity/Account/Register
to
~/?area=Identity&page=%2FAccount%2FRegister
Which breaks all account related functions
If I leave it at
services.AddDefaultIdentity<IdentityUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Then the routing still works, I can enter my credentials via the login page and the login is successful, but
SignInManager.IsSignedIn(User)
returns false, so I'm guessing something is fundamentally broken here.
I have added identityserver to my ConfigureServices:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.IdentityResources.GetIdentityResources())
.AddInMemoryApiResources(Config.APIResources.GetApiResources())
.AddInMemoryClients(Config.Clients.GetClients())
.AddAspNetIdentity<IdentityUser>();
Any ideas what needs to change - I'm guessing its something in the latest version of asp.net core that has caused this has it?
The Identity UI is implemented using Razor Pages. For endpoint-routing to map these, add a call to MapRazorPages in your UseEndpoints callback:
app.UseEndpoints(endpoints =>
{
// ...
endpoints.MapRazorPages();
});
In Net Core 2.1 Microsoft have removed the AccountController and moved all the Identity logic to Razor pages (there is no alternative now available) which makes the logic difficult to follow (it reminds me of ASP classic or PHP). The Quickstart in the documentation relies entirely on the AccountController remaining in place (no longer the case) and guess this needs to be rewritten as Razor pages before anything will work. However, there is not a lot of point in doing this whilst the authentication mechanism is broken.
I used the following Startup.cs to demonstrate that authentication no longer works in IdentityServer4 when added to a new Net Core 2.1 project. It should work but shows the following behaviour when accessing a controller method protected by [Authorize] and the challenge presented as a Login page.
1) Entering the incorrect credentials causes the 'Invalid login attempt' text to be displayed
2) Entering correct credentials fails to authenticate and this can be seen by there being no Logout link or debugging and observing User.isAuthenticated is false
A couple of changes can be made to the Startup.cs in order to show authentication works when IdentityServer is disabled and the standard authentication enabled. Simply comment out the block commencing 'services.AddIdentityServer(options =>
' to disable IdentityServer. Next comment out 'useIdentityServer()' and uncomment 'useAuthentication()' and all the authentications work correctly again.
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.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;
});
// Add authentication options
services.AddAuthentication(options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie("Cookies")
.AddOpenIdConnect("oidc", options =>
{
options.SignInScheme = "Cookies";
options.Authority = "http://localhost:5000";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc";
options.ClientSecret = "secret";
options.ResponseType = "code id_token";
options.SaveTokens = true;
options.GetClaimsFromUserInfoEndpoint = true;
options.Scope.Add("api1");
options.Scope.Add("offline_access");
});
// Identity Context
services.AddDbContext<ApplicationDbContext>(options =>
{
options.UseSqlServer(Configuration["IdentityConnection"],
sqlOptions => sqlOptions.MigrationsAssembly(typeof(Startup).GetTypeInfo().
Assembly.GetName().Name));
},
ServiceLifetime.Scoped
);
// Configure default Identity implementation
services.AddDefaultIdentity<ApplicationUser>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultUI()
.AddDefaultTokenProviders()
.AddEntityFrameworkStores<ApplicationDbContext>();
// Add application services.
services.AddTransient<Microsoft.AspNetCore.Identity.UI.Services.IEmailSender, EmailSender>();
services.AddMvc();
// configure identity server with in-memory stores, keys, clients and scopes
services.AddIdentityServer(options =>
{
options.UserInteraction.LoginUrl = "/Identity/Account/Login";
options.UserInteraction.LogoutUrl = "/Identity/Account/Logout";
})
.AddDeveloperSigningCredential()
.AddInMemoryPersistedGrants()
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryClients(Config.GetClients())
.AddAspNetIdentity<ApplicationUser>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// 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();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
//app.UseAuthentication(); // not needed, since UseIdentityServer adds the authentication middleware
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
I'm not sure how to the authentication working in IdentityServer4 since have not followed how it would work in Net Core 2.1. Has anyone got further than me and got this server working?
Figured this out in the end. Seems like a weird bug as MSFT migrates to Razor pages.
All I needed to do was add in the Scaffolding UI and it just started working

ASP.NET Core 2.0, Kubernetes, https missing in reply address?

I have an ASP.NET Core 2.0 web application deployed to a Kubernetes cluster. The application is using Azure AD for authentication to some protected pages. The Kubernetes cluster is setup with a Nginx ingress controller and Let's encrypt to support https.
I can access https://x.eastus.cloudapp.azure.com with no problem and by clicking on a link on the site I'm directed to https://x.eastus.cloudapp.azure.com/link, also with no problems.
But, when I click on a link, which requires a logged in user, I get:
Sign in
Sorry, but we’re having trouble signing you in.
AADSTS50011: The reply address 'http://x.eastus.cloudapp.azure.com/signin-oidc' does not match the reply addresses configured for the application: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx'. More details: not specified
Note that URL above misses https and that is the problem.
I have registered "https://x.eastus.cloudapp.azure.com/signin-oidc" as a reply URL for the application in Azure AD.
But, I don't understand why the reply url used when logging in is missing https.
If I deploy the exact same application to an Azure Web App, I don't get this problem.
What could be the issue?
This is my Ingress YAML file:
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: x-ingress
annotations:
kubernetes.io/ingress.class: nginx
# Add to generate certificates for this ingress
kubernetes.io/tls-acme: 'true'
spec:
rules:
- host: x.eastus.cloudapp.azure.com
http:
paths:
- path: /
backend:
serviceName: x-service
servicePort: 80
tls:
# With this configuration kube-lego will generate a secret called `x-tls-secret`
# for the URL `x.eastus.cloudapp.azure.com`
- hosts:
- "x.eastus.cloudapp.azure.com"
secretName: x-tls-secret
I have have the following code in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders =
ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddAzureAd(options => Configuration.Bind("AzureAd", options))
.AddCookie();
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseForwardedHeaders();
app.UseStaticFiles();
app.UseAuthentication();
}
Add a custom Middleware in the Configure method to perform the manual http-https redirection
app.Use(async (context, next) =>
{
if (context.Request.IsHttps || context.Request.Headers["X-Forwarded-Proto"] == Uri.UriSchemeHttps)
{
await next();
}
else
{
string queryString = context.Request.QueryString.HasValue ? context.Request.QueryString.Value : string.Empty;
var https = "https://" + context.Request.Host + context.Request.Path + queryString;
context.Response.Redirect(https);
}
});
I know this is old but I had a similar problem with ASP .NET Core 3 and the IdentityModel library with a custom OpenID Connect provider. The redirect urls would also be http instead of https.
There were multiple things I had to do to finally get it working:
configure forward headers to tell ASP .NET Core to use the x-forwared-for and x-forwarded-proto headers the ingress is sending
tell ASP .NET Core to trust proxies coming from the Kubernetes networks
raise the forward limit that tells ASP .NET Core how many forwards can happen
Here is the relevant Startup code (be sure to modify the IP ranges according to your cluster:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<ForwardedHeadersOptions>(options =>
{
// This tells the middleware to update IP and scheme according to forwarded headers
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
options.ForwardLimit = 2;
// Add Kubernetes Networks to trusted networks
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.42.0.0"), 16));
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("the IP range of our cluster nodes"), 27));
});
// ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// Enable to process forward headers from https proxy like ingress nginx, this middleware must run before others
// configuration is above in ConfigureServices
app.UseForwardedHeaders();
}
// ...
}
Strangely this would work only sometimes. Sometimes it would still try to redirect to a HTTP page and after a redeploy it would suddenly work. So I also added app.UseHttpsRedirection(); to the Configure method and it finally seems to work every time.
Edit: A little down in this blog it's explained on why you need to set the ForwardLimit at least to 2.

Can't read swagger JSON file on ASP.NET Core 1.2 Application after hosting into local IIS

After hosting my asp.net core 1.2 application, I am getting an error as:
swagger is unable to find the swagger.json file.
I have tried to solve the problem by giving a virtual path name app.UseSwaggerUI() but it's not working.
Edit to clarify question based on comments:
After hosting Asp.net core application in IIS, the swagger.json file is generating on localhost:<random_port>/swagger/v1/swagger.json path.
How do I serve the swagger.json file on a custom route like:
localhost:<random_port>/virtualpathname/swagger/v1/swagger.json
I have tried to set a virtual path in app.UseSwaggerUI() like {virtualpathname}/swagger/v2/swagger.json but still it is not working
Could be a few reasons for this - one being that .Net Core doesnt serve static files by default (although looking at online examples this doesnt seem to be an issue).
If you havent already, try installing the package Microsoft.AspNetCore.StaticFiles and adding UseStaticFiles() in your Configure() method in Startup.cs with the following configuration. I dont think that the order is important, but this is the order I have mine running in a working app.
public void Configure(...)
{
// Enable middleware to serve static files (like .json)
app.UseStaticFiles();
//Enable middleware for your API
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.SwaggerEndpoint("/swagger/v1/swagger.json", "YourApp API V1");
});
}
You will also need SwaggerGen middleware configured in your ConfigureServices() method.
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new Info { Title = "api_name", Version = "1.0"});
});
Edit Based on comment - to serve swagger json on a custom route:
// Enable middleware to serve generated Swagger as a JSON endpoint on a custom endpoint
app.UseSwagger(c => c.RouteTemplate = "custom/swagger/{documentName}/swagger.json");
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
// Using custom endpoint defined above
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/custom/swagger/v1/swagger.json", "YourApp API V1");
});
If you need to serve SwaggerUI on a custom route as well, then:
// Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)
// Using custom endpoint defined above
// And serving UI on a custom route
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/custom/swagger/v1/swagger.json", "YourApp API V1");
c.RoutePrefix = "custom"; // serves UI on http://{domain}:{port}/custom/
});
I suggest you to perform the two next steps.
First, open your project web.config and enable stdoutLogEnabled. (Remember to create the folder logs on your application folder and give it proper permissions)
Second, make sure you're doing the right configuration. (https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger)
Note: The first step is going to give you more details about the error you're facing.
In my case the issue was the virtual directory which I fixed by adding a relative path(../). In any case make sure you setup ConfigureServices first, then when Configure make sure everything is in order, UseSwagger should be before UseMvc and at the end UseSwaggerUI
public void ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddMvc();
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new Swashbuckle.AspNetCore.Swagger.Info { Title = "Utility", Version = "v1" });
});
// initialize configuration
var conf = new ConfigurationHelper(Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath);
Configuration = conf.Configuration; // just in case
// inject the RestApiWrapperService as singleton into the services configuration
var restService = new RestApiWrapperService(conf);
services.AddSingleton<IRestApiWrapperService>(restService);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseSwagger();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
// app.UseMvc();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
app.UseSwaggerUI(s => {
s.RoutePrefix = "help";
s.SwaggerEndpoint("../swagger/v1/swagger.json", "Utility");
s.InjectStylesheet("../css/swagger.min.css");
});
Change the following on your startup.cs class:
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyService.API v1");
});
To
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/MyWebsiteName/swagger/v1/swagger.json",
"MyService.API v1");
});
[MyWebsiteName] being the name of application configured in IIS.
I happened to have a simple copy paste mistake!
see the first line in below code, the if statement env.IsDevelopment() is causing this section to not run when deployed to IIS. One option is to comment it out!
//if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger(c =>
{
c.RouteTemplate = "swagger/{documentName}/swagger.json";
});
app.UseSwaggerUI(c => {
c.RoutePrefix = "swagger";
c.SwaggerEndpoint("/swagger/v1/swagger.json", "StockOps.WebAPI v1");
});
}

Resources