Connecting with ADAL to Microsoft Graph gives 406 error - c#

I am trying to get a ActiveDirectoryClient in a C# client, like this:
Uri servicePointUri = new Uri("https://graph.microsoft.com/v1.0/me/messages");
Uri serviceRoot = new Uri(servicePointUri, <OUR-AZURE-TENANT-ID>);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(serviceRoot,
async () => await AcquireTokenAsyncForUser());
With this AcquireTokenAsyncForUser() method:
public static async Task<string> AcquireTokenAsyncForUser()
{
return await GetTokenForUser();
}
public static async Task<string> GetTokenForUser()
{
if (TokenForUser == null)
{
AuthenticationContext authenticationContext = new AuthenticationContext("https://login.microsoftonline.com/common/v2.0");
UserPasswordCredential userCredential = new UserPasswordCredential("<USERNAME>#outlook.com", <PASSWORD>);
AuthenticationResult userAuthnResult = await authenticationContext.AcquireTokenAsync("https://graph.microsoft.com/v1.0/me/messages",
<AZURE AD APP CLIENT ID>, userCredential);
TokenForUser = userAuthnResult.AccessToken;
Console.WriteLine("\n Welcome " + userAuthnResult.UserInfo.GivenName + " " +
userAuthnResult.UserInfo.FamilyName);
}
return TokenForUser;
}
I keep getting this error:
Error getting signed in user
accessing_ws_metadata_exchange_failed: Accessing WS metadata exchange failed-
Response status code does not indicate success: 406 (NotAcceptable).-
It does not matter if I use correct or incorrect credentials.

AAD does not support WS-Trust sign in for MSA accounts. You have to sign in the user via webview by calling
AcquireTokenAsync("https://graph.microsoft.com/v1.0/me/messages",
<AZURE AD APP CLIENT ID>, new Uri("<your redirect uri>", new PlatformParameters(PromptBehavior.Auto{or whatever you want}, null));

Related

Calling MS Graph API has error CompactToken parsing failed with error code: 80049217

We implement to get the phone numbers being used in MFA of the signed-in user. We use password grant flow where we have a service account(with Global admin role) that will call MS Graph API on behalf of the user.
We are able to get the access token. However, when making a call to MS Graph encounters the error below.
Error:
ServiceException: Code: InvalidAuthenticationToken
Message: CompactToken parsing failed with error code: 80049217
MS Graph API call:
MicrosoftGraphClientSDK client = new MicrosoftGraphClientSDK();
var graphClient = client.GetAuthenticatedClient();
// Error encountered here:
var phones = await graphClient.Me.Authentication.PhoneMethods[{objectiD of the user}].Request().GetAsync();
This is how we get the access token in GetAuthenticatedClient
public MicrosoftGraphClientSDK()
{
_app_public = PublicClientApplicationBuilder.Create(clientID)
.WithAuthority("https://login.microsoftonline.com/{tenantID}")
.Build();
}
public Beta.GraphServiceClient GetAuthenticatedClient()
{
var accessToken = GetUserAccessTokenAsync();
var delegateAuthProvider = new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken.ToString());
return Task.FromResult(0);
});
_graphClient = new Beta.GraphServiceClient(delegateAuthProvider);
return _graphClient;
}
public async Task<string> GetUserAccessTokenAsync()
{
AuthenticationResult result;
var accounts = await _app_public.GetAccountsAsync();
if (accounts.Any())
{
result = await _app_public.AcquireTokenSilent(_scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
SecureString password = new SecureString();
foreach (char c in pass)
password.AppendChar(c);
result = await _app_public
.AcquireTokenByUsernamePassword(_scopes, username, password)
.ExecuteAsync();
}
return result.AccessToken;
}
I have search online about the error but could not get figure out the solution.
I appreciate your response. Thanks.

The token response was successfully returned: unsupported_grant_type

I'm migrating from .NET Core 1.1 to 2.0, and now I have to update my Authentication too.
I'm using OAuth and OpenIddict to .NET Core 2.0
When I'm sending the request to my connect/token I'm getting this:
OpenIddict.Server.OpenIddictServerHandler[0] The token response was
successfully returned: {
"error": "unsupported_grant_type",
"error_description": "The specified 'grant_type' parameter is not
supported."
}.
This is my request method:
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, $"{url}/connect/token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "client_credentials",
["client_id"] = clientId,
["client_secret"] = clientSecret,
["pessoaid"] = pessoaId,
["usuarioid"] = usuarioId,
["conta"] = conta,
["cpfcnpj"] = userDoubleCpf,
["fonteDados"] = fonteDados,
["userIdsLogged"] = userIdsLogged
});
var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
var result = JObject.Parse(await response.Content.ReadAsStringAsync());
if (result["error"] != null)
{
throw new InvalidOperationException("An error occurred while retrieving an access token.");
}
return result;
}
My OpenIddictApplications is generated when an application is linked to the user account, so the ClientId and Secret is generated, when a login request is send to my API and retrieve the respective values.
I have folowed the oppeniddict documentation and I have included everything in my Startup.cs
This is my AuthorizationController:
[HttpPost("~/connect/token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
Debug.Assert(request.IsTokenRequest(),
"The OpenIddict binder for ASP.NET Core MVC is not registered. " +
"Make sure services.AddOpenIddict().AddMvcBinders() is correctly called.");
if (request.IsClientCredentialsGrantType())
{
// Note: the client credentials are automatically validated by OpenIddict:
// if client_id or client_secret are invalid, this action won't be invoked.
var application = await _applicationManager.FindByClientIdAsync(request.ClientId, HttpContext.RequestAborted);
if (application == null)
{
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.InvalidClient,
ErrorDescription = "The client application was not found in the database."
});
}
// Create a new authentication ticket.
var ticket = CreateTicket(request, application);
return SignIn(ticket.Principal, ticket.Properties, ticket.AuthenticationScheme);
}
return BadRequest(new OpenIdConnectResponse
{
Error = OpenIdConnectConstants.Errors.UnsupportedGrantType,
ErrorDescription = "The specified grant type is not supported."
});
}
I'm generating the AuthenticationTicket and returning this.
Any idea about what might be causing this kind of badrequest when I try to send the request to take my token?
This happens because you do not configure the client credentials flow on you Startup.cs.
See the example: https://github.com/openiddict/openiddict-samples/blob/dev/samples/ClientCredentialsFlow/AuthorizationServer/Startup.cs
Attention for line 52:
// Enable the client credentials flow.
options.AllowClientCredentialsFlow();

Azure - Token is not renewed (Authentication_ExpiredToken). Why?

I have a C# Azure Web Application that accesses Azure Active Directory to get data from users and groups.
After 1h of activity, I get the following error:
{"odata.error":{"code":"Authentication_ExpiredToken","message":{"lang":"en","value":"Your access token has expired. Please renew it before submitting the request."},"date":"2018-03-16T16:17:59","requestId":"cfa18a20-3c2c-4806-ac36-9d4e9ba7738c","values":null}}
Here is my method to get a ActiveDirectoryClient:
using Microsoft.Azure.ActiveDirectory.GraphClient;
using Microsoft.Azure.ActiveDirectory.GraphClient.Extensions;
using Microsoft.IdentityModel.Clients.ActiveDirectory;
private ActiveDirectoryClient CreateGraphClient()
{
string tenantId = WebConfigurationManager.AppSettings["ida:TenantId"],
applicationId = WebConfigurationManager.AppSettings["ida:ClientId"],
secret = WebConfigurationManager.AppSettings["ida:ClientSecret"],
authority = WebConfigurationManager.AppSettings["ida:AADInstance"] + tenantId,
resrouce = "https://graph.windows.net",
token = string.Empty;
ClientCredential credential = new ClientCredential(applicationId, secret);
AuthenticationContext authContext = new AuthenticationContext(authority);
try
{
token = authContext.AcquireTokenAsync(resrouce, credential).Result.AccessToken;
}
catch (Exception)
{
token = authContext.AcquireTokenSilentAsync(resrouce, credential.ClientId).Result.AccessToken;
}
Uri baseServiceUri = new Uri(resrouce);
ActiveDirectoryClient activeDirectoryClient = new ActiveDirectoryClient(new Uri(baseServiceUri, tenantId), async () => await Task.FromResult(token));
return activeDirectoryClient;
}
I think the server should refresh the token but it's not happenning. I'm using ADAL 3.x so the 'refresh_token' doesn't exist anymore (I guess).
I know this try/catch is not the way but it was a test.
Any help?

Getting 401 (Unauthorized) error in Active Directory user image in Multi tenant Application

I have implemented Multi tenant application using Azure Active Directory in Angular 4.After user logged into my application i'm able get user info.But user photo is not getting from the Active directory for that i have implemented Graph API like below snippet.
public Task<UserDto> getPhoto(TenantDto tenantDto)
{
var client = new HttpClient();
client.BaseAddress = new Uri(String.Format("https://graph.windows.net/{0}/users/{1}/thumbnailPhoto?api-version=1.6", tenantDto.tenantKey, tenantDto.email));
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("image/jpeg"));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", tenantDto.token);
HttpResponseMessage response = client.GetAsync("").Result;
if (response.IsSuccessStatusCode)
{
return null;
//Status status = response.Content.ReadAsAsync<Status>().Result;
//if (status.Code == 200)
// InBoundResponse = JsonConvert.DeserializeObject<InBoundCallResponse>(status.Data.ToString());
//return InBoundResponse;
}
else
{
return null;
}
}
Here tenantDto.token is nothing but a logged in user "token" While calling this Graph API i'm getting 401 (Unauthorized) error. I have tried all but no use.
I have changed Graph API setting s in Active Directory APP also like below attachment
Also i have tried like below code it's working only for single tenant
[Route("AdUserImage"), HttpGet]
public async Task<HttpResponseMessage> userImage()
{
var authContext = new AuthenticationContext("https://login.windows.net/sampletest.onmicrosoft.com/oauth2/token");
var credential = new ClientCredential(clientID, clientSecret);
ActiveDirectoryClient directoryClient = new ActiveDirectoryClient(serviceRoot, async () =>
{
var result = await authContext.AcquireTokenAsync("https://graph.windows.net/", credential);
return result.AccessToken;
});
var user = await directoryClient.Users.Where(x => x.UserPrincipalName == "balu#sampletest.onmicrosoft.com").ExecuteSingleAsync();
DataServiceStreamResponse photo = await user.ThumbnailPhoto.DownloadAsync();
using (MemoryStream s = new MemoryStream())
{
photo.Stream.CopyTo(s);
var encodedImage = Convert.ToBase64String(s.ToArray());
}
//string token = await HttpAppAuthenticationAsync();
Status status = new Status("OK");
status = new Status("Found", null, "User exists.");
return Request.CreateResponse(HttpStatusCode.OK, status, _jsonMediaTypeFormatter);
}
but i need to implement for Multi tenant app.
Any Answer Appreciated.
Thanks in Advance........!
Delegate-user token:
1 .Acquire the token via the implict flow:
https://login.microsoftonline.com/{tenant}/oauth2/authorize?response_type=token&client_id={clientId}&redirect_uri={redirect_uri}&resource=https%3A%2F%2Fgraph.windows.net&nonce={nonce}
2 .Call the Azure AD Graph
GET: https://graph.windows.net/{tenant}/me/thumbnailPhoto?api-version=1.6
Content-Type: image/jpeg
Application token:
1 .Acquire the token via the client credentials flow
POST:https://login.microsoftonline.com/{tenant}/oauth2/token
grant_type=client_credentials&client_id={client_id}&client_secret={client_secret}&resource=https%3A%2F%2Fgraph.windows.net
2 .Call the Azure AD Graph
GET:https://graph.windows.net/{tenant}/users/{upn}/thumbnailPhoto?api-version=1.6
Content-Type: image/jpeg
If you only to get the thumbnail photo of sign-in user for the multiple tenant, you should login-in with Azure AD first and acquire the access token for the delegate user and used that token to call Azure AD Graph REST. Difference between these two kinds of token, you can refer the links below:
Get access on behalf of a user
Get access without a user
I'm using Delegate-user token as per your explnation using below url
https://login.microsoftonline.com/{tenant}/oauth2/authorize?response_type=token&client_id={clientId}&redirect_uri={redirect_uri}&resource=https%3A%2F%2Fgraph.windows.net&nonce={nonce}
But still not able receiving but i'm able getting 200 status but token is not return.i have implemented like below
var client = new HttpClient();
client.BaseAddress = new Uri("https://login.microsoftonline.com/{TenantID}/oauth2/authorize?response_type=token&client_id={ClientID}&redirect_uri={ApplicationUrl}&resource=https%3A%2F%2Fgraph.windows.net&nonce=a9d7730c-79f3-4092-803a-07f346de2cdf");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("text/html"));
HttpResponseMessage response = client.GetAsync("").Result;
if (response.IsSuccessStatusCode)
{
}
else
{
//return null;
}
It's not return the token.it is returning html content in success block

Sharepoint - ADAL OAuth - file does not exist

I am creating demo app that consumes sharepoint REST API. I'm able to successfully retrieve Bearer token (by client ID and certificate). App is registered in Azure AD and all Sharepoint permissions there are checked. I am able to retrieve all lists for example, but I am not able to retrieve a file. The same query works in browser. I assume it is permission problem. Do I have to register this app in SP? https://tenant.sharepoint.com/IT/_layouts/15/appinv.aspx I tried it without success, but I think permissions in Azure AD should be sufficient. My GET query returns
{"error":{"code":"-2130575338, Microsoft.SharePoint.SPException","message":{"lang":"en-US","value":"The file /IT/vystupnidokumentydoc/filename.docx does not exist."}}}
Same query is OK in browser in user context.
REST call:
private async static Task DoStuffInOffice365(string token)
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + token);
client.DefaultRequestHeaders.Add("Accept", "application / json; odata = verbose");
string url;
url = "https://tenant.sharepoint.com/_api/web/GetFileByServerRelativeUrl('/IT/vystupnidokumentydoc/filename.docx')";
using (HttpResponseMessage response = await client.GetAsync(url))
{
var contents = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
Console.WriteLine("Fail!");
else
Console.WriteLine("OK.");
}
}
Get token:
private async static Task<string> GetAccessToken()
{
//authentication context
string authority = "https://login.windows.net/tenant.onmicrosoft.com/";
AuthenticationContext authenticationContext = new AuthenticationContext(authority, false);
var certPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
certPath = certPath.Substring(0, certPath.LastIndexOf('\\')) + $"\\{CERT_FILE}";
var certfile = System.IO.File.OpenRead(certPath);
var certificateBytes = new byte[certfile.Length];
certfile.Read(certificateBytes, 0, (int)certfile.Length);
var cert = new X509Certificate2(
certificateBytes,
PRIVATE_KEY_PASSWORD,
X509KeyStorageFlags.Exportable |
X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
ClientAssertionCertificate cac = new ClientAssertionCertificate(CLIENT_ID, cert);
var authenticationResult = await authenticationContext.AcquireTokenAsync(P_URL, cac);
return token = authenticationResult.AccessToken;
}
Based on the error message, it seems the file was not found on the location provided. Please ensure the file exits on the site.
Do I have to register this app in SP?
If you were developing SharePoint add-in, yes. And you can refer here for the authentication/authorization for the SharePoint add-in.
If you were not developing an SharePoint add-ins, we also can use the Microsoft Graph-GetItem to get the item and download the drive item through this REST.

Categories

Resources