Azure Graph API : Error 403 Forbidden with Azure AD B2C - c#

I have an Azure AD B2C. Since Azure Active Directory has been migrated to new portal, I have a problem to read and write tenant users data with the Azure Graph API. Before, I had an application which was created from the old portal and which doesn't work now.
So, I created a new application from the new portal, as following :
Open "Azure Active Directory" tab
Open "App registrations"
Click "New application registration"
"Properties" tab with :
Name : GraphApi
App ID URI : https://myTenant.onmicrosoft.com/graphapi
Home page URL : https://graph.windows.net/winbizdev.onmicrosoft.com
"Reply URLs" tab with :
https:// graph.windows.net/winbizdev.onmicrosoft.com
"Required permissions" tab with :
Windows Azure Active Directory -> Check 3 elements which don't require admin
"Keys" tab with :
CLIENT_SECRET which never expires
Here is now my C# code to access user data from Azure Graph API :
_authContext = new AuthenticationContext("https://login.microsoftonline.com/myTenant.onmicrosoft.com");
_credential = new ClientCredential("<Application Client Guid>", "<Client secret which I created in "Keys" tab");
var result = await _authContext.AcquireTokenAsync("https://graph.windows.net/", _credential);
var http = new HttpClient();
var graphUrl = "https://graph.windows.net/myTenant.onmicrosoft.com/users/<My User Guid>?api-version=1.6";
var request = new HttpRequestMessage(new HttpMethod("GET"), graphUrl);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
var response = await http.SendAsync(request);
return response;
And I always obtain a 403 error Forbidden. The version of the assembly "Microsoft.IdentityModel.Clients.ActiveDirectory" is 3.13.8.99.
So, what is wrong in my configuration? What I have to do to read and write user tenant users data again with Azure Graph API?
Thanks for your help!
Alex

You are acquiring acess token using client credential flow . That means you need add related Application Permissions in Required permissions blade .
All application permissions of azure ad graph api need admin consent . Please click Grant Permissions button(login with admin's account) after adding application permissions .

Related

Failed to acquire ms graph token

I have problem while trying to acquire the ms graph access token without a user.
I login from code using the quickstart provided settings
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create("<application client ID>")
.WithClientSecret("<application secret>")
.WithAuthority(new Uri("https://login.microsoftonline.com/<tenant ID>"))
.Build();
var result = await app.AcquireTokenForClient(new List<string>() { "https://graph.microsoft.com/.default" })
.ExecuteAsync();
HttpClient sender = new HttpClient();
sender.DefaultRequestHeaders.Add(
"Authorization",
String.Format("Bearer " + result.AccessToken)
);
HttpResponseMessage meResult = await sender.GetAsync("https://graph.microsoft.com/v1.0/users/<email adress>/photo/$value");
string context =await meResult.Content.ReadAsStringAsync();
Console.WriteLine("WAAA");
Basically the problem is that I can aquire a token before creating the HTTP client, but when I would like to use it to get the user photo the response is 401 :(
With the message: 401 - Unauthorized: Access is denied due to invalid credentials The Azure application has these permissions granted to them.
Can anybody spot what am I missing?
Which permission do you have on your Azure portal? I guess you have now Delegated permission only.
You should have User.Read.All Application permission and afterwords need to add Grant admin consent. It should be like below:
You can get details here in our offical document
Permission:
Once you set the permission you can get below output:
Output:
Postman Test Result:
Azure profile Pciture:
Note: Your context required Application permission but seems you have Delegated Permission only.
Hope that would help.

Create a 'event as online meeting' or only the onlineMeeting using Microsoft Graph API

Any user who logged into our system (IdentityServer as Auth) under a specific tenant should be able to create an event as an online meeting (MS Teams).
We followed Build ASP.NET Core MVC apps with Microsoft Graph and Create and enable an event as an online meeting to create an application that authenticates an AD user of an organization and allow him to create an event as an online meeting.
We are able to implement it successfully and was able to create the event as an online meeting.
But the exact scenario here is any user who is authenticated in our web application (not a AD user) should be able create a MS Teams meeting event and share it with other participants who should be able to join the meeting.
I am not sure how to achieve this.
Edit
Or at least how do I create onlineMeeting ? I tried with Client credentials provider as below
IConfidentialClientApplication confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("<<App_Id>>")
.WithTenantId("<<Tenant_Id>>")
.WithClientSecret("<<Client_Secret>>")
.Build();
ClientCredentialProvider authenticationProvider = new ClientCredentialProvider(confidentialClientApplication);
GraphServiceClient graphClient = new GraphServiceClient(authenticationProvider);
var onlineMeeting = new OnlineMeeting
{
StartDateTime = DateTimeOffset.Parse("2020-01-15T21:30:34.2444915+05:30"),
EndDateTime = DateTimeOffset.Parse("2020-01-15T22:00:34.2464912+05:30"),
Subject = "User Token Meeting"
};
var meeting = graphClient.Me.OnlineMeetings
.Request()
.AddAsync(onlineMeeting).Result;
but it was throwing
Code: Forbidden
Inner error:
AdditionalData:
request-id: <<some_id>>
date: 2020-07-09T16:42:23
ClientRequestId: <<some_id>>
I been working on your question in few days, I was going to mention some of the suggestions comes with the other answer. But in addition, the main challenge here, the system need to know who is authorized to do what.
So IMO The Best choice to solve this is creating a guest login in AzureAD, than you can use that to create a Team Events. Further more you can added an extra step after guest user logon, so that guest should enter his/her name and use it as reference.
You will need to take these two steps.
Get the right token
Create an event (but change the url to
https://graph.microsoft.com/v1.0/users/the_user#domain.com/events)
The hard part is getting the right token, you have multiple options.
Use the client credentials flow this will force an admin from every new tenant to authorize your application for their organization. You can then use the tenant from the user info to request a token for that tenant and use the user id to create the right url to post to.
Make IdentityServer save the access token and allow you to access it. At coonfiguration level you have access to token callback and there you can also save the Azure AD access token. I think you can add it to a reference token, that way it isn't transmitted everytime but your web application is still able to access it.
Use the on-behalf-of flow, this would require you to pass the Azure AD access token token retrieved from azure AD by the IdentityServer to be passed to your application.
Just remove the identity server from the flow and have your web application logging straight with Azure AD. That way you'll have the right token available all the time.
Edit
After reading your editted question, what you want is a website where the user doesn't have to be an member of your Azure AD, just wants access to some new online meeting?
Best option is to created a shared mailbox, authorize an application (with Calendar.ReadWrite). Get a token with client credentials and call Create Event and then extract the meeting url from the event (that you'll get back when the posts completes succesfully.
To create an online meeting for the "Client Credentials" flow, I used the following:
var confidentialClientApplication = ConfidentialClientApplicationBuilder
.Create("my-app-client-id")
.WithTenantId("my-aad-tenant-id")
.WithClientSecret("my-client-secret")
.Build();
var authProvider = new ClientCredentialProvider(confidentialClientApplication);
var graphClient = new GraphServiceClient(authProvider);
var meetingGuid = Guid.NewGuid();
var createMeetingResponse = await graphClient.Users["my-aad-user-object-id"].OnlineMeetings
.CreateOrGet(meetingGuid.ToString())
.Request()
.PostAsync();
The issue with your code is that referencing graphClient.Me causes the Graph requests to go to https://graph.microsoft.com/v1.0/me/onlineMeetings, which is not what you want in the "Client Credentials" flow. See this screenshot from the documentation found here: https://learn.microsoft.com/en-us/graph/api/onlinemeeting-createorget?view=graph-rest-1.0&tabs=csharp
I had to grant "application" permissions in Azure Portal to allow my app to access the online meetings API, and I had to create a client secret. I also had to follow this article to create a policy and grant it to specific users using Microsoft Teams PowerShell:
https://learn.microsoft.com/en-us/graph/cloud-communication-online-meeting-application-access-policy
For users not in your organization, you can invite them as a guest user to your tenant.
I had issues using the Microsoft Teams Powershell commands due to settings in Windows Remote Management, which I did something like this to work around:
https://lonesysadmin.net/2017/08/10/fix-winrm-client-issues/

User information from Microsoft Graph

Background information.
I have an application (MS Teams Bot) that I have hosted in Microsoft Azure as an "App Service". This application. This application has its own application Id. In its "Settings" I have amended the "Authentication/Authorization" to be enabled as such,
So I have created a new Azure Active Directory Application within my existing app service in Azure. Now this application has its own application ID and key. The Multi-tenant feature has been enabled on this.
This application has the following permissions,
Application permissions.
Delegated permissions.
Problem.
I have 2 unrelated environments in Azure. One (we can call this Azure A), which is for hosting test applications and the other (Azure B) is for hosting live applications and is host to our live active directory. My bot application is in Azure A but when a user from Azure B uses the bot, the bot tries to authenticate the user against the active directory it came from, it is unable to do so. My bot application gains an access token to run against the api "https://graph.microsoft.com", which it successfully acquires.
Here I have a class, which acquires an access token for my bot application to run against,
class AzureAuthenticationProvider : IAuthenticationProvider
{
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
string clientId = "client-id"; // azure ad app id
string clientSecret = "client-secret"; // azure ad app secret
string authority = "https://login.microsoftonline.com/tenant-id"; // Authentication URI. tenant-id taken from Azure B
AuthenticationContext authContext = new AuthenticationContext(authority);
ClientCredential creds = new ClientCredential(clientId, clientSecret);
AuthenticationResult authResult = await authContext.AcquireTokenAsync("https://graph.microsoft.com/", creds);
request.Headers.Add("Authorization", "Bearer " + authResult.AccessToken);
}
}
I then run the following to try and access user information from Azure B,
GraphServiceClient client = new GraphServiceClient(new AzureAuthenticationProvider());
string userId = user object id from Azure B;
User user = await client.Users[userId].Request().GetAsync();
I then receive the following error,
Microsoft.Graph.ServiceException: Code: Authorization_IdentityNotFound Message: The identity of the calling application could not be established.
So my questions are,
i) Should the client id and secret used in AuthenticateRequestAsync() be of the Azure Active Directory application or should this be taken from my bot application? These 2 are run in the same app service in Azure A.
ii) If the Azure Active Directory application has multi-tenant enabled for it, will it be able to authenticate against users from an active directory from outside of my domain?
iii) I suspect the authentication URI has to contain the tenant id of where the user is from (Azure B) and not the tenant id of where the application is hosted (Azure A), is this correct?
From the AAD registration:
clientid = Application ID of AAD Application.
clientSecret = the generated key of the AAD Application .
Yes, this is one of the use cases of the multi-tenanted solution.
In this instance, you want to use the /common endpoint(https://login.microsoftonline.com/common). This will authenticate the user against the tenant they live in.
If it were a single-tenant application, you would use /{tenant-id} in place of /common. Also, note the /common endpoint isn't supported with the client_credential OAuth flow.

Microsoft Graph Api token invalid or not authorized?

I'm trying to read the user's data from Azure Active Directory via Microsofts' Graph API. Using the Graph Explorer I'm able to get all users but using a stand alone application I end up with an "unauthorized" response after receiving a token. I'm clearly missing some steps but it isn't obvious to me what steps that would be. Any insight would be appreciated
The code below is based off a MSFT sample:
// config values
// authority = "https://login.microsoftonline.com/{ TENANT ID }/oauth2/"
// resource uri = "https:// APP NAME .azurewebsites.net";
// graph uri = https://graph.windows.net/TENANT ID/ also tried https://graph.windows.net/v1.0/
// short form
public async void GetUsers( ADConfiguration config )
{
_authContext = new AuthenticationContext(config.GetAuthority());
_clientCredential = new ClientCredential(config.ClientId, config.ClientSecret);
AuthenticationResult result = null;
// obtain the token, this part is still successful
result = await _authContext.AcquireTokenAsync(config.ResourceUri, _clientCredential );
_httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
string address = config.GetGraphiUri() + "users?api-version=1.6";
// this response is always unauthorized
HttpResponseMessage response = await _httpClient.GetAsync(address);
}
In addition to answer your new problem . From you code , you are acquiring token using client credential flow. In the client credentials flow, permissions are granted directly to the application itself.
Since you are using Azure AD Graph API , you need to add application permission :
In the Azure portal, choose your application, click on Settings
In the Settings menu, choose the Required permissions section ,select Windows Azure Active Directory(Azure ad graph api) , add related application permissions your app requires .
Inside your app's blade, hit Grant Permissions to do admin consent with your admin's credential .
Your config values seem off:
Authority should be: https://login.microsoftonline.com/{TENANT ID}.
It seems to me that you are trying to use Azure AD Graph API, not Microsoft Graph API.
In that case:
Resource URI should be: https://graph.windows.net/. (MS Graph API is https://graph.microsoft.com/)

Connection Issues when using AcquireTokenAsync() in Xamarin Forms Windows App with Azure AD B2C and custom policy

I am using MSAL to connect my Xamarin.Forms-Windows 8.1-App to Azure AD B2C with a custom SignInSignUp-policy (to login the user by using a custom E-Mail address and a password). The "normal" authentication against Azure AD (without custom policy) works fine, but when I use the policy, the message "We can't connect to the service you need right now. Check your network connection or try this again later." appears everytime after providing the credentials (directly within the dialog). Since the dialog does not finish correctly, I am not retrieving any exception or AuthenticationResult and so I am not able to determine the concrete issue (the internet connection itself is not the problem).
Note: I also tried this in a Xamarin.Forms-UWP-App which leads me to the same issue. Under iOS, it is working fine and the dialog finishes and closes correctly.
NuGet Versions:
MSAL (Microsoft.Identity.Client): 1.0.304142201-alpha
Xamarin.Forms: 2.3.2.118-pre1
Thats my code (simplified):
var clientId = "{My application's ClientId}";
var redirectUri = "urn:ietf:wg:oauth:2.0:oob";
var authority = "https://login.microsoftonline.com/{My Azure AD B2C}";
string[] scopes = { clientId };
var policy = "{My SignUpSignIn policy}";
var clientApplication = new PublicClientApplication (authority, clientId);
clientApplication.RedirectUri = redirectUri;
var result = await this.clientApplication.AcquireTokenAsync(scopes, string.Empty, UiOptions.SelectAccount, string.Empty, null, authority, policy);
Any ideas on this?
Thanks in advance!
Regards,
Marcel
I had the same issue. Turns out I forgot to properly set up my mobile app to allow loggin in via Active Directory
The steps I took that solved my problem were:
In Azure B2C Blade select Application and set the Reply URL to the address of the Azure Mobile Apps instance (e.g. https://myapp.azurewebsites.net), followed by /.auth/login/aad/callback
Make sure the policies used in the Azure Active Directory B2C tenant should be configured so that the Reply URL is set to the same as above
In the Azure Mobile or Services App go to settings-->authentication/autorization and make sure that a non authenticated request is set up to log in with AAD
in the same blade select the AAD provider, go to advanced and set up Client ID and Issuer URL, with the Client ID being the Application ID of the Azure Active Directory B2C tenant, and the Issuer Url being the Metadata Endpoint for the Azure Active Directory B2C policy
Once I did the above all worked flawlessly.
More detailed instructions can be found at https://developer.xamarin.com/guides/xamarin-forms/web-services/authentication/azure-ad-b2c-mobile-app/
Let me know if this helped...

Categories

Resources