I have a native iOS App with a Single Sign On Authentication with Azure ADFS. On launching the App, the user must login to Azure Active Directory and on successful authentication gets back a Token to validate with a Web API for all the Service Endpoint calls.
The Authorization works very well for few users (e.g. Developer Logins) but for a few AD logins within the organization the Web API returns back Authorization has been denied for this request
.
I have eliminated the App From this equation to keep it simple and tried with the Postman to hit the Web API passing in the Authorization Token in the Header.
For few accounts, the Web API returns back the data successfully and for few accounts the Authorization has been denied for this request
is thrown. (401 Unauthorized)
Given the fact that the Authentication engine works successfully for few accounts, I am wondering whether this is an issue with the Active Directory User Roles / Group Policies.
Has anyone had this issue or know the reason for this issue? Any help on this would be highly appreciated.
More Details:
- WebAPI 2.0 hosted in Azure.
In the Web API StartUp class
var azureActiveDirectoryBearerAuthenticationOptions = new WindowsAzureActiveDirectoryBearerAuthenticationOptions { AuthenticationType = "OAuth2Bearer", Tenant = "companyName.onmicrosoft.com", TokenValidationParameters = new TokenValidationParameters { ValidAudience = ConfigurationManager.AppSettings["ida:AppIdUri"], }, Provider = new OAuthBearerAuthenticationProvider() { OnRequestToken = (context) => { OAuthRequestTokenContext token = context; System.Diagnostics.Debug.WriteLine(token.Token); return Task.FromResult(0); }, OnApplyChallenge = (context) => { OAuthChallengeContext challenge = context; var types = challenge.OwinContext.Authentication.GetAuthenticationTypes(); foreach (var type in types) { System.Diagnostics.Debug.WriteLine(type.Caption + " "+ type.AuthenticationType); } return Task.FromResult(0); }, OnValidateIdentity = (context) => { var authenticationTicket = context.Ticket; var claims = ClaimsHelper.GetClaimsFor(authenticationTicket.Identity.Name); context.Ticket.Identity.AddClaims(claims); return Task.FromResult(0); } } };
`
The OWIN Auth Types in the OAuthBearerAuthenticationProvider
are OAuth2Bearer and Federation.
No role based Claims are involved at this point.
The client generates the token like this and passes this on each request as a Bearer token in the header
AuthenticationContext authenticationContext = new AuthenticationContext(azureSettings.AdAuthority);
AuthenticationResult authenticationResult =
await authenticationContext.AcquireTokenAsync(azureSettings.AdResource,
azureSettings.AdClientId,
new Uri(azureSettings.AdRedirect),
new AuthorizationParameters(view),
UserIdentifier.AnyUser,
string.Format("domain_hint={0}", azureSettings.AdDomainHint));
`
Azure Active Directory is in sync with the local Active Directory.
Tried the Following but in Vain:
Create a new AD User with same exact roles and dept as a user who can successfully retrieve data and made the sync to AAD and even the new user gets Unauthorized. Ensured the Web API on Azure is not restricted to individual Assigned Users.
Finally got to the bottom of this issue. There was nothing wrong with the ADAL setup or the OAuth2.0 Authentication for ADFS. The call did proceed to the
OnValidateIdentity = (context) =>
which means that the Set up is all good.The issue lies within the code which has logic to check for users belonging to a specific claim.
var claims = ClaimsHelper.GetClaimsFor(authenticationTicket.Identity.Name);
Deep in the
ClaimsHelper.GetClaimsFor
method,Since the authorization failed there and an exception was thrown, this exception bubbled up to OWIN middleware and the default (generic) 401 Unauthorized exception was thrown back. But the Exception details were never passed along for some reason by OWIN middleware (not even as an inner exception).
I was confused with the generic exception that there was something wrong with the setup of the AD user roles and it was not even authenticating in the first place.
Note:
Logging (DB or File System) would be a good idea if the Authorization has been denied deliberately by our own code.
I am still investigating the best approach for the denied requests to pass back more meaningful information to the App. Please let me know if you know the best way to handle this.
Also, a side note: Katana only logs to .Net trace if there is any exception thrown even if the log listener is enabled in the config file.
Tip: Best way to see if the jwt is not corrupt - jwt.io
Thanks for those who tried to help.