I am trying to get authenticated my Mobile App ( for testing running from visual studio) to authenticate my Web API 2 Authentication Token in Single Page APP
But Ajax always cancelled with status cancelled
I have enabled CORS as below
var cors = new EnableCorsAttribute("*", "*", "*") {SupportsCredentials = true};
config.EnableCors(cors);
//Use only bearer token authentication
config.SuppressDefaultHostAuthentication();
Web Config
<system.web>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime targetFramework="4.5" />
<roleManager enabled="true" />
<httpModules>
<add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
<add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
<add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
</httpModules>
</system.web>
My Ajax Request
$.ajax('http://retail.leap-tel.com/token', {
type: "POST",
data: data,
xhrFields: {
withCredentials: true
}
Chorme Developer tools Log
Status : Cancelled
Request URL:http://retail.leap-tel.com/token
Request Headersview source
Accept:*/*
Cache-Control:no-cache
Content-Type:application/x-www-form-urlencoded; charset=UTF-8
Origin:http://localhost:1539
Pragma:no-cache
Referer:http://localhost:1539/index.html
User-Agent:Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Form Dataview sourceview URL encoded
I made some changes in DurandalAuth for enabling CORS and created a sample Javascript client in another repo
Check it out
https://github.com/yagopv/DurandalAuth/commit/4e7c09bc589345c946319b42d320e2b4d8313573
https://github.com/yagopv/DurandalAuthjsclient
Perhaps your main issue is about using the default Web API CORS attribute. You should use Microsoft.Owin.Cors attribute instead for working with the Owin middleware
public static void Register(HttpConfiguration config)
{
var corsAttribute = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(corsAttribute);
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
}
Add cors attribute in webapiConfig class register method for application level.
Related
I have an API on ASP.net Core 2 (windows authentication) and a front on angular.
I make a cors configuration to querying my backend from the SPA angular, but im blocked in cause of the preflight who are rejected from the IIS server because he don't have identification information.
error message :
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://XXXXXX' is therefore not allowed access. The response had HTTP status code 401.
code side front :
//MY HEADER
private headers = new Headers({
'Content-Type': 'application/json',
'Access-Control-Allow-Credentials':'true',
'Access-Control-Allow-Origin':'true'
});
//REQUEST
let options = new RequestOptions({headers:this.headers, withCredentials:true});
return this.http.get(this.tasksUrl,options).map(res=>res.json());
code side back : (Startup.cs)
public void ConfigureServices(IServiceCollection services)
{
services.AddCors();
services.AddCors(options =>
{
options.AddPolicy("AllowSpecificOrigin",
builder =>
{
builder.WithOrigins("http://theURLofTheFront:8080" )
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors("AllowSpecificOrigin");
app.UseMvc();
}
I try this :
CORS preflight request returning HTTP 401 with windows authentication.
and i added custom header to specify the 'Acces-control-allow-origin' on IIS, dont work for me.
and this dont work for me :
https://blogs.msdn.microsoft.com/friis/2017/11/24/putting-it-all-together-cors-tutorial/
I can't remove the default authorization rule.
I thank you in advance for you
There are several ways to accomplish this, other answers can be found on this similar question --> Angular4 ASP.NET Core 1.2 Windows Authentication CORS for PUT and POST Gives 401
CORS Module
It is possible to configure IIS by using the CORS Module.
As seen here: https://blogs.iis.net/iisteam/getting-started-with-the-iis-cors-module
And further information available here: https://blogs.iis.net/iisteam/getting-started-with-the-iis-cors-module
The IIS CORS module is designed to handle the CORS preflight requests
before other IIS modules handle the same request. The OPTIONS requests
are always anonymous, so CORS module provides IIS servers a way to
correctly respond to the preflight request even if anonymous
authentification needs to be disabled server-wise.
You will need to enable the CORS Module via the Webconfig:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<cors enabled="true">
<add origin="*" allowCredentials="true" />
</cors>
</system.webServer>
</configuration>
for more granular control:
<?xml version="1.0"?>
<configuration>
<system.webServer>
<cors enabled="true">
<add origin="https://readonlyservice.constoso.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="HEAD" />
</allowMethods>
<allowHeaders>
<add header="content-type" />
<add header="accept" />
</allowHeaders>
</add>
<add origin="https://readwriteservice.constoso.com" allowCredentials="true">
<allowMethods>
<add method="GET" />
<add method="HEAD" />
<add method="POST" />
<add method="PUT" />
<add method="DELETE" />
</allowMethods>
</add>
</cors>
</system.webServer>
</configuration>
Redirect OPTIONS
You can redirect all OPTIONS requests to always give an OK status.
This will however subvert the entire idea of a preflight request, so use this only if it's applicable to your situation.
Install the redirect module in IIS.
Add the following redirect to your Webconfig.
<rewrite>
<rules>
<rule name="CORS Preflight Anonymous Authentication" stopProcessing="true">
<match url=".*" />
<conditions>
<add input="{REQUEST_METHOD}" pattern="^OPTIONS$" />
</conditions>
<action type="CustomResponse" statusCode="200" statusReason="Preflight" statusDescription="Preflight" />
</rule>
</rules>
</rewrite>
Middleware
Alternatively the desired result can be achieved by enabling anonymous authentication in IIS and creating a middleware in the Net Core API that checks if a person is properly authenticated.
Middleware:
public AuthorizationMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_log = logger;
}
public async Task Invoke(HttpContext httpContext)
{
//Allow OPTIONS requests to be anonymous
if (httpContext.Request.Method != "OPTIONS" && !httpContext.User.Identity.IsAuthenticated)
{
httpContext.Response.StatusCode = 401;
await httpContext.Response.WriteAsync("Not Authenticated");
}
await _next(httpContext);
}
Preflight request does not send authentication information. So, enable anonymous authentication as well (no need to remove windows authentication). refer to https://stackoverflow.com/a/50354772/946773
I am encountering an infinite redirect loop between login.microsoftonline.com and my application. My project is implementing authentication and authorization in an Asp.net 4.8 web forms project. I am able to add authentication using the default Owin startup file and then require authentication in the web config file. The below works correctly for requiring a user to sign in before being able to access pages/AuthRequired
StartupAuth.CS
public partial class Startup
{
private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
private static string postLogoutRedirectUri = ConfigurationManager.AppSettings["ida:PostLogoutRedirectUri"];
private static string authority = ConfigurationManager.AppSettings["ida:Authority"];
private static string clientSecret = ConfigurationManager.AppSettings["AppRegistrationSecret-Local"];
public void ConfigureAuth(IAppBuilder app)
{
//for debugging
//IdentityModelEventSource.ShowPII = true;
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions
{
ClientId = clientId,
Authority = authority,
PostLogoutRedirectUri = postLogoutRedirectUri,
ClientSecret = clientSecret,
RedirectUri = postLogoutRedirectUri,
//This allows multitenant
//https://github.com/Azure-Samples/guidance-identity-management-for-multitenant-apps/blob/master/docs/03-authentication.md
TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false
},
Notifications = new OpenIdConnectAuthenticationNotifications()
{
AuthenticationFailed = (context) =>
{
return Task.FromResult(0);
}
}
}
);
// This makes any middleware defined above this line run before the Authorization rule is applied in web.config
app.UseStageMarker(PipelineStage.Authenticate);
}
}
Web.Config
<configuration>
...
<system.web>
<authentication mode="None" />
</system.web>
<location path="Pages/AuthRequired">
<system.web>
<authorization>
<deny users="?" />
</authorization>
</system.web>
</location>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
...
</configuration>
I need to add authorization so that only users with the admin role will be able to access Pages/AuthRequired. I have done that by updating the web config:
<configuration>
...
<system.web>
<authentication mode="None" />
</system.web>
<location path="Pages/AuthRequired">
<system.web>
<authorization>
<allow roles="Admin" />
<deny users="*" />
</authorization>
</system.web>
</location>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
...
</configuration>
Adding authorization to the authenticated page works correctly if the user has that role, but if a user who doesn't have the role tries to access the page they are redirected back to login.microsoftonline.com and then back to the application in an infinite loop.
I can see that Owin UseOpenIdConnectAuthentication is returning a 302 response on unauthorized and that is causing the loop.
How can I change it so that instead of redirecting unauthorized (but authenticated) users to login.microsoftonline.com, that user should be directed to an app page that displays a 401 error?
Please check if below work around helps:
Its usually possible that if forms authentication is enabled, you will be redirected to the login page when status code is 401.
As a workaround try Adding the below to global.asax in the application end request and you can create own unauthorized page if needed and redirect to that.
if (this.Response.StatusCode == 302&& this.Response.StatusCode == 401
&& this.Response.RedirectLocation.ToLower().Contains("login.aspx"))
{
this.Response.StatusCode = 401;
//or Response.Redirect("Unauthorized.aspx");
}
You can also check this > Redirect unauthorised user to message page in ASP .Net. (microsoft.com)
Other references
Prevent redirect to login on status code 401 (Unauthorized)
(microsoft.com)
asp.net - In-place handling (no redirect) of 401 unauthorized? -
Stack Overflow
ASP.NET URL Authorization doesn't appear to interoperate well with OIDC (i.e. Azure AD).
First remove the URL Authorization from your Web.config:
<configuration>
...
<system.web>
<authentication mode="None" />
</system.web>
<location path="Pages/AuthRequired">
<system.web>
-- <authorization>
-- <allow roles="Admin" />
-- <deny users="*" />
-- </authorization>
</system.web>
</location>
<system.webServer>
<modules>
<remove name="FormsAuthentication" />
</modules>
</system.webServer>
...
</configuration>
Optionally make authenticated required for all pages globally:
<system.web>
<deny users="?" />
</system.web>
You can override this behaviour with <Allow users="?" /> for specific pages i.e. logins/logouts/erorr pages/etc.
Second add authorization logic to your AuthRequired.aspx page:
public partial class AuthRequired {
protected void Page_Load(object sender, EventArgs e)
{
Authorization.AuthorizeAuthRequiredPage();
...
}
}
public static class Authorization
{
public static void AuthorizeAuthRequiredPage()
{
if (!Authorized(HttpContext.User))
{
Redirect("/Anauthorized.aspx");
}
}
private static bool Authorized(User user) => { ... }
}
I have ASP.NET MVC4 application project. I also added WebApi to my project by creating ApiContoller. I have Forms Authentication for MVC and Basic Authentication (Thinktecture) for Web API.
I noticed that in ApiContoller [Authorize] is working well but [Authorize(Roles="")] never let to invoke methods. I think the reason is that in MVC Contoller descendants both statements User.IsInRole(""); and Roles.IsUserInRole(User.Identity.Name, ""); returns true, but in ApiContoller descendants first statement is always false, when second returns true if user has role:
bool booool1 = User.IsInRole("Radiolog");
bool booool2 = Roles.IsUserInRole(User.Identity.Name, "Radiolog");
Here is my web.config configuration:
<add key="enableSimpleMembership" value="false" />
<add key="autoFormsAuthentication" value="false" />
...
<roleManager cacheRolesInCookie="false" defaultProvider="CustomRoleProvider" enabled="true">
<providers>
<clear />
<add name="CustomRoleProvider" type="RisSystem.Services.CustomRoleProvider" />
</providers>
</roleManager>
...
<authentication mode="Forms">
<forms loginUrl="~/Account/Login" timeout="2880" />
</authentication>
In ApiController methods I am authenticating with: client.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue(login, password); (Thinktecture) and FormsAuthentication in MVC Contoller.
Authentication for WebApi is set in WebApiConfig.cs in Register(HttpConfiguration config) function:
var authConfig = new AuthenticationConfiguration();
authConfig.AddBasicAuthentication((userName, password) => AuthenticationService.ValidateUser(userName, password));
config.MessageHandlers.Add(new AuthenticationHandler(authConfig));
Q: How to get to work Authorize Attribute with Roles in ASP.NET Web API
I have found numerous articles and blog posts which go into detail of the various ways this can be accomplished. Some look involved with custom coding, and are (right at this moment) not what I am wanting (I want a configuration solution). Some of these posts (SO included) share how to do this through configuration over custom coding, but to no success on my end. Here is my attempt so far. Have I overlooked anything? Is my Web.config to blame, or is there something else I must specify on my request?
Additional Details
I am running IIS Express 8 (product version: 8.5.9748.0) on my
localhost
Windows 8.1
I am making requests with Fiddler - but the same response occurs with ajax calls (AngularJS)
Fiddler Get Headers
User-Agent: Fiddler
Host: localhost:5293
Accept: application/json; charset=utf-8, application/json
Accept-Encoding: gzip
JS Request (AngularJS)
$http.get('localhost:5293/api/Whatever', {
headers: {
'Accept': 'application/json; charset=utf-8, application/json',
'Accept-Encoding': 'gzip'
}
});
Problem
My response is always
application/json; charset=utf-8
Efforts
I have enabled Dynamic Content Compression and Static Content Compression on Windows features under IIS (Turn Windows features on or off)
Reset Windows Process Activation Service
Run following commands, and verified the results as such
appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json',enabled='True']" /commit:apphost
appcmd.exe set config -section:system.webServer/httpCompression /+"dynamicTypes.[mimeType='application/json; charset=utf-8',enabled='True']" /commit:apphost
Verify Command:
appcmd.exe list config -section:system.webServer/httpCompression
Verify Response:
<system.webServer>
<httpCompression directory="%SystemDrive%\inetpub\temp\IIS Temporary Compressed Files">
<staticTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="application/atom+xml" enabled="true" />
<add mimeType="application/xaml+xml" enabled="true" />
<add mimeType="*/*" enabled="false" />
</staticTypes>
<dynamicTypes>
<add mimeType="text/*" enabled="true" />
<add mimeType="message/*" enabled="true" />
<add mimeType="application/x-javascript" enabled="true" />
<add mimeType="application/javascript" enabled="true" />
<add mimeType="*/*" enabled="false" />
<add mimeType="application/json" enabled="true" />
<add mimeType="application/json; charset=utf-8" enabled="true" />
</dynamicTypes>
<scheme name="gzip" dll="%Windir%\system32\inetsrv\gzip.dll" />
</httpCompression>
</system.webServer>
I have placed this exact configuration in my Web.config file under the <system.webServer> node, and still no luck...
Web Api Controller Method
public JsonResult Get()
{
return new JsonResult
{
Data = "foo",
ContentType = "application/json; charset=utf-8",
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
Anyway, don't dictate the format. Let content negotiation do its job. And compression will start working too:
public IHttpActionResult Get()
{
return Ok("foo")
}
Created a SPA application with .NET Framework 4.5 that will use AngularJS. I implemented the System.IdentityModel per instructions to point to a third party authentication.
Web.config edits:
<configSections>
<section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
<section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
</configSections>
<location path="FederationMetadata">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
...
<system.web>
<authorization>
<deny users="?" />
</authorization>
<authentication mode="None" />
<compilation debug="true" targetFramework="4.5" />
<httpRuntime requestValidationType="ourcustomvalidator, ourcustomnamespace" />
<pages controlRenderingCompatibilityVersion="4.0" validateRequest="false" />
</system.web>
....
<modules runAllManagedModulesForAllRequests="true">
<remove name="FormsAuthentication" />
<add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
<add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
</modules>
...
So on initial launch of the Single Page Application, the site redirects to the authorization site. From there you login, authorize and you're redirected back to the application.
Now I have a WebAPI Restful section being a part of the solution as well to log client errors and also to handle logging out within our application. This has lead me to a few problems that I am not grasping:
1) If I make a /api/logoff call to my WebApi, I can call FederatedAuthentication.SessionAuthenticationModule.SignOut(); and I am signed out behind the scenes. Using angular, how should I go about redirecting the user? I noticed after I issue this command, if I hit F5, my site is refreshed but I am automatically logged back in. I would prefer this go back to the login screen I get on initial page load.
2) If I make a /api/custom call to my WebApi and I was logged out behind the scenes, how do I capture that and redirect the user? Right now I am getting an error message along the lines of:
XMLHttpRequest cannot load https://mycustomloginurl.com/?wa=wsignin1.0&wtrealm=http%3a%2f%2flocalhost%3a561…%3dpassive%26ru%3d%252fwebapi%252fims%252ftesting&wct=2014-02-21T21%3a47%3a09Z.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:56181' is therefore not allowed access.
Sorry if this confusing, I am trying to wrap my head around all of this.
Thanks to some more research, this post: Prevent XmlHttpRequest redirect response in .Net MVC WS-Federation Site helped me get to the right answer.
Basically I followed the same code but added the following:
resp.Clear(); // cleared the response
var fa = FederatedAuthentication.WSFederationAuthenticationModule;
var signInRequestMessage = new SignInRequestMessage(new Uri(fa.Issuer), fa.Realm);
var signInURI = signInRequestMessage.WriteQueryString();
resp.Write(signInURI);
So I cleared the response and made the body of the response contain the Sign-In URL for the authentication. In angular, I have a HTTP interceptor that checks for status code 401 and then uses the above data to redirect to the login.
app.config([
'$httpProvider', function($httpProvider) {
var interceptor = ['$rootScope', '$q','$window', function (scope, $q, $window) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
$window.location.href = response.data.replace(/"/g, "");
}
// otherwise
return $q.reject(response);
}
return function (promise) {
return promise.then(success, error);
}
}];
$httpProvider.responseInterceptors.push(interceptor);
}
]);