AADSTS70001: Application is not supported for this API version - azure

I have an ASP.NET MVC web application that will need to check if a user is member of a specific group in an Azure Active Directory. To achieve this I will use Microsoft Graph API, so I downloaded their example to try it out from here and got it running fine.
My next step is to get it running with my own AppId, AppSecret and RedirectUri and this is where I get in trouble. In Azure I went to "App registrations" for the AAD and assured that the application was added. I opened the app and copied the "Application ID" for the AppId, created a key and used it as the AppSecret. I checked all the permissions and pressed "Grant Permissions" and added my URL to the Reply URLs. I also changed the Authority attribute from https://login.microsoftonline.com/common/v2.0 to https://login.windows.net/xxxxxx.onmicrosoft.com.
When I run the application and press "Sign in" I will come to the login screen correctly, but it will crash when I try to sign in. The crash occurs when running AcquireTokenByAuthorizationCodeAsync in the code below:
AuthorizationCodeReceived = async (context) =>
{
var code = context.Code;
string signedInUserID = context.AuthenticationTicket.Identity.FindFirst(ClaimTypes.NameIdentifier).Value;
ConfidentialClientApplication cca = new ConfidentialClientApplication(
appId,
redirectUri,
new ClientCredential(appSecret),
new SessionTokenCache(signedInUserID, context.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase));
string[] scopes = graphScopes.Split(new char[] { ' ' });
AuthenticationResult result = await cca.AcquireTokenByAuthorizationCodeAsync(scopes, code);
},
AuthenticationFailed = (context) =>
{
context.HandleResponse();
context.Response.Redirect("/Error?message=" + context.Exception.Message);
return Task.FromResult(0);
}
The error message I get in AuthenticationFailed looks like this:
AADSTS70001: Application 'xxxxxxxx-4f81-4508-8dcb-df5b94f2290f' is not supported for this API version. Trace ID: xxxxxxxx-d04c-4793-ad14-868810f00c00 Correlation ID: xxxxxxxx-ab83-4805-baea-8f590991ec0c Timestamp: 2017-06-13 10:43:08Z
Can someone explain to me what the error message means? Should I try a different version? I have tried with both Microsoft.Graph v1.3.0 / Microsoft.Graph.Core v1.4.0 and Microsoft.Graph v1.4.0 / Microsoft.Graph.Core v1.5.0.

You are trying to use MSAL with the v1 endpoints. This is evident from using ConfidentialClientApplication. You need to use ADAL (Azure AD Authentication Library) instead. Or register the app for v2 at https://apps.dev.microsoft.com.
You can get ADAL from NuGet: https://www.nuget.org/packages/Microsoft.IdentityModel.Clients.ActiveDirectory/
You can find a sample app using ADAL here: https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect
Your OnAuthorizationCodeReceived will need to look something like this:
private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
ClientCredential credential = new ClientCredential(clientId, appKey);
string userObjectID = context.AuthenticationTicket.Identity.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
AuthenticationContext authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
// If you create the redirectUri this way, it will contain a trailing slash.
// Make sure you've registered the same exact Uri in the Azure Portal (including the slash).
Uri uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, graphResourceId);
}

Related

.NET Core 2.1 Azure AD B2C Web and API

It is easy to create an Azure AD B2C Web or API App out of the box using VS 2017 15.7.2+.
So I created a solution that has both, but I am trying to get the access token from the Web App and use it to call the API and I cannot find a way.
I tried option 1 (doesn't work):
private async Task<string> GetAccessTokenCacheAsync()
{
string authority = $"{AzureAdOptions.Instance}{AzureAdOptions.ClientId}/{AzureAdOptions.SignUpSignInPolicyId}/";
string clientId = AzureAdOptions.ClientId;
Uri redirectUri = new Uri("https://localhost:12345/");
string resource = targetApplicationID;
AuthenticationContext ac = new AuthenticationContext(authority);
AuthenticationResult result = null;
try
{
result = await ac.AcquireTokenSilentAsync(resource, clientId);
}
catch (AdalException adalException)
{
if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently
|| adalException.ErrorCode == AdalError.InteractionRequired)
{
result = await ac.AcquireTokenAsync(resource, clientId, redirectUri,
new PlatformParameters()); //PromptBehavior.Auto
}
}
return result.AccessToken;
}
And I Got:
System.NotImplementedException: 'The method or operation is not implemented.'
I tried option 2 (doesn't work), MSAL (Microsoft.Identity.Client):
private async Task<string> GetAccessTokenCacheAsync() {
string authority = $"{AzureAdOptions.Instance}{AzureAdOptions.ClientId}/{AzureAdOptions.SignUpSignInPolicyId}/";
PublicClientApplication myApp = new PublicClientApplication(AzureAdOptions.ClientId, authority);
AuthenticationResult authenticationResult = await myApp.AcquireTokenAsync(
new[] { $"{ApiScopeUrl}/read", $"{ApiScopeUrl}/write" }
,
myApp.Users.FirstOrDefault()
,
UIBehavior.ForceLogin
,null,null
,authority
).ConfigureAwait(false);
return authenticationResult.AccessToken;
}
And I Got:
System.NotImplementedException: 'The method or operation is not implemented.'
I also tried to adapt the code from Azure samples such as https://github.com/Azure-Samples/active-directory-b2c-dotnet-webapp-and-webapi (Framework 4.5.1) or https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect-aspnetcore (Core 2.0 and OpenID, no B2C) and fail.
This https://github.com/Azure-Samples/active-directory-b2c-dotnetcore-webapp also mention to implement OnAuthorizationCodeReceived, but Core 2.1 seems to has this implemented with AddAzureADB2C (black-boxed). The documentation on this new library seems to be missing or in progress because I can't find it.
I want to know, how to get the Access Token using built-in features in .NET Core 2.1?
I believe if I know how to implement OnAuthorizationCodeReceived, I will be one step closer to find the answer. Here the code source and ticket that I created relates to this: https://github.com/aspnet/AADIntegration/issues/21

How to call Microsoft Graph from console application c#

I need to call Microsoft Graph API to create user in Azure AD.
First I need to test from console application and then need to implement in Azure function.
https://developer.microsoft.com/en-us/graph/graph-explorer
I am new to Microsoft Graph API , How can I connect and execute API from c# console application.
I have already registered the application in AAD.
I am trying to acquire token as :
string resourceId = "https://graph.microsoft.com";
string tenantId = "<tenantID>";
string authString = "https://login.microsoftonline.com/" + tenantId;
string upn = String.Empty;
string clientId = "<ClientID>";
string clientSecret = "<clientSecret>";
//string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
log.Verbose("ClientSecret=" + clientSecret);
log.Verbose("authString=" + authString);
var authenticationContext = new AuthenticationContext(authString, false);
// Config for OAuth client credentials
ClientCredential clientCred = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resourceId,clientCred);
string token = authenticationResult.AccessToken;
log.Verbose("token=" + token);
I trying to use existing AADB2C.
b2c-extensions-app. Do not modify. Used by AADB2C for storing user data.
I have enabled permission as:
I neither get exception nor get access token and program silently exit
Also :
There is new library
<package id="Microsoft.Identity.Client" version="1.1.0-preview" targetFramework="net46" />
How can I direct login without login pop-up with the following and acquire token ?
PublicClientApplication
I assume that you already have Azure AD application with granted Administrative Consent.
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
Then you have to add two NuGet dependencies to your dotnet project
<PackageReference Include="Microsoft.Graph" Version="1.15.0" />
<PackageReference Include="Microsoft.Identity.Client" Version="4.0.0" />
Microsoft.Identity.Client for authentication using Azure AD and Microsoft.Graph for executing MS Graph queries.
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure app builder
var authority = $"https://login.microsoftonline.com/{tenantId}";
var app = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority(new Uri(authority))
.Build();
// Acquire tokens for Graph API
var scopes = new[] {"https://graph.microsoft.com/.default"};
var authenticationResult = await app.AcquireTokenForClient(scopes).ExecuteAsync();
// Create GraphClient and attach auth header to all request (acquired on previous step)
var graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(requestMessage => {
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("bearer", authenticationResult.AccessToken);
return Task.FromResult(0);
}));
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
Update 2020.01
There is a new package Microsoft.Graph.Auth that simplify auth and token management.
Let's say you want to use some Beta API this time.
<PackageReference Include="Microsoft.Graph.Auth" Version="1.0.0-preview.2" />
<PackageReference Include="Microsoft.Graph.Beta" Version="0.12.0-preview" />
var tenantId = "you-azure-tenand-id";
var clientId = "azure-ad-application-id";
var clientSecret = "unique-secret-generated-for-this-console-app";
// Configure application
var clientApplication = ConfidentialClientApplicationBuilder
.Create(clientId)
.WithTenantId(tenantId)
.WithClientSecret(clientSecret)
.Build();
// Create ClientCredentialProvider that will manage auth token for you
var authenticationProvider = new ClientCredentialProvider(clientApplication);
var graphClient = new GraphServiceClient(authenticationProvider);
// Call Graph API
var user = await graphClient.Users["Me#domain.com"].Request().GetAsync()
In order to connect from a console app, you'll need to first obtain a valid token. Since you lack a UI, you'll want to Get access without a user. Note that this type of "app-only" token requires Administrative Consent before it can be used.
In order to support the Create User scenario, you will need to ensure your permission scopes include User.ReadWrite.All.
Once you have a valid token you can make calls into the Graph API. Graph is a REST API so all calls are made over HTTP with the token passed within the Authorization Header.
You can read a general overview at Get started with Microsoft Graph and REST. There are also several language/framework specific overviews available but all of them assume you have a UI (i.e. not simply console). Generally speaking, if you're looking for a console tool for creating users you may prefer using PowerShell.
This question is rather old, but it was one of the first questions that popped up when I initially needed to do the same thing. Below I will document the steps and resources I used to make it happen:
I used an O365 tenant (you can get one from office.com - note that you can get a one year developer trial). Once you have a tenant, you also have access to Azure portal if you log in as your tenant admin user. Under Azure Portal, go to Active Directory/properties to see the tenant ID.
I followed the instructions here https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-v2-netcore-daemon to create a new registered application. I created a new secret and copied the value (that will be client secret in your console app). The registered application id will be the client ID in your console app.
I cloned the github repo in the above link and changed the values in the appsettings to the tenant ID, client ID, and client secret noted in the steps above.
The code in that repo has some methods called which no longer exist in ConfigurationBuilder as of .NETCore 2.1. I substituted these lines (there's probably a better / shorter way):
authenticationConfig.Tenant = Configuration.GetSection("Tenant").Value.ToString();
authenticationConfig.ClientId = Configuration.GetSection("ClientId").Value.ToString();
authenticationConfig.ClientSecret = Configuration.GetSection("ClientSecret").Value.ToString();
You should now be iterating through users in your tenant. You can go to the graph explorer ( https://developer.microsoft.com/en-us/graph/graph-explorer ) to find more URLs (find the line in Program.cs to substitute them). As far as I know so far, v2.0 of the API is "beta" (put "beta" where "v1.0" is - someone please correct me if I'm wrong).
await apiCaller.CallWebApiAndProcessResultASync("https://graph.microsoft.com/v1.0/users", result.AccessToken, Display);
This MSAL console app tutorial describes getting a token using MSAL (Microsoft Authentication Library) in a .NET console app.
To make a Microsoft Graph call, I replaced the RunAsync() function
with this, which attaches the acquired token to the requests with the
GraphServiceClient:
static async Task RunAsync()
{
const string clientId = "your client id";
string[] scopes = { "User.Read" };
AuthenticationResult result;
var clientApp = new PublicClientApplication(clientId);
try
{
result = await clientApp.AcquireTokenAsync(scopes.Split(new char[] { ' ' }));
Console.WriteLine(result.AccessToken);
GraphServiceClient graphClient = new GraphServiceClient(
new DelegateAuthenticationProvider(
async (requestMessage) =>
{
// Append the access token to the request.
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", result.AccessToken);
// Some identifying header
requestMessage.Headers.Add("SampleID", "aspnet-connect-sample");
}));
// Get a page of mail from the inbox
var inboxMail = await graphClient.Me.MailFolders.Inbox.Messages.Request().GetAsync();
foreach(var mail in inboxMail.CurrentPage.ToList())
{
Console.Write("From: {0}\nSubject: {1}\nBody:\n{2}\n--------------------\n",
mail.From.EmailAddress.Address, mail.Subject, mail.BodyPreview);
}
}
// Unable to retrieve the access token silently.
catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}

Resource not found for the segment 'me'

i'm using Graph API to retrieve profile information of user who's currently logged in from the Azure AD, unfortunately i'm receiving the following error message :
{"odata.error":{"code":"Request_ResourceNotFound","message":{"lang":"en","value":"Resource not found for the segment 'me'."}}}
Below is my code :
Uri serviceRoot = new Uri(serviceRootURL);
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
async () => await GetAppTokenAsync());
var user = (User)await adClient.Me
.Expand(x => x.Manager)
.ExecuteAsync();
And below is my code for GetAppTokenAsync() :
private static async Task<string> GetAppTokenAsync()
{
// Instantiate an AuthenticationContext for my directory (see authString above).
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
// Create a ClientCredential that will be used for authentication.
// This is where the Client ID and Key/Secret from the Azure Management Portal is used.
ClientCredential clientCred = new ClientCredential(clientID, clientSecret);
// Acquire an access token from Azure AD to access the Azure AD Graph (the resource)
// using the Client ID and Key/Secret as credentials.
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resAzureGraphAPI, clientCred);
// Return the access token.
return authenticationResult.AccessToken;
}
From your code "await GetAppTokenAsync()" , you are getting an app-only token , which using application identity, instead of as a user's identity .
The "(User)await adClient.Me" won't work if that token is not associated with a user .
To use app token to get user manager information ,you need to specify the user you want to query , code below is for your reference :
try
{
User manager = (User)await adClient.Users.GetByObjectId("5eba8883-c258-45d0-8add-a286a1ec1e91").Manager.ExecuteAsync();
}
catch (Exception ex)
{
throw;
}
Update
You could use authorization code flow for delegated permissions(user's identity) . If you want a client library code sample , you could refer to this code sample . After user sign in , you could use below code to get manager of current login user :
ActiveDirectoryClient client = AuthenticationHelper.GetActiveDirectoryClient();
User manager = (User)await client.Me.Manager.ExecuteAsync();
I used an application identity with the legacy Azure Active Directory api and the 'Application.ReadWrite.OwnedBy' permission to work around the Resource not found for the segment 'me' error. The same permission exists in the Microsoft Graph api, but the behavior is not identical. More information here.

Application access to SharePoint Online using Azure AD Token

How can I get an application token to query SharePoint with application credentials (= without user impersonation) using Azure AD?
The following code works perfectly for querying data as a user but we need to fetch information without impersonation like listing all sites in the collection regardless of user permissions etc.
Exception thrown:
An exception of type
'Microsoft.IdentityModel.Clients.ActiveDirectory.AdalServiceException'
occurred in mscorlib.dll but was not handled in user code
Additional information: AADSTS70001: Application with identifier 'xxx'
was not found in the directory sharepoint.com
Code to get token:
internal static async Task<string> GetSharePointAccessToken(string url, string userAccessTokenForImpersonation)
{
string clientID = #"<not posted on stack overflow>";
string clientSecret = #"<not posted on stack overflow>";
var appCred = new ClientCredential(clientID, clientSecret);
var authContext = new Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext("https://login.windows.net/common");
// Use user assetion if provided, otherwise use principal account
AuthenticationResult authResult = null;
if (string.IsNullOrEmpty(userAccessTokenForImpersonation))
{
authResult = await authContext.AcquireTokenAsync(new Uri(url).GetLeftPart(UriPartial.Authority), appCred);
}
else
{
authResult = await authContext.AcquireTokenAsync(new Uri(url).GetLeftPart(UriPartial.Authority), appCred, new UserAssertion(userAccessTokenForImpersonation));
}
return authResult.AccessToken;
}
Test code:
// Auth token from Bearer https://xxx.azurewebsites.net/.auth/me
string authHeader = #"<valid jwt bearer token from azure auth>";
var sharePointUrl = #"https://xxx.sharepoint.com/sites/testsite/";
string sharePrincipalToken = await GetSharePointAccessToken(sharePointUrl, null); // <-- doesn't work
string sharePointUserToken = await GetSharePointAccessToken(sharePointUrl, authHeader); // <-- works
Permissions in Azure AD:
The error message you are getting implies that you are signing in with a user that is pointing our token service to get a token in the context of "sharepoint.com"
This is because you are using the "common" endpoint. Read more about that here.
Instead try using a fixed endpoint, where the tenant is the same as where the application is registered and see if that solves your issue.
If your plan is to make this application accessible by multiple tenants, make sure that you have explicitly set your application to be multi-tenant, and then make sure you have a user from the external tenant try and sign into the application before you try doing service to service calls.
Let me know if this helps.

Getting Unauthorized from from Azure Web API

I created a basic project using Visual Studio 2015 Update 3 for Web API (nothing custom, bare bone) and deployed it to Azure (Free Account) following the instruction here.
Then I created a Console client with the following code.
public static async Task<bool> ReadValues()
{
try
{
// Authenticate the user and get a token from Azure AD
//AuthenticationResult authResult = await AuthContext.AcquireTokenSilentAsync(Resource, ClientId);
AuthenticationResult authResult = AuthContext.AcquireToken(Resource, ClientId, RedirectUri);
// Create an HTTP client and add the token to the Authorization header
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
//"Bearer"
authResult.AccessTokenType
, authResult.AccessToken);
// Call the Web API to get the values
var requestUri = new Uri(WebApiUri, "api/values");
Console.WriteLine("Reading values from '{0}'.", requestUri);
HttpResponseMessage httpResponse = await httpClient.GetAsync(requestUri);
Console.WriteLine("HTTP Status Code: '{0}'", httpResponse.StatusCode.ToString());
//Console.WriteLine("HTTP Header: '{0}'", httpClient.DefaultRequestHeaders.Authorization.ToString());
if (httpResponse.IsSuccessStatusCode)
{
//
// Code to do something with the data returned goes here.
//
var s = await httpResponse.Content.ReadAsStringAsync();
Console.WriteLine(s);
}
else
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine(httpResponse.ReasonPhrase);
}
return (httpResponse.IsSuccessStatusCode);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
return false;
}
It works fine when I run the WEB API locally from the Visual Studio in debug, but when I deploy it to the Azure, it returns Unauthorized.
Few common things that I might get asked:
I do receive a valid bearer token
I have created the App registrations in the Azure AD for bot hthe WEB API and the client
The client and WEB API are using the correct redirect, resource uri
The account I am using to login is the same as the one used to create the Azure account and it has full privileges in the domain/AD/API
On the API side, this is whole of the startup.auth.cs
using System.Configuration;
using System.IdentityModel.Tokens;
using Microsoft.Owin;
using Microsoft.Owin.Security.ActiveDirectory;
using Owin;
using WebApi;
[assembly: OwinStartup("default", typeof(Startup))]
namespace WebApi
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters {
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
}
}
What else should I check?
Other references
https://www.simple-talk.com/cloud/security-and-compliance/azure-active-directory-part-3-developing-native-client-applications/
Thanks for help from Juunas who provided me with a working copy, I was able to narrow down the cause. When I attached a debugger to the Azure instance of the Web API I was able to see a exception for Bad Audience. On trying to retrace my steps, I found that while deployment from Visual Studio, I was selection Enterprise Authentication in settings that was causing the web.config to change in way that lead to the problem. Not selecting that option, I was able to access the API through bearer token.

Resources