The code "var location = xhr.getResponseHeader('Location');" stopped working - owin

I have this in the done handler for a $.ajax request I am doing:
var location = xhr.getResponseHeader('Location');
It is for a cross-domain request, and it used to work, but it stopped with a recent deployment.
It's returning null now. I still have access to both deployment and staging environments and have been inspecting requests to figure out what changed.
It seem that in the working environment, the following header is included:
Access-Control-Expose-Headers:Location
The backend is ASP.NET Web API v2.
When looking at changes, the only thing I can see that we changed was moving from HttpConfiguration Cors support to Owin Cors.
Before:
var cors = new EnableCorsAttribute("*", "*", "*", "Location");
configuration.EnableCors(cors);
After:
app.UseCors(CorsOptions.AllowAll);
Is there a way to set the Location header as exposed also with the Owin.UseCors?

I found something similar in the Katana Repository and am giving this a try. It solved my issue adding the Location to ExposedHeaders.
var policy = new CorsPolicy
{
AllowAnyHeader = true,
AllowAnyMethod = true,
AllowAnyOrigin = true,
SupportsCredentials = true,
};
policy.ExposedHeaders.Add("Location");
CorsOptions = new CorsOptions
{
PolicyProvider = new CorsPolicyProvider
{
PolicyResolver = context => Task.FromResult(policy)
}
};

Related

Setting CORS rules using Azure.Storage.Blobs

I'm trying to migrate from the deprecated Microsoft.WindowsAzure.Storage to Azure.Storage. In my API app, I have a method that I call occasionally to programmatically set the CORS rules in my Azure Storage account.
How do I add CORS rules to the properties using the new Azure.Storage.Blobs?
My original code that worked under Microsoft.WindowsAzure.Storage is as follows. In the following code, the _client is an instance of CloudBlobClient. I understand that in Azure.Storage.Blobs, I need to use BlobServiceClient which I now do but as I said, some parts of the following code are not working because some methods/properties are no longer there. I'm sure they're moved somewhere else but I haven't been able to figure out where.
public async Task ConfigureCors()
{
var ALLOWED_CORS_ORIGINS = new List<String> { "http://localhost:49065", "https://myappdomain.com", "https://www.myappdomain", "https://login.microsoftonline.com" };
var ALLOWED_CORS_HEADERS = new List<String> { "x-ms-meta-qqfilename", "Content-Type", "x-ms-blob-type", "x-ms-blob-content-type" };
const CorsHttpMethods ALLOWED_CORS_METHODS = CorsHttpMethods.Get | CorsHttpMethods.Delete | CorsHttpMethods.Put | CorsHttpMethods.Options;
const int ALLOWED_CORS_AGE_DAYS = 5;
var properties = await _client.GetServicePropertiesAsync();
properties.DefaultServiceVersion = "2013-08-15";
await _client.SetServicePropertiesAsync(properties);
var addRule = true;
if (addRule)
{
var ruleWideOpenWriter = new CorsRule()
{
AllowedHeaders = ALLOWED_CORS_HEADERS,
AllowedOrigins = ALLOWED_CORS_ORIGINS,
AllowedMethods = ALLOWED_CORS_METHODS,
MaxAgeInSeconds = (int)TimeSpan.FromDays(ALLOWED_CORS_AGE_DAYS).TotalSeconds
};
properties.Cors.CorsRules.Clear();
properties.Cors.CorsRules.Add(ruleWideOpenWriter);
await _client.SetServicePropertiesAsync(properties);
}
}
Looks like I can get and set properties by changing _client.GetServicePropertiesAsync() to _client.GetPropertiesAsync() but DefaultServiceVersion is no longer there. Also I can't seem to find the right way to set CORS rules.
I'd appreciate your suggestions. Thanks!
You can use the code below when using Azure.Storage.Blobs(I'm using sync method, please change it to async method if you need that):
var properties = blobServiceClient.GetProperties().Value;
properties.DefaultServiceVersion = "xxx";
BlobCorsRule rule = new BlobCorsRule();
rule.AllowedHeaders= "x-ms-meta-qqfilename,Content-Type,x-ms-blob-type,x-ms-blob-content-type";
rule.AllowedMethods = "GET,DELETE,PUT,OPTIONS";
rule.AllowedOrigins = "http://localhost:49065,https://myappdomain.com,https://www.myappdomain,https://login.microsoftonline.com";
rule.MaxAgeInSeconds = 3600; // in seconds
properties.Cors.Add(rule);
blobServiceClient.SetProperties(properties);

Azure IoT Hub, File Upload through proxy?

Upload file fails while I try to upload from company network to Azure IoT Hub. Network team suggested to go through another proxy. Implemented proxy as mention in below code but doubt that this code is not respecting proxy settings and upload still fails. Am I missing anything here?
var conSettings = new Http1TransportSettings
{
Proxy = new System.Net.WebProxy {
UseDefaultCredentials = true,
Address = new Uri("http://proxy.com:1234"),
BypassProxyOnLocal = true
}
};
using(var deviceClient = DeviceClient.CreateFromConnectionString(conString, "xxx:xxx:xxx:xxx", new ITransportSettings[] { conSettings }))
using(var sourceData = new FileStream(file, FileMode.Open))
{
await deviceClient.UploadToBlobAsync(fileName, sourceData);
}
How to ensure that this code is making request using given proxy settings?

Kentor/Owin/Azure AD Authentication

I have a web forms app which I am trying to authenticate against Azure AD using SAML 2/Kentor/Owin. I think I have things configured OK, but when my login page issues the following command I am not being redirected to a login page.
HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Login.aspx" });
Here is my startup.cs
private void ConfigureSAML2Authentication(IAppBuilder app) {
var authServicesOptions = new KentorAuthServicesAuthenticationOptions(false)
{
SPOptions = new SPOptions
{
EntityId = new EntityId("https://login.microsoftonline.com/<tenant guid>/saml2")
}
},
AuthenticationType = "KentorAuthServices",
Caption = "ADFS - SAML2p",
};
authServicesOptions.IdentityProviders.Add(new IdentityProvider(
new EntityId("https://sts.windows.net/<tenant guid>/"),
authServicesOptions.SPOptions)
{
MetadataLocation = "https://login.microsoftonline.com/<tenant guid>/federationmetadata/2007-06/federationmetadata.xml",
LoadMetadata = true,
});
app.UseKentorAuthServicesAuthentication(authServicesOptions);
}
As far as I can tell looking at the Network Tools in chrome, no auth request is being sent at all. Is anyone able to tell me why?
The AuthServices middleware is configured as Passive by default, so it will not automatically respond to an authentication challenge unless you specify the provider.
When you issue the challenge you should specify the same AuthenticationType that you used when the middleware was set up. By default this is "KentorAuthServices" but can be changed.
If you change your challenge to include the type, it should trigger the redirect:
HttpContext.Current.GetOwinContext().Authentication.Challenge(new AuthenticationProperties { RedirectUri = "/Login.aspx" }, "KentorAuthServices");

Azure Blob Rest API with SAS

Im trying to upload file directly from web browser using the Azure storage Rest API and Ajax(actually im using Angular's $http)
I've followed every possible official and custom guides without success.
For starters my CORS is set like this:
var storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var blobServiceProperties = blobClient.GetServiceProperties();
blobServiceProperties.Cors = new CorsProperties();
blobServiceProperties.Cors.CorsRules.Add(new CorsRule()
{
AllowedHeaders = new List<string>() { "*" },
AllowedMethods = CorsHttpMethods.Put | CorsHttpMethods.Get | CorsHttpMethods.Head | CorsHttpMethods.Post,
AllowedOrigins = new List<string>() { "*" },
ExposedHeaders = new List<string>() { "*" },
MaxAgeInSeconds = 1800 // 30 minutes
});
blobClient.SetServiceProperties(blobServiceProperties);
At UI level I call my own API that returns the SAS URL like this:
var storageAccount = CloudStorageAccount.Parse(StorageConnectionString);
var blobClient = storageAccount.CreateCloudBlobClient();
var container = blobClient.GetContainerReference("fotosrestaurantes");
var blobPermissions = container.GetPermissions();
blobPermissions.SharedAccessPolicies.Clear();
blobPermissions.SharedAccessPolicies.Add("enviarFotoRestaurante", new SharedAccessBlobPolicy()
{
Permissions = SharedAccessBlobPermissions.Write | SharedAccessBlobPermissions.Read | SharedAccessBlobPermissions.Create | SharedAccessBlobPermissions.Add
SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(30),
});
container.SetPermissions(blobPermissions);
var sasToken = container.GetSharedAccessSignature(null, "enviarFotoRestaurante");
return Ok(sasToken);
Then im using this Angular Module for uploading the blob: https://github.com/kinstephen/angular-azure-blob-upload
So far so good, but when I try to upload I get this from OPTIONS request (from Chrome's Network tab):
403 Server failed to authenticate the request. Make sure the value of
Authorization header is formed correctly including the signature.
And this from the console:
XMLHttpRequest cannot load 'myAzureHttpLink/fotosrestaurantes/google.jpg?sv=2015-04-05&sr=b&si=enviarFoto&sig=***&se=2016-01-11T14%3A16%3A22Z&sp=w&comp=block&blockid=YmxvY2stMDAwMDAw
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:34090' is therefore not allowed
access. The response had HTTP status code 403.
Now, as far as I could understand from the oficial documentation(most are very breif on thier matter and have lots of links that almost never cover what you really need on them) if I choose to use SAS URL I dont need the authorization header. As Gaurav says here: https://stackoverflow.com/a/33846704/3198372
I've tryed everything, from container SAS to blob SAS but nothing works. As if the CORS configuration and SAS URL just dont work(even if they are there).
Anyone knows where Im wrong?

How can I get my custom OWIN auth working in Azure Mobile Service

I have a working Web API 2 mobile service hosted in AWS and I want to move to AMS. It works in Postman and on mobile devices just fine.
I followed several blog/posts and spent several hours rewriting and reordering the WebApiConfig.Register. I then created a new AMS project and copied over all my controllers etc. and I had the same result. I reviewed many similar questions but am brain dead over 20 something lines of code.
It works locally through Postman but after I published it I get
HTTP 401 - {"message":"Authorization has been denied for this request."}
Here is the AWS working startup.cs -- I do not call WebApiConfig.Register
namespace Savviety.Data.Service
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
var config = new HttpConfiguration();
ConfigureOAuth(app);
// remove in production
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors(cors);
app.UseWebApi(config);
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var path = AppDomain.CurrentDomain.BaseDirectory + #"\log4net.config";
var fileInfo = new FileInfo(path);
XmlConfigurator.ConfigureAndWatch(fileInfo);
if (fileInfo.Exists)
{
log4net.Config.XmlConfigurator.ConfigureAndWatch(fileInfo);
}
else
{
throw new FileNotFoundException("Could not find log4net.config");
}
}
public void ConfigureOAuth(IAppBuilder app)
{
var oAuthServerOptions = new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(1),
Provider = new SimpleAuthorizationServerProvider()
};
// Token Generation
app.UseOAuthAuthorizationServer(oAuthServerOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
}
}
In the AMS version I call the WebApiConfig.Register method from Application.Onstart in Global.asax
public static void Register( )
{
.
var options = new ConfigOptions();
var config = ServiceConfig.Initialize(new ConfigBuilder(options));
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Make sure this is after ServiceConfig.Initialize
// Otherwise ServiceConfig.Initialize will overwrite your changes
Microsoft.WindowsAzure.Mobile.Service.Config.StartupOwinAppBuilder.Initialize(appBuilder =>
{
ConfigureOAuth(appBuilder);
appBuilder.UseWebApi(config);
var path = AppDomain.CurrentDomain.BaseDirectory + #"\log4net.config";
var fileInfo = new FileInfo(path);
});
//var cors = new EnableCorsAttribute("*", "*", "*");
//config.EnableCors(cors);
// Web API routes
// config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
I also replaced [Authorize] with [AuthorizeLevel(AuthorizationLevel.User)] and removed the startup.cs class.
In Postman it works locally, but not after I publish it. It generates a token, but authentication fails.
TIA
Gary
The AuthorizeLevel attribute looks for a token issued by Mobile Services. Since you are not actually issuing such a token in the above, it will fail.
Things are probably working locally since the default config makes all local calls accepted. As described here, you will want to go into the Register() method of WebApiConfig.cs and add the following:
config.SetIsHosted(true);
This should cause calls to start failing locally.
To address the core issue, it is possible to wire your own OWIN provider into the Mobile Services pipeline. You will need to create a child class of LoginProvider which basically does your ConfigureAuth() call inside of its ConfigureMiddleware(). Please see the example in this blog post which sets up a LinkedIn middleware.
Ok, the primary issue is Azure will not support custom OWIN authentication or I cannot find how to implement it anywhere. I have to use a provided list of users and passwords from another system so it has to be custom.
The solution is a custom LoginController and LoginProvider the relevant code is below.
MyLoginProvider is a subclass of LoginProvider and calls the CreateLoginResult base method.
I had to modify my javascript auth interceptor to config.headers["X-ZUMO-AUTH"] = $localStorage.token; instead of the OAuth bearer token header.
I cannot get the email or display name from the claims identity on a request but I used a work around. When I figure it out I will post it here, but for now it is not blocking me.
public HttpResponseMessage Post(LoginRequest loginRequest)
{
var mongoDbManager = MongoDbManager.GetInstance();
var userCollection = mongoDbManager.GetCollection<UserDocument>(CollectionNames.User);
var q0 = Query<UserDocument>.EQ(i => i.ClientId, loginRequest.ClientId);
var q1 = Query<UserDocument>.EQ(i => i.UserEmailAddress, loginRequest.UserName);
var q2 = Query<UserDocument>.EQ(i => i.UserPassword, loginRequest.Password);
var query = Query.And(q0, q1, q2);
var result = userCollection.FindOne(query);
if (result == null)
{
return this.Request.CreateResponse(HttpStatusCode.Unauthorized, "Invalid username or password");
}
else
{
var claimsIdentity = new ClaimsIdentity();
claimsIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, result.UserId));
claimsIdentity.AddClaim(new Claim(ClaimTypes.Email, result.UserEmailAddress));
claimsIdentity.AddClaim(new Claim("DisplayName", result.DisplayName));
var loginResult = new SavvietyLoginProvider(handler).CreateLoginResult(claimsIdentity, Services.Settings.MasterKey);
return this.Request.CreateResponse(HttpStatusCode.OK, loginResult);
}
}
}

Resources