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.
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.
Related
I have been going through some of my .NET Core2 services and adding some JWT authentication to them to provide some basic security.
I created a new ProvisioningService which has an endpoint that builds a token and returns it:
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Jwt:Issuer"],
_config["Jwt:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return new JwtSecurityTokenHandler().WriteToken(token);
I altered one of my existing services (which I'll refer to as TestService) by adding AddAuthentication in the StartUp. The endpoint for this call has the [HttpPost(), Authorize] attributes. I deployed these changes to my Test server.
When I call TestService/api/updateSomething I am returned a 401 Unauthorized as expected. On my local machine, I create a new token via ProvisioningService/api/buildToken and add the token from the response to my TestService call via the Authorization header. To my surprise...this worked.
Why does my TestService (on a completely different server) view a token created on my local machine as a valid token and allow the call to work? I was expecting this to return the same 401 because I assumed this token was going to be invalid on my Test server. My inexperience with JWT is probably showing....but I am not understanding how these tokens are being stored/shared between servers.
I failed to understand that the token itself has what it needs to authorize itself after it is decrypted. This question is no longer needed.
I'm trying to secure my aspnet core web API server by making it authenticate against Azure B2C using user-provided JWT bearer tokens. I've followed some sample code found on official microsoft github pages, but can't seem to get it working.
In my B2C policy, I've got it set to use the default issuer URL format: https:////v2.0/
In my web application, I've got that same URL specified as the Authority in the JWT options.
When I submit an HTTP request to my server, the identity server code fails as it tries to reach out to B2C to fetch the openid-configuration. It fails with the following error ...
HttpRequestException: Response status code does not indicate success: 404 (Not Found).
System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
IOException: IDX20804: Unable to retrieve document from: 'https://innovativelitfoundry.b2clogin.com/0f55bfb6-6af5-4293-8963-29ae099183cc/v2.0/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.HttpDocumentRetriever.GetDocumentAsync(string address, CancellationToken cancel)
InvalidOperationException: IDX20803: Unable to obtain configuration from: 'https://innovativelitfoundry.b2clogin.com/0f55bfb6-6af5-4293-8963-29ae099183cc/v2.0/.well-known/openid-configuration'.
Microsoft.IdentityModel.Protocols.ConfigurationManager<T>.GetConfigurationAsync(CancellationToken cancel)
Indeed, that URL will not work because it does not appear to be including the policy name, from the used token, in the query string. So, that URL does indeed not work.
I'm unsure how to make the code provide that policy name in the query string, though? Or should it be doing that automatically?
Here is the code, in my aspnet core web api application, where I configure the authentication settings ...
public void ConfigureServices(IServiceCollection services)
{
IdentityModelEventSource.ShowPII = true;
services
.AddAuthentication(ConfigureAuthentication)
.AddJwtBearer(ConfigureJwt);
services
.AddCors();
services
.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services
.AddSingleton(Configuration);
}
private void ConfigureAuthentication(AuthenticationOptions options)
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}
private void ConfigureJwt(JwtBearerOptions options)
{
var tenant = Configuration["AzureAd:TenantId"];
options.Audience = Configuration["AzureAd:ApplicationId"];
options.Authority = $"https://innovativelitfoundry.b2clogin.com/{tenant}/v2.0/";
}
Does anybody perhaps know what I may be doing incorrectly here? How can I get my aspnet core web api application to correctly pull down that openid configuration document?
You must set options.Authority to an authority URL that includes the policy ID:
options.Authority = $"https://innovativelitfoundry.b2clogin.com/{tenant}/{policy}/v2.0/";
As long as you have set the issuer claim for all policies to the issuer URL that doesn't contain the policy ID, then your API application can download the configuration document for any policy and then validate tokens that are issued for all policies.
I am using Azure's MobileServiceClient sdk to authenticate with my server. With the upgrades to 4.x version I am also using Xamarin.Auth to authenticate users with Google and Facebook. When the response comes back from Google I am getting a refresh token. I then call the mobile service sdk like so:
var accessToken = account.Properties["access_token"];
var idToken = account.Properties["id_token"];
var zumoPayload = new JObject();
zumoPayload["access_token"] = accessToken;
zumoPayload["id_token"] = idToken;
var user = await client.LoginAsync(MobileServiceAuthenticationProvider.Google, zumoPayload, );
This work perfectly fine. What does not work is the call to client.RefreshUserAsync(). That is throwing a 403 every time saying the refresh token is either expired or no longer valid even when I call that method right after I logged in. I do not see many examples at all using the MobileServiceClient 4.x sdk and none of them have examples of how to use the refresh token.
I have tried sending that upin the zumo payload as well but it does not work. I have tried invalidating my user on Google (I am getting the refresh token back), tried logging in through the browser and going to auth/me but the refresh token is not there. Any help would be great!
AFAIK, you could leverage the Xamarin.Auth SDK to independently contact the identity provider and retrieve the access token on your mobile client side, then you need to login with your backend (azure mobile app) along with the token for retrieving the authenticationToken, then you could leverage the authenticationToken to access the resources under your mobile app.
Since you are using Client-managed authentication, for refreshing the new access_token, you need to do it on your mobile client side. I checked Xamarin.Auth and found that there is no method for requesting an access token. You need to refer to Refreshing an access token and implement this feature by yourself. I followed OAuth2Authenticator.cs and created a extension method for requesting an access token as follows:
public static class OAuth2AuthenticatorExtensions
{
public static Task RefreshAccessTokenAsync(this OAuth2Authenticator authenticator, Account account)
{
var dics = new Dictionary<string, string>
{
{"refresh_token",account.Properties["refresh_token"]},
{"client_id", authenticator.ClientId},
{"grant_type", "refresh_token"}
};
if (!string.IsNullOrEmpty(authenticator.ClientSecret))
{
dics["client_secret"] = authenticator.ClientSecret;
}
return authenticator.RequestAccessTokenAsync(dics).ContinueWith(task =>
{
if (task.IsFaulted)
{
//todo:
}
else
{
authenticator.OnRetrievedAccountProperties(task.Result);
}
});
}
}
Additionally, if you leverage Server-managed authentication with Microsoft.Azure.Mobile.Client, then you could leverage RefreshUserAsync for refreshing the access token, at this point your previous access_token, clientId are stored on azure, and your mobile app backend would directly communicate with Google's OAuth 2.0 endpoint and request a new access token for you and update the token store on Azure. For more details about token store within App Service, you could follow here.
Need help in authenticating the token request from any client application to WEB API. We have registered our web API has multi-tenant application in Azure AAD. My client application which has been registered in different tenant was able to get Access token from AAD.while making Http request to our endpoint with passing the access token part of request header, we are receiving 401 unauthorized exception. I found reason while browsing for multi tenant scenario is to disable ValidateIssuer and have custom handler.
• Is there any custom handler on implementing for WindowsAzureActiveDirectoryBearerAuthentication. I see people are using OpenIDConnect. But for WEB API, we are using WindowsAzureActiveDirectoryBearerAuthentication i.e Is there any equivalent Event for validation of access token in UseWindowsAzureActiveDirectoryBearerAuthentication and tell user is authenticated ?.
• Is there any better standard of validation of access token and tell user is valid user ?.
• Can we get the claims of user by passing bearer token to WEBAPI Authorize filter ?. or will httprequest object claims gets user information like given name, tenant name, object ID (esp. localhost debugging scenario also.), If we can get those information, we can have our own logic of validation.
Please let us know whether this approach is best practice for authenticating a user.
You could implement a custom issuer validator and assign it to the IssuerValidator property. This is useful when you can't specify a predefined list of issuers in configuration and need some runtime logic to determine if you trust the issuer presented in the token:
TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
{
IssuerValidator = (issuer, token, tvp) =>
{
if (db.Issuers.FirstOrDefault(b => (b.Issuer == issuer)) == null)
return issuer;
else
throw new SecurityTokenInvalidIssuerException("Invalid issuer");
}
}
You could decode the access token to get basic user information like family_name/given_name , but you can only get that by using user identity to acquire the access token .
I have set up a new web app to be able to use the Oauth2 V2 authorization endpoint. I defined the app in https://apps.dev.microsoft.com/
If I want to obtain a new authorization token, following instructions in
https://blogs.msdn.microsoft.com/richard_dizeregas_blog/2015/09/04/working-with-the-converged-azure-ad-v2-app-model/
I get the following error on the login page:
Sorry, but we’re having trouble signing you in.
We received a bad request.
Additional technical information:
Correlation ID: eb9c2331-32bd-45a9-90d1-e9105f0bfa87
Timestamp: 2016-05-22 18:10:48Z
AADSTS70011: The provided value for the input parameter 'scope' is not valid. The scope https://graph.microsoft.com/Calendar.Read is not valid.
The scope is taken from an example in :
https://github.com/Azure/azure-content/blob/master/articles/active-directory/active-directory-v2-scopes.md
So I imagine it is a valid scope.
In v1 of the OAuth2 protocol, it was necessary to configure access to APIs in the Azure AD of my tenant, prior to using them. So I attempted to do so for the new application.
Attempting to do so, the Azure application management reports an error:
{
"message":"This request has a value that is not valid.",
"ErrorMessage":"This request has a value that is not valid.",
"httpStatusCode":"InternalServerError","operationTrackingId":null,"stackTrace":null,"Padding":null
}
What is missing to be able to use the new authorization endpoint ?
The documentation contains a typo if states calendar.read. It must be calendars.read:
private static string[] scopes = {
"https://graph.microsoft.com/calendars.readwrite"};
Uri authUri = await authContext.GetAuthorizationRequestUrlAsync(scopes, additionalScopes, clientId, redirectUri, UserIdentifier.AnyUser, null);