Azure API failed to authenticate the request - azure

I am using the next code to get the token for Azure AD authentication
errorMessage = "";
AuthenticationResult result = null;
var context = new AuthenticationContext(String.Format(CultureInfo.InvariantCulture, ConfigurationManager.AppSettings["login"], ConfigurationManager.AppSettings["tenantId"]),false);
ClientCredential clientCredential = new ClientCredential(ConfigurationManager.AppSettings["clientId"], ConfigurationManager.AppSettings["key"]);
try
{
result = context.AcquireToken(ConfigurationManager.AppSettings["apiEndpoint"], clientCredential);
}
catch (AdalException ex)
{
if (ex.ErrorCode == "temporarily_unavailable")
{
errorMessage = "Temporarily Unavailable";
return null;
}
else
{
errorMessage = "Unknown Error";
return null;
}
}
string token = result.AccessToken;
var credential = new TokenCloudCredentials(ConfigurationManager.AppSettings["subscriptionId"],token);
//string certificateString = ConfigurationManager.AppSettings["managementCertificate"];
//var cert = new X509Certificate2(Convert.FromBase64String(base64cer));
return credential;
After that I am doing the next to create a website in Azure
using (var computeClient = new WebSiteManagementClient(credentials))
{
var result = computeClient.WebSites.IsHostnameAvailable(websiteName);
if (result.IsAvailable)
{
await computeClient.WebSites.CreateAsync(WebSpaceNames.WestEuropeWebSpace, new WebSiteCreateParameters() {
Name= websiteName,
ServerFarm= ConfigurationManager.AppSettings["servicePlanName"]
});
}
else
{
return ResultCodes.ObjectNameAlreadyUsed;
}
}
But every time I execute that I got the following error:
ForbiddenError: The server failed to authenticate the request. Verify that the certificate is valid and is associated with this subscription.
I tried to import the management certificate as they said here:
https://www.simple-talk.com/cloud/security-and-compliance/windows-azure-management-certificates/
And also tried this one:
http://technetlibrary.com/change-windows-azure-subscription-azure-powershell/198
For importing management certificate.
Also I gave the application permissions to access management API.

Azure AD Authentication DOES NOT use the management certificate authentication.
There is a good documentation and code sample on MSDN on how to resolve your current issue.
Authenticating Service Management Requests

Looks like your application does not have permission to access the Azure API's.
Please use this link to get permissions.
After this please add permissions to access API in app permission or user permission.

Related

C# CSOM Sharepoint Bearer request from azure active directory

I am using the following approach as the basis of this (https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-devquickstarts-webapi-dotnet).
I got all this example working after setting up azure. But now we need to port it to an actual existing mobile app and web api app. The mobile app can get the Bearer token, but when we pass it to the web api, we pass this in a CSOM request as follows, but we still get a 401 Unauthroised response.
public static ClientContext GetSharepointBearerClientContext(this JwtTokenDetails tokenDetails)
{
var context = new ClientContext(tokenDetails.SiteUrl);
//context.AuthenticationMode = ClientAuthenticationMode.Anonymous;
context.ExecutingWebRequest += new EventHandler<WebRequestEventArgs>((s, e) =>
{
e.WebRequestExecutor.RequestHeaders["Authorization"] = "Bearer " + tokenDetails.BearerToken;
});
return context;
}
Our web api doesn't use any of the tech as in the example above, as I presume that we should just be able to pass the token through the CSOM request in the header, but this is not working, what else could I look at?
I have assigned the Office 365 Sharepoint Online (Microsoft.Sharepoint) permission and set the following
I have also done the same for the app registration, which we don't really use! Still not sure how the app registration comes into it)...
So this was possible, it was just microsoft telling us to put in an incorrect value. All the documentation says put the APP ID URI in the Resource. But in our case it needed to be the sharepoint url.
So we have the tenant name which on azure id the domain name e.g. srmukdev.onmicrosoft.com
Tenant: srmukdev.onmicrosoft.com
Application Id: This is the guid for the app registered in azure active directory.
RedirectUri: This can be any url(URI), its not actually used as a url for a mobile app as far as I can see.
ResourceUrl: srmukdev.sharepoint.com
The code I am using to get a token is as follows for a WPF example. The aadInstance is https://login.microsoftonline.com/{0}
private static string authority = String.Format(CultureInfo.InvariantCulture, aadInstance, tenant);
public async void CheckForCachedToken(PromptBehavior propmptBehavior)
{
//
// As the application starts, try to get an access token without prompting the user. If one exists, populate the To Do list. If not, continue.
//
AuthenticationResult result = null;
try
{
result = await authContext.AcquireTokenAsync(resourceUrl, applicationId, redirectUri, new PlatformParameters(propmptBehavior));
TokenTextBox.Text = result.AccessToken;
// A valid token is in the cache - get the To Do list.
GetTokenButton.Content = "Clear Cache";
}
catch (AdalException ex)
{
if (ex.ErrorCode == "user_interaction_required")
{
// There are no tokens in the cache. Proceed without calling the To Do list service.
}
else
{
// An unexpected error occurred.
string message = ex.Message;
if (ex.InnerException != null)
{
message += "Inner Exception : " + ex.InnerException.Message;
}
MessageBox.Show(message);
}
return;
}
}

Obtaining a valid access token for Microsoft Graph API

I am working on an ASP.NET MVC5 Web App that uses Azure ADAL libraries to authenticate users, it works fine, however, when I manually send requests to graph, ex: GET https://graph.microsoft.com/v1.0/me or GET https://graph.microsoft.com/v1.0/groups?$filter=from/displayName eq 'whatever'.
I have tried updating the App Registration in Azure as to add the required Graph permissions, and I have also tried creating new app registrations, no matter what I do my requests will always respond 401 Unauthorized, is there anything I am missing?
EDIT: Example response from Postman
{
"error": {
"code": "InvalidAuthenticationToken",
"message": "Access token validation failure.",
"innerError": {
"request-id": "a142576b-acce-4e59-8a8d-adede61aaf59",
"date": "2017-04-05T13:27:36"
}
}
}
EDIT: C# Request Example
public async Task<GroupGraph> GetGroupIdByDisplayName(string displayName)
{
var accessToken = await authenticationService.GetTokenUserOnly();
GroupGraph groupGraphResponse = null;
using (var client = new HttpClient())
{
using (var request = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/groups?$filter=from/displayName eq '{displayName}'"))
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using (var response = client.SendAsync(request).Result)
{
if (response.IsSuccessStatusCode)
{
using (var content = response.Content)
{
var result = await content.ReadAsStringAsync();
groupGraphResponse = JsonConvert.DeserializeObject<GroupGraph>(result);
}
}
}
}
}
return groupGraphResponse;
}
EDIT: The way I obtain the token
public async Task<string> GetTokenUserOnly()
{
string signedInUserID = ClaimsPrincipal.Current.FindFirst(ClaimTypes.NameIdentifier).Value;
string tenantID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid").Value;
string userObjectID = ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
// get a token for the Graph without triggering any user interaction (from the cache, via multi-resource refresh token, etc)
ClientCredential clientcred = new ClientCredential(clientId, appKey);
// initialize AuthenticationContext with the token cache of the currently signed in user, as kept in the app's database
AuthenticationContext authenticationContext = new AuthenticationContext(aadInstance + tenantID, new TableTokenCache(signedInUserID));
//AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenSilentAsync(graphResourceID, clientcred, new UserIdentifier(userObjectID, UserIdentifierType.UniqueId));
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(graphResourceID, clientcred);
return authenticationResult.AccessToken;
}
You can't use ADAL to get tokens for graph.microsoft.com. ADAL is for graph.windows.net.
In order to get tokens for the Graph library (graph.windows.com) look into the Nuget Package Microsoft.Graph. Microsoft also has some documentation on how to pull user info using Graph.
Be forewarned though, using Graph Libraries and ADAL libraries side by side can lead to some weird side effects, such as the credential cache being cleared.
It seems you are using the client credential grant flow to acquire the access token for graph api(graphResourceID is https://graph.microsoft.com ?) :
AuthenticationResult authenticationResult = authenticationContext.AcquireToken(graphResourceID, clientcred);
So you need to grant app permission in azure ad portal :
For error "Access token validation failure" , you could use online tool like http://jwt.calebb.net/ to decode your access token , check the audience or lifetime of the access token .
To obtain a valid token for Microsoft Graph API you can use Azure.Identity.
To use any implementation of TokenCredential we need to build our own IAuthenticationProvider.
public class TokenCredentialAuthenticationProvider : IAuthenticationProvider
{
private readonly TokenCredential _tokenCredential;
public TokenCredentialAuthenticationProvider(TokenCredential tokenCredential)
{
_tokenCredential = tokenCredential;
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
var accessToken = await _tokenCredential.GetTokenAsync(new TokenRequestContext(new[] { "https://graph.microsoft.com" }), CancellationToken.None);
request.Headers.Authorization = new AuthenticationHeaderValue("bearer", accessToken.Token);
}
}
Now we can for instance use AzureCliCredential to acquire an access token.
Open Powershell and type in az login in order to login with your Azure AD account.
In Azure you could also use Managed Identity to get a token based on a Azure resource e.g. Azure App Service. Here need to use ManagedIdentityToken.
Usage:
var client = new GraphServiceClient(new TokenCredentialAuthenticationProvider(new AzureCliCredential()));
var user = await client.Me.Request().GetAsync();

Create custom extension through Graph API with Client Credentials auth

I have a .NET Web API that I am using to do some interaction with Microsoft Graph and Azure AD. However, when I attempt to create an extension on the user, it comes back with Access Denied.
I know it is possible from the documentation here however, it doesnt seem to work for me.
For the API, I am using client credentials. So my web app authenticates to the API using user credentials, and then from the API to the graph it uses the client.
My app on Azure AD has the Application Permission Read and Write Directory Data set to true as it states it needs to be in the documentation for a user extension.
I know my token is valid as I can retrieve data with it.
Here is my code for retrieving it:
private const string _createApprovalUrl = "https://graph.microsoft.com/beta/users/{0}/extensions";
public static async Task<bool> CreateApprovalSystemSchema(string userId)
{
using(var client = new HttpClient())
{
using(var req = new HttpRequestMessage(HttpMethod.Post, _createApprovalUrl))
{
var token = await GetToken();
req.Headers.Add("Authorization", string.Format("Bearer {0}", token));
req.Headers.TryAddWithoutValidation("Content-Type", "application/json");
var requestContent = JsonConvert.SerializeObject(new { extensionName = "<name>", id = "<id>", approvalLimit = "0" });
req.Content = new StringContent(requestContent, Encoding.UTF8, "application/json");
using(var response = await client.SendAsync(req))
{
var content = await response.Content.ReadAsStringAsync();
ApprovalSystemSchema schema = JsonConvert.DeserializeObject<ApprovalSystemSchema>(content);
if(schema.Id == null)
{
return false;
}
return true;
}
}
}
}
Is there anyone who may have a workaround on this, or information as to when this will be doable?
Thanks,
We took a look and it looks like you have a bug/line of code missing. You appear to be making this exact request:
POST https://graph.microsoft.com/beta/users/{0}/extensions
Looks like you are missing the code to replace the {0} with an actual user id. Please make the fix and let us know if you are now able to create an extension on the user.

Azure App Services (Mobile Apps) AAD authentication token refresh

I am trying to use Azure Active Directory to perform login functions on my uwp app. This happens successfully however I cannot get it to refresh the token when it expires and always receive the error "Refresh failed with a 403 Forbidden error. The refresh token was revoked or expired." and so I have to bring up the login window again. I am using the version 2.1.0 and the following code to authenticate:
private async Task<bool> AuthenticateAsync(bool forceRelogon = false)
{
//string message;
bool success = false;
// Use the PasswordVault to securely store and access credentials.
PasswordVault vault = new PasswordVault();
PasswordCredential credential = null;
//Set the Auth provider
MobileServiceAuthenticationProvider provider = MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory;
MobileServiceUser user = null;
try
{
// Try to get an existing credential from the vault.
var credentials = vault.FindAllByResource(provider.ToString());
credential = credentials.FirstOrDefault();
}
catch (Exception ex)
{
// When there is no matching resource an error occurs, which we ignore.
Debug.WriteLine(ex);
}
if (credential != null && !forceRelogon)
{
// Create a user from the stored credentials.
user = new MobileServiceUser(credential.UserName);
credential.RetrievePassword();
user.MobileServiceAuthenticationToken = credential.Password;
// Set the user from the stored credentials.
App.MobileService.CurrentUser = user;
//message = string.Format($"Cached credentials for user - {user.UserId}");
// Consider adding a check to determine if the token is
// expired, as shown in this post: http://aka.ms/jww5vp.
if (RedemptionApp.ExtensionMethods.TokenExtension.IsTokenExpired(App.MobileService))
{
try
{
await App.MobileService.RefreshUserAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
success = true;
}
else
{
try
{
// Login with the identity provider.
user = await App.MobileService
.LoginAsync(provider);
// Create and store the user credentials.
if (credential != null)
vault.Remove(credential);
credential = new PasswordCredential(provider.ToString(),
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
success = true;
//message = string.Format($"You are now logged in - {user.UserId}");
}
catch (MobileServiceInvalidOperationException)
{
//message = "You must log in. Login Required";
}
}
//var dialog = new MessageDialog(message);
//dialog.Commands.Add(new UICommand("OK"));
//await dialog.ShowAsync();
return success;
}
Can anyone see something wrong with what I am doing, or need to do anything within the AAD service provider?
You might be able to get more accurate information by taking a look at the server-side application logs. Token refresh failure details will be logged there automatically. More details on application logs can be found here: https://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/. I recommend setting the trace level to Informational or Verbose.
Also, if you haven't done this already, Azure AD requires a bit of extra configuration to enable refresh tokens. Specifically, you need to configure a "client secret" and enable the OpenID Connect hybrid flow. More details can be found in this blog post: https://cgillum.tech/2016/03/07/app-service-token-store/ (scroll down to the Refreshing Tokens section and see where it describes the process for AAD).
Besides what has been said about mobile app configuration, I can spot this.
You have:
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
It should be:
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
new Dictionary<string, string>() {{ "response_type", "code id_token" }});
Maybe this will help:
https://azure.microsoft.com/en-us/blog/mobile-apps-easy-authentication-refresh-token-support/

How to enable App Service Mobile App SSO for UWP

I am building a Universal Windows Platform (UWP) app that uses the Azure App Service Mobile App backend as well as the user's OneDrive account. I have 2 requirements for authentication:
If the user is logged in to their UWP device with a Microsoft account (e.g. Windows 10) then I don't want them to be presented with a login prompt (i.e. Single Sign On, re-using their Microsoft account credentials).
I want to have a single authentication event across Azure & OneDrive, i.e. the user authorises once and I re-use that token for both services.
I did this in Windows Phone 8 with an Azure Mobile Service by logging in with the Live SDK and then passing the returned token to the MobileServiceClient.LoginAsync() method, however I can't get this to work in UWP with an Azure Mobile App. When I call that same method I receive a 401 Unauthorised response.
I have associated my UWP app with the store and set up the
application at the Microsoft Account Developer Centre, including
adding the redirect URI from the Azure Mobile App.
I have set up the Azure App Service Mobile App, including adding the
Client ID & Secret from the Microsoft Account Developer Centre.
I have tried numerous ways to retrieve the token, including the
OnlineIdAuthenticator, WebAuthenticationCoreManager and
WebAuthenticationBroker. None has worked so far.
I currently use the following code in a class LiveAuthenticationService to retrieve an access token:
public async Task<bool> LoginAsync()
{
AccessToken = null;
bool success = false;
OnlineIdAuthenticator onlineIdAuthenticator = new OnlineIdAuthenticator();
EventWaitHandle waithandle = new ManualResetEvent(false);
OnlineIdServiceTicketRequest serviceTicketRequest = new OnlineIdServiceTicketRequest(scopes, "DELEGATION");
UserIdentity result = await onlineIdAuthenticator.AuthenticateUserAsync(serviceTicketRequest);
if (!string.IsNullOrWhiteSpace(result?.Tickets[0]?.Value))
{
currentUserId = result.SafeCustomerId;
AccessToken = result.Tickets[0].Value;
success = true;
waithandle.Set();
}
else
{
await logger.LogErrorAsync("Error signing in to Microsoft Live",
new Dictionary<string, string> { { "errorCode", result?.Tickets[0]?.ErrorCode.ToString() } });
}
waithandle.WaitOne(10000); //10 second timeout
return success;
}
And then this to attempt to login to my Azure Mobile App with that token, which uses LiveAuthenticationService from above:
private async Task RefreshUserIdAndAccessToken()
{
try
{
var tcs = new TaskCompletionSource<MobileServiceUser>();
var authService = new LiveAuthenticationService();
await UiDispatcher.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
try
{
await authService.LoginAsync();
var jsonAuthenticationToken = JObject.Parse(#"{""authenticationToken"": """ + authService.AccessToken + #"""}");
tcs.SetResult(await mobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount, jsonAuthenticationToken));
}
catch (Exception ex)
{
tcs.SetException(ex);
}
});
var user = await tcs.Task;
currentUserId = user.UserId;
AccessToken = user.MobileServiceAuthenticationToken;
}
catch (Exception ex)
{
await logger.LogExceptionAsync(ex,
Constants.LOGGING_DATAKEY_REFRESHACCESSTOKENFAILURE,
currentUserId);
currentUserId = null;
AccessToken = null;
}
}
As stated this results in a 401 Unauthorised response from Azure. I have run Fiddler and the request seems to be correct, the expected authentication token is included in a JSON payload with the request.
UPDATE
One thing I can see is that the token issued by the code above is almost 900 characters long, all in the form YnElFkAAcK8bRSQab/FK+PT5n/wA4CPU..., while the token issued if I let Azure Mobile App handle the authentication, i.e. call MobileServiceClient.LoginAsync() without passing a token, is only about 350 characters long and in the form hbGciOi.eyJmdWWxsIiwiRGJn... (notice the period towards the beginning).
This issue is really causing me problems now. I can't release the app without the authentication working and I can't figure out how to fix it. Any help will be appreciated.
This was a tough one for me to solve as I was facing this problem too.
The most important part is the OnlineIdServiceTicketRequest the request should look like this:
var mobileServicesTicket = new OnlineIdServiceTicketRequest("https://yourmobileservice.azure-mobile.net/", "JWT");
Note that we are specifying your endpoint and also requesting a JWT token instead of delegation. This will get the 350ish character token you were looking for.
Here is a full code sample of what I'm doing:
public async Task<bool> LoginAsync()
{
var authenticator = new Windows.Security.Authentication.OnlineId.OnlineIdAuthenticator();
var mobileServicesTicket = new Windows.Security.Authentication.OnlineId.OnlineIdServiceTicketRequest("https://yourendpoint.azure-mobile.net/", "JWT");
var ticketRequests = new List<OnlineIdServiceTicketRequest>() { mobileServicesTicket };
var authResult = await authenticator.AuthenticateUserAsync(ticketRequests, CredentialPromptType.PromptIfNeeded);
if ((authResult.Tickets.Count == 1) && (authResult.Tickets[0].ErrorCode == 0))
{
var accessToken = authResult.Tickets[0];
var res = await _mobileServiceClient.LoginWithMicrosoftAccountAsync(accessToken.Value);
return true;
}
else
{
return false;
}
}
_mobileServiceClient is injected into the class and is a reference to Microsoft.WindowsAzure.MobileServices.MobileServiceClient object within the WindowsAzure.MobileServices library.
I actually ended up writing a blog article about this problem here http://jshapland.com/single-sign-on-with-azure-mobile-services-in-a-uwp-app/

Resources