1
votes

I have an Azure Mobile service which I am using for authentication. I have a custom auth provider which, once validated, returns the following information:

JwtSecurityToken token = AppServiceLoginHandler.CreateToken(claims, signingKey, audience, issuer, null);
return Ok(new LoginResult()
{
  AuthenticationToken = token.RawData,
  User = signinedInUser,
  StatusCode = System.Net.HttpStatusCode.OK
});

Notice the Timespan is set to null so the token doesn’t expire.

I then make a subsequent request to my AMS which has a controller protected with the Authorize() attribute. However, these are all failing with a 401 Unauthorized response before any of my breakpoints are being hit.

I can see from the Azure logs where this is happening:

2017-10-05T12:18:54 PID[5524] Information Request, Method=POST, Url=https://mywebsite.azurewebsites.net/api/userinfo/update, Message='https://mywebsite.azurewebsites.net/api/userinfo/update'

2017-10-05T12:18:54 PID[5524] Information Message='UserInfo', Operation=DefaultHttpControllerSelector.SelectController

2017-10-05T12:18:54 PID[5524] Information Message='MyAMS.Controllers.UserInfoController', Operation=DefaultHttpControllerActivator.Create

2017-10-05T12:18:54 PID[5524] Information Message='MyAMS.Controllers.UserInfoController', Operation=HttpControllerDescriptor.CreateController

2017-10-05T12:18:54 PID[5524] Information Message='Selected action 'Update(User cpUser)'', Operation=ApiControllerActionSelector.SelectAction

2017-10-05T12:18:54 PID[5524] Information Message='Will use same 'JsonMediaTypeFormatter' formatter', Operation=JsonMediaTypeFormatter.GetPerRequestFormatterInstance

2017-10-05T12:18:54 PID[5524] Information Message='Selected formatter='JsonMediaTypeFormatter', content-type='application/json; charset=utf-8'', Operation=DefaultContentNegotiator.Negotiate

2017-10-05T12:18:54 PID[5524] Information Operation=AuthorizeAttribute.OnAuthorizationAsync, Status=401 (Unauthorized)

2017-10-05T12:18:54 PID[5524] Information Operation=UserInfoController.ExecuteAsync, Status=401 (Unauthorized)

2017-10-05T12:18:54 PID[5524] Information Response, Status=401 (Unauthorized), Method=POST, Url=https://mywebsite.azurewebsites.net/api/userinfo/update, Message='Content-type='application/json; charset=utf-8', content-length=unknown'

You can see that the Authorize attribute is setting a 401 response:

2017-10-05T12:18:54 PID[5524] Information Operation=AuthorizeAttribute.OnAuthorizationAsync, Status=401 (Unauthorized)

On the client, I an populating both the User ID and the Auth Token:

this.client = new MobileServiceClient("https://mywebsite.azurewebsites.net");
var user = UserProfileService.GetCurrentSignedInUser();
client.CurrentUser = new MobileServiceUser(user.UserId.ToString())
{
    MobileServiceAuthenticationToken = user.AuthToken
};

And stepping through the code I have confirmed that the UserID matches that of the user and also the AuthToken is the same AutToken returned in my login method.

Is there something else I need to set/do to enabled authenticated requests to an Azure Mobile Service?

Thanks

EDIT I have since disabled all other authentication providers in Azure but this hasn't solved the problem. I have also debugged the code locally and the issue does not occur running on my localhost, only when deployed to Azure.

1

1 Answers

2
votes

According to your code, you are using custom authentication for your azure mobile app. As adrian hall's book about Custom Authentication:

You must turn on Authentication / Authorization in your App Service. Set the Action to take when request is not authenticated to Allow Request (no action) and do not configure any of the supported authentication providers.

Moreover, I would recommend you just use postman or fiddler to simulate the request against your api endpoint, you need to add a new X-ZUMO-AUTH header and set the value to AuthToken to narrow this issue. Also, you could check this issue on your local side, then simulate the request with the token to see whether your code could work on your side or this is the issue on azure side. For your client, you could use client.LoginAsync("custom", JObject.FromObject(user)) for logging without setting the CurrentUser by yourself. For more details, you could follow adrian hall's book to check this issue.

UPDATE:

According to your comments, I tested it on my side. I used the UseAppServiceAuthentication middleware both on my local side and azure side, and read the SigningKey, ValidAudience, ValidIssuer from my web.config under Startup.MobileApp.cs as follows:

//if (string.IsNullOrEmpty(settings.HostName))
{
    app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
    {
        // This middleware is intended to be used locally for debugging. By default, HostName will
        // only have a value when running in an App Service application.
        SigningKey = ConfigurationManager.AppSettings["SigningKey"],
        ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
        ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
        TokenHandler = config.GetAppServiceTokenHandler()
    });
}

Note: I enable the Authentication / Authorization on azure. For my CustomAuthController.cs I called AppServiceLoginHandler.CreateToken with the setting from my web.config as the UseAppServiceAuthentication middleware initialized. Upon the settings, it could work as expected on my side and azure side.

Then I disable the UseAppServiceAuthentication middleware when deployed to azure side. I encountered the 401 as you mentioned, I assumed that the token validation may fail. I rechecked Custom Authentication and found that you need to init SigningKey, ValidAudience, ValidIssuer from the environment variables under your CustomAuthController.cs as follows:

signingKey = Environment.GetEnvironmentVariable("WEBSITE_AUTH_SIGNING_KEY");
var website = Environment.GetEnvironmentVariable("WEBSITE_HOSTNAME");
audience = $"https://{website}/";
issuer = $"https://{website}/";

Use jwt.io to decode the token, the iss and aud have changed, but I found it would still return 401. I noticed that we both set lifetime to null when invoke AppServiceLoginHandler.CreateToken. I tried to specific it with a value (e.g. TimeSpan.FromDays(30)), then it works on azure side.

In summary, this issue may due to the lifetime parameter value when invoke AppServiceLoginHandler.CreateToken, and you need to set a specific value instead of null on azure side. Moreover, you could add your issue here for professional explanation.

Additionally, your mobile backend would use AppServiceTokenHandler.cs for handling security token, you could override it and specific the TokenHandler parameter when using the UseAppServiceAuthentication middleware, then you could debug the token validation processing.