How to make ASP.NET/React app serve SPA from subpath? - asp.net

I have a stock aspnetcore and reactjs app, generated from the starter template (dotnet new react). I would like the SPA app to be served from a subpath off the root url; e.g. instead of the sample app being https://localhost:5001/counter I'm looking for it to instead be served from https://localhost:5001/myapp/counter.
I changed the Startup.cs from:
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
to this:
app.Map(new Microsoft.AspNetCore.Http.PathString("/myapp"), appMember =>
{
appMember.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
});
This sort of works. If I browse to https://localhost:5001/myapp/ it appears to load the index.html, but the static files are attempting to load from the root path and not the subpath.
What needs to be changed so that the react app uses the subpath as the root? I'd like this to work both in the interactive VS dev environment and when deployed, likely on IIS. It seems like it's close but I'm missing something.
Sample demo of the solution is available here: https://github.com/petertirrell/mvc-spa-demo/tree/master/mvc-spa-demo
Thanks!

Start with moving app to sub-path by adding this to top of package.json:
"homepage": "/myapp/",
When running npm start inside ClientApp folder, app is now serving http://localhost:3000/myapp
Then change Startup.cs like this:
First remove
app.UseSpaStaticFiles()
then add
const string spaPath = "/myapp";
if (env.IsDevelopment())
{
app.MapWhen(ctx => ctx.Request.Path.StartsWithSegments(spaPath)
|| ctx.Request.Path.StartsWithSegments("/sockjs-node"),
client =>
{
client.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.UseReactDevelopmentServer(npmScript: "start");
});
});
}
else
{
app.Map(new PathString(spaPath), client =>
{
// `https://github.com/dotnet/aspnetcore/issues/3147`
client.UseSpaStaticFiles(new StaticFileOptions()
{
OnPrepareResponse = ctx =>
{
if (ctx.Context.Request.Path.StartsWithSegments($"{spaPath}/static"))
{
// Cache all static resources for 1 year (versioned file names)
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(365)
};
}
else
{
// Do not cache explicit `/index.html` or any other files. See also: `DefaultPageStaticFileOptions` below for implicit "/index.html"
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
}
});
client.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
spa.Options.DefaultPageStaticFileOptions = new StaticFileOptions()
{
OnPrepareResponse = ctx => {
// Do not cache implicit `/index.html`. See also: `UseSpaStaticFiles` above
var headers = ctx.Context.Response.GetTypedHeaders();
headers.CacheControl = new CacheControlHeaderValue
{
Public = true,
MaxAge = TimeSpan.FromDays(0)
};
}
};
});
});
}
Don't forget to clear browser history before testing changes for the first time on e.g. Azure.

You can do so by having:
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
in your ConfigureServices and:
string spaPath = "/myapp";
if (env.IsDevelopment())
{
app.MapWhen(y => y.Request.Path.StartsWithSegments(spaPath), client =>
{
client.UseSpa(spa =>
{
spa.UseReactDevelopmentServer(npmScript: "start");
});
});
}
else
{
app.Map(new PathString(spaPath), client =>
{
client.UseSpaStaticFiles();
client.UseSpa(spa => {});
});
}
It should be noted that in development we use .MapWhen because .Map would cause your static files to be available at /myapp/myapp/[file] as opposed to /myapp/[file].

I combined some of each answer to get it working. You need to add the "homepage": "/myapp" to the package.json as well as the config changes to Startup.cs. I used the simpler config provided in Shoe's answer without all the extra caching and sockets directives, as I don't need those.
Because my application also used React Router for SPA routing under /myapp I also needed to add basename to the root BrowserRouter:
<BrowserRouter basename="/myapp" >...</BrowserRouter>

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

Aspnet Core 3.1 MVC Razor pages gets 302 after authenticating with Identity Server 4 OpenIdConnect

I have an MVC Razor pages app which I want to hook into our Identity Server Implementation. We have followed the tutorial in their quickstart https://identityserver4.readthedocs.io/en/latest/quickstarts/2_interactive_aspnetcore.html and got it working in a brand new project, so we assume the client and IDS configuration is OK.
However, when we port it into our RazorPages application we get into a loop. We are sent off to the IDS, we log in and we're sent back to the signin-oidc page. This page seems to generate a 302.
Please see this network trace. Each time the request is made a new "code_challenge" parameter is requested
My startup is (sorry long and) here:
public void ConfigureServices( IServiceCollection services )
{
services.AddIdentity<ApplicationUser, IdentityRole>( options =>
{
options.SignIn.RequireConfirmedEmail = true;
} )
.AddEntityFrameworkStores<CourseRegContext>()
.AddDefaultTokenProviders();
var cs = Configuration.GetConnectionString( "DefaultConnection" );
var skipHTTPS = Configuration.GetValue<bool>( "LocalTest:skipHTTPS" );
services.Configure<MvcOptions>( options =>
{
if ( /*Environment.IsDevelopment() && */!skipHTTPS )
{
options.Filters.Add( new RequireHttpsAttribute() );
options.EnableEndpointRouting = false;
}
} );
services.AddMvc()
.SetCompatibilityVersion( CompatibilityVersion.Version_2_1 )
.AddNewtonsoftJson(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddMvc( config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add( new AuthorizeFilter( policy ) );
config.EnableEndpointRouting = false;
} ).AddRazorPagesOptions( options =>
{
options.Conventions.AllowAnonymousToFolder( "/Oops" );
options.Conventions.AuthorizeFolder( "/Test" );
} );
services.AddMemoryCache();
services.AddHttpsRedirection( options =>
{
options.RedirectStatusCode = StatusCodes.Status307TemporaryRedirect;
if ( Environment.IsDevelopment() )
{
options.HttpsPort = 44311;
}
else
{
options.HttpsPort = 443;
}
} );
services.AddHsts( options =>
{
options.Preload = true;
options.IncludeSubDomains = true;
options.MaxAge = TimeSpan.FromHours( 1 );
} );
AddAuthorisation( services );
}
private void AddAuthorisation( IServiceCollection services )
{
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
services.AddAuthentication( options =>
{
options.DefaultScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
} )
.AddCookie( "Cookies" )
.AddOpenIdConnect( "oidc", options =>
{
options.Authority = "https://localhost:41012";
options.ClientId = "validId";
options.ClientSecret = "somesecret";
options.ResponseType = "code";
options.Scope.Add( "roles" );
options.GetClaimsFromUserInfoEndpoint = true;
options.SaveTokens = true;
//options.UsePkce = true;
} );
//services.AddAuthorization();
services.AddAuthorization( options =>
{
options.AddPolicy( AuthPolicies.GlobalAdmin, policy =>
policy.RequireRole( ApplicationRoles.Administrator ) );
options.AddPolicy( AuthPolicies.CourseAdmin, policy =>
policy.RequireRole(
ApplicationRoles.Administrator,
ApplicationRoles.CourseAdmin ) );
options.AddPolicy( AuthPolicies.Presenter, policy =>
policy.RequireRole( ApplicationRoles.CourseViewer ) );
options.AddPolicy( AuthPolicies.UserAdmin, policy =>
policy.RequireRole( ApplicationRoles.UserAdmin, ApplicationRoles.Administrator ) );
} );
}
// 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();
app.UseBrowserLink();
}
else
{
app.UseExceptionHandler( "/Oops/C500" );
app.UseHsts( options => options.MaxAge( hours: 1 ).IncludeSubdomains() ); //todo when confident it is working, use 180 days
}
app.UseStatusCodePagesWithRedirects( "/Oops/C{0}" );
app.UseXXssProtection( options => options.EnabledWithBlockMode() );
app.UseXContentTypeOptions();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints( endpoints =>
{
//endpoints.MapDefaultControllerRoute()
//.RequireAuthorization();
} );
app.UseMvc();
}
I wonder if it is something do with MVC and EndPoints as part of the upgrade to .NET Core 3.1, but I am unsure how to keep the rest of the project working and yet still get the IDS integration.
I started a blank project and got it authenticating with no other code attached and it worked. So I then put each line back fixing each DI error until I saw the error again. It was to do with services.AddIdentity<ApplicationUser, IdentityRole> which I guess makes sense as it adds login authentication stuff as well as the managers. So, this line needs to be removed, but also need to remove all references to the usermanager etc too.
One mass refactor later and have now broken the redirect to IDS, but hopefully will get it redirecting. (it's now just acting like there is no authentication at all)

'Invalid token' when using webapi thru Swagger authenticated by Azure AD

I try to use AAD authentification on my WebApi (dotnet core 3.1) from swagger (Swashbuckle) client.
In my Startup class, I've configured like follow:
// Configure authentication
services.AddAuthentication(AzureADDefaults.JwtBearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
my settings for AzureAd:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "xxxx-xxxxx1b",
"Domain": "myoffice.onmicrosoft.com",
"TenantId": "xxxxx-xxxxa5",
"Scope": "api://xxxxxxxx-abc3cff48f1b/Full.Access",
"ScopeDescription": "Full Access"
},
...
services.AddSwaggerGen(c =>
{
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Type = SecuritySchemeType.OAuth2,
In = ParameterLocation.Header,
Flows = new OpenApiOAuthFlows()
{
Implicit = new OpenApiOAuthFlow
{
TokenUrl = new Uri($"Configuration["AzureAd:Instance"]}/{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/token"),
AuthorizationUrl = new Uri($"{Configuration["AzureAd:Instance"]}/{Configuration["AzureAd:TenantId"]}/oauth2/v2.0/authorize"),
Scopes =
{
{
Configuration["AzureAd:Scope"],Configuration["AzureAd:ScopeDescription"]
}
}
}
}
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
And in my Configure method:
app.UseSwaggerUI(c =>
{
c.RoutePrefix = string.Empty;
c.SwaggerEndpoint($"/swagger/{ApiVersion}/swagger.json", ApiName);
c.OAuthClientId(Configuration["AzureAd:ClientId"]);
c.OAuthScopeSeparator(" ");
});
Swagger corectly log to AAD using my credential and when I use a route protected by [Authorize] the token is correcly sent to the API by I receive a 401 error with the following message:
www-authenticate: Bearer error="invalid_token"error_description="The issuer 'https://login.microsoftonline.com/{tenantid}/v2.0' is invalid"
The url https://login.microsoftonline.com/{tenantid}/v2.0 is in the token in the iss section.
What is wrong?
According to your error and your code, you do not tell your application the ValidIssuer. So you get the error. Please add the following code in the method ConfigureServices of startup.cs file
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidIssuers = new[] {
},
});
For example
Configure Azure AD for your web API. For more details, please refer to the document
a. Create Azure AD web api application
b. Expose API
c. Configure code
config file
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"ClientId": "[Client_id-of-web-api-eg-2ec40e65-ba09-4853-bcde-bcb60029e596]",
"TenantId": "<your tenant id>"
},
Add following code in the Stratup.cs
services.AddAuthentication(AzureADDefaults.BearerAuthenticationScheme)
.AddAzureADBearer(options => Configuration.Bind("AzureAd", options));
services.Configure<JwtBearerOptions>(AzureADDefaults.JwtBearerAuthenticationScheme, options =>
{
options.Authority += "/v2.0";
options.TokenValidationParameters = new TokenValidationParameters
{
/**
* with the single-tenant application, you can configure your issuers
* with the multiple-tenant application, please set ValidateIssuer as false to disable issuer validation
*/
ValidIssuers = new[] {
$"https://sts.windows.net/{Configuration["AzureAD:TenantId"]}/",
$"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/v2.0"
},
ValidAudiences = new[]
{
options.Audience,
$"api://{options.Audience}"
}
};
});
Configure swagger. For more details, please refer to the blog.
a. Create Azure Web application
b. Configure API permissions. Regarding how to configure, you can refer to the document
c. code
Install SDK
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
config file
"Swagger": {
"ClientId": ""
},
Add the following code to Startup.cs in the ConfigureServices method:
services.AddSwaggerGen(o =>
{
// Setup our document's basic info
o.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Protected Api",
Version = "1.0"
});
// Define that the API requires OAuth 2 tokens
o.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
Implicit = new OpenApiOAuthFlow
{
Scopes = new Dictionary<string, string>
{
{ "api://872ebcec-c24a-4399-835a-201cdaf7d68b/user_impersonation","allow user to access api"}
},
AuthorizationUrl = new Uri($"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/oauth2/v2.0/authorize"),
TokenUrl = new Uri($"https://login.microsoftonline.com/{Configuration["AzureAD:TenantId"]}/oauth2/v2.0/token")
}
}
});
o.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "oauth2",
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
});
Add the following code to the Configure method:
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.OAuthClientId(Configuration["Swagger:ClientId"]);
c.OAuthScopeSeparator(" ");
c.OAuthAppName("Protected Api");
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
});
Test

.Net Core with WS-Federation generates large wctx

I am attempting to learn how to use WS-Federation in a .NET Core application. I am attempting a basic step of having my client application redirect to an authentication service (defined in my FederationMetadata.xml file), but when I attempt to access a controller requiring authentication [url in this case is http://localhost/STSAwareApp/Test], my redirect url becomes too large to be usable (I get a 404.15, query string is too large).
Since I'm new to WS-Federation, I'm assuming that I have a configuration issue in my startup:
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services.AddControllersWithViews();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddWsFederation(authenticationScheme: "WsFederation", displayName: "Test WS-Fed", options =>
{
options.Wtrealm = "http://localhost/STSAwareApp/Test";
options.MetadataAddress = "http://localhost/STSAwareApp/files/FederationMetadata.xml";
options.RequireHttpsMetadata = false;
}).AddCookie(options =>
{
options.Cookie.Name = "TestStsAuth";
options.Cookie.HttpOnly = true;
});
services.AddLogging(
builder =>
{
builder.AddFilter("Microsoft", LogLevel.Trace)
.AddFilter("System", LogLevel.Trace)
.AddConsole();
});
}
}
Here is an example of the redirect URL that is getting generated (just for completeness sake):
http://localhost:80/STSAwareApp/Test?wtrealm=http%3A%2F%2Flocalhost%2FSTSAwareApp%2FTest&wa=wsignin1.0&wreply=http%3A%2F%2Flocalhost%2FSTSAwareApp%2Fsignin-wsfed&wctx=CfDJ8O7dpxEY6MBCgxct4kkpp1gFIwYvsJN7p6zOuAiyltKmCqff605h1uCh7ZBNM6WneU_7XlxHKAt7CYmBdXG_e19L8z-p64d21gJjDJCdjOkfNieQWNRSPQPGZDUL8eBEVqs4vWaKN-sof8lnblDbySiP8NJPR945c8IYqRwaf7ZBZ-_IxoWZLN_OgMOgFnU5XjtDeUfFCcHh0dtGwSc4PVDPxhKIpxb3JyIEMBRA19qZpudqQEylX6WHek5LkNK1IDbWDv2ll9F5HCJSQxvpVDrLw62dBfF6IDNg3Ar8q2Yr_bpV1gA1RR7kHp3Gs4soxfZENfvi96qkPJs4ZOqvUYjRQjho34Lkc9VH5q2w7n4Oty6abFXs_jeDQQN7ZyFBGQrb-wxBZBEuvNJAFp-ckhGVCeKrtdmXS4bVAvbEtPAEtLHXJpv82Y843_UVCeAQycMjmz2stIovI-HiKAWwCkoc03J7gOlTEwyrn1cR-Ia3QWN4mPN2ncqxW5e80kamNDIDmRxiWoox1Z6x5SATSIO3KergXc7VE1G8-2gLicc8_flyLR6NXUAdDRZTnxGzChHzf2L1eqjm0K_PvioAdqJNuFDlFMeGyfarEbXahAqpchuDvSgolSEKgGO-uLw5GEdCS-5cX_Ztt3bAjbXzkPMdhzYbXFWTDdYTMMMta18nhzgAk5CIzDvo1BmniWGdwUy-lAWm9BoNd4TsroQa-F8NJ86K4sixQIqRqQ-D-Bf_672hHbIkY1QEEe8tqTH-1Qwn9K5RY5sVFQLu0Ec4bp0Zj2EDis-GAtMxhp6761MciYjjhqgORhe3gsLeej5GEY0AErXUOCxdghQKs-waLQtNQ2F4Xn226DYp6NVn8bLs5pu4mFblaWRn9cVzKPHUosRT9BjKqbnLpCCC0A4cOXec-G5znWLOXa6G4qsZjFl5h79MwStDnzP6GU2Wg6TaLG83783f6bRsJwX8blc1CMEjByphkpZp-VdR6FytLXvu4bh8gQQo2K3ad76pNlF8HnA4y1f0p86A82i2IPPDrOeW6YFupzZRITSFz-JvhjAZbkSzu26bgqgHNVTIz1ebu9mHIMQzGzpAu0rFIl16HszR7Omxn8TljADTCCLasQyLNRUIXSA5teeowULetXEv_rmOr6ANkk0kQ-q3pPuiOzkA0aFV6g1jYQ-JvS9K817IafEes7akoDrPbeHEmvD5sWzxERlMtnEQtYwcrPiOroWXIh1QgLjqUgTxtagWmkzoBWVM5PnNmMVkk0alyTgZKOomTcZN8ePkLRp4sY0d0D_uqb0Rn_s757Nb-oDztAz6SLOkCzWnPDif3eIAFTZy24v_oYr3SOFfvM2J-_t0kg3zlRovg25_bPPSs-qyfrMMBSbMammB5e7SKbIna4dPhMdv93Vm6I2GwJ8-VY-pAuBT4MQXPLD1VwdiBT3hWsZOoeMUl1JuL7B9pJDAMBNO2OUTaRb7dajP3VsA09XSgVrBeZ1Hvk733TrzFVoR5KQgHS4qw9cxquRmqP2XfEYTQocB-mUL4b-n0h3RN2qzaHn_VH2pZDV842YcanF4SZ8dDPB4EnLCWU7pf67IwvruInvu8MXg01xNoURh6rKLmSwikbgsEM7Es87RMQSEvar1QixBId9XMO1YiHVvGAdJoivUveJSO1T8Aj4A2xFllBjtD4SfnJc5UDTQ7UxGnVmIVw6pwS9N26U_u09n-T4j5R-ZVQyCNgSjoNRg-3jmMatXcAhT4vJgO-kRuzMiBKnavJ7EPyS8Th8KUK0ws1tQYQKmQQGvd7DT_GRC0wXT8HrTZ1uxTmxxDibzyCLxJZmulLHPcYaXwpWw6j56vOxgCrGy-3L5GtfnXNN1UdE3QzbE6_XL3xF8B3uD6Z5g5ZB_ZR4Q0QS0K9Kb6guaAtxEJYKc2eE2DZ2OpMNtyw5imNYt9crd5J4mB05GR7c0Nur2vqzk1mGM56_0IQD9L4HV4fXNmQuprEpwNZ41NyW-bhcVS30rZn73WLc-XBlNWhCrE_HiTWzCDOn9juofX7_C2AcQypJt-aweXEN5uxRWPp_W9qFJNblrkjzAEr3o7_dylYLYTstOvW4dYuIE4WlTUiJdJF3Iy02whGQUpclOINsxZ3wotkKY2JsnUzsolSeIfWe-es8soGOkPnDSthgjRbpTxltmVz10L0kAo4zckz4HvhEmziWPsGWZH1UVtRKLniT60qq9PPxeuu_dsodov-ByanyRwMHlkzCJhmSBDE0
I'm under the assumption that the url is not only incorrect because the wctx is too long, but it should be attempting to redirect to a different service [http://localhost/STS/V1], it is attempting to the original url with the federation parameters in the query string. I think that the wctx should be smaller, because even if I increase the size of my URL limits, it just continues to grow.
The issue with this was that the FederationMetadata.xml document was not configured correctly. Not exactly sure what was wrong, but instead of attempting to have a valid xml document, it was simpler to update the Configuration property of the WsFederationOptions. Attached is the updated Startup call:
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services.AddControllersWithViews();
services.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme;
sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddWsFederation(authenticationScheme: "WsFederation", displayName: "Test WS-Fed", options =>
{
WsFederationConfiguration configuration = new WsFederationConfiguration();
configuration.TokenEndpoint = "http://localhost/STSSpike/V1";
options.Configuration = configuration;
options.Wtrealm = "http://localhost/STSAwareApp/Test";
}).AddCookie(options =>
{
options.Cookie.Name = "TestStsAuth";
options.Cookie.HttpOnly = true;
});
services.AddLogging(
builder =>
{
builder.AddFilter("Microsoft", LogLevel.Trace)
.AddFilter("System", LogLevel.Trace)
.AddConsole();
});
}
Guessing that this is a pretty localized issue, so might want to close.

asp.net vnext camelCase JSON serialization [duplicate]

Here is my Configure method from my Startup class.
public void Configure(IApplicationBuilder app)
{
// Setup configuration sources
var configuration = new Configuration();
configuration.AddJsonFile("config.json");
configuration.AddEnvironmentVariables();
// Set up application services
app.UseServices(services =>
{
// Add EF services to the services container
services.AddEntityFramework()
.AddSqlServer();
// Configure DbContext
services.SetupOptions<DbContextOptions>(options =>
{
options.UseSqlServer(configuration.Get("Data:DefaultConnection:ConnectionString"));
});
// Add Identity services to the services container
services.AddIdentitySqlServer<ApplicationDbContext, ApplicationUser>()
.AddAuthentication();
// Add MVC services to the services container
services.AddMvc();
});
// Enable Browser Link support
app.UseBrowserLink();
// Add static files to the request pipeline
app.UseStaticFiles();
// Add cookie-based authentication to the request pipeline
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = ClaimsIdentityOptions.DefaultAuthenticationType,
LoginPath = new PathString("/Account/Login"),
});
// Add MVC to the request pipeline
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action}/{id?}",
defaults: new { controller = "Home", action = "Index" });
routes.MapRoute(
name: "api",
template: "{controller}/{id?}");
});
}
Where can I access the HttpConfiguration instance, so that I could set the CamelCasePropertyNamesContractResolver, just like I did in WebApi 2:
var formatterSettings = config.Formatters.JsonFormatter.SerializerSettings;
formatterSettings.Formatting = Formatting.None;
formatterSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
Replace services.AddMvc(); with the following.
services.AddMvc().SetupOptions<MvcOptions>(options =>
{
int position = options.OutputFormatters.FindIndex(f =>
f.Instance is JsonOutputFormatter);
var settings = new JsonSerializerSettings()
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
var formatter = new JsonOutputFormatter(settings, false);
options.OutputFormatters.Insert(position, formatter);
});
UPDATE
With the current version of ASP.NET, this should do.
services.AddMvc().Configure<MvcOptions>(options =>
{
options.OutputFormatters
.Where(f => f.Instance is JsonOutputFormatter)
.Select(f => f.Instance as JsonOutputFormatter)
.First()
.SerializerSettings
.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
UPDATE 2
With ASP.NET 5 beta5, which is shipped with Visual Studio 2015 RTM, the following code works
services.AddMvc().Configure<MvcOptions>(options =>
{
options.OutputFormatters.OfType<JsonOutputFormatter>()
.First()
.SerializerSettings
.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
UPDATE 3
With ASP.NET 5 beta7, the following code works
services.AddMvc().AddJsonOptions(opt =>
{
opt.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
});
RC2 UPDATE
MVC now serializes JSON with camel case names by default. See this announcement.
https://github.com/aspnet/Announcements/issues/194
The Configure function was removed from services.AddMvc() in either Beta 6 or 7. For Beta 7, in Startup.cs, add the following to the ConfigureServices function:
services.AddMvc().AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
});
Use below setting to avoid lowercase replacement for .net core
services.AddMvc().AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);

Resources