Reputation: 7304
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.
Upvotes: 1
Views: 2005
Reputation: 18465
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.
Upvotes: 2