Microsoft Graph - Get MemberOf - c#

I'm trying to access the groups a user is a member of using the Microsoft Graph API.
I'm facing an issue because I think my permissions are set correctly, however, when I sign into the app, I get the message :
AADSTS90093: Calling principal cannot consent due to lack of permissions.
The weird thing is that I'm only asking for this permission scope :
public static String[] ClientScope = { "User.Read", "User.ReadBasic.All", "Group.Read.All"};
What makes me confused is that if I sign in the Microsoft Graph explorer and go to https://graph.microsoft.com/v1.0/me/memberOf ,
I get the correct results.
I got an admin to consent to the permissions I'm asking in the scope of course.
Has anyone encountered that issue ?
Any idea how I should correct that ?

The memberOf API requires one of the following scopes:
Directory.Read.All
Directory.ReadWrite.All
Directory.AccessAsUser.All
Regardless of which of these scopes you choose, they all will require administrative consent before a regular user can authorize them. To do this, you'll first need to have them go through the “Admin Consent” workflow. This workflow requires an administrator but once complete any users of your application will have “admin consent” to the restricted scope.
For example, you would normally you would then authenticate users by redirecting them to
https://login.microsoftonline.com/common/oauth2/authorize?<your params>.
Since this scope requires an Admin however, you fist need to obtain consent by first having an Admin authenticate by redirecting them to
https://login.microsoftonline.com/common/adminconsent?<yours params>.
Once an Admin grants consent, normal users will be able to use OAUTH to authenticate.

Related

AAD client-credentials flow request scope for web api

I got a console app that needs to support two flows, authenticating against AAD to talk to a web api:
for regular usage by humans, it needs to support interactive login
for usage by a CI/CD pipeline it needs to support client-credentials.
The interactive flow works perfectly, but the client-credentials flow is giving me problems with the requested scope.
I'm using the latest Microsoft.Identity* nuget packages.
When I construct the scope for the webapi like I do for the interactive flow, I get an error message telling me that for client-credentials flows I need to append ./default. Okay, fair enough, I also found documentation for this, so I append ./default. But when I do that, I get another error message telling me
The resource principal named api:///access_as_user was not found in the tenant named .
There are two problems with this error message:
the resource principal quoted definitely exists - also, I couldn't login interactively if it didn't, but as mentioned, interactive login works just fine
it says api://<webApiAppId>/access_as_user, not api://<webApiAppId>/access_as_user/.default, despite my appending that
My next though was: well, maybe the problem is that the app registration used for the client-credentials flow doesn't have permissions on the web-api. But it does.
So now I've run out of ideas. Hopefully, someone here can help.
To make everything a bit clearer, let me list the app regs involved:
A. Web Api
Was setup via the VS Wizard/dotnet-msidentity tool
has a few App Roles defined
exposes a single API api://<itsownAppId>/access_as_user
B. Interactive Login
manually created
redirect URI for localhost
API Permissions: added WebApi | access_as_user as delegated
C. Non-interactive login/Service Principal
setup manually
is used also for other things by the CI/CD pipeline
has a ClientSecret defined
API Permissions: added WebApi | access_as_user with 2 of the app roles defined for A
has other API Permissions that have nothing to do with this here (for Graph)
granted admin consent for all permissions
The code I use to authenticate is (for the confidential flow):
ConfidentialClientApplicationBuilder.Create(_configuration.ApplicationId)
.WithTenantId(_configuration.Directory)
.WithLogging(Log, LogLevel.Error)
.WithClientSecret(_configuration.ClientSecret)
.Build()
.AcquireTokenForClient(_configuration.Scopes)
.ExecuteAsync();
where the values of _configuration are:
ApplicationId: the appId from app registration C
Directory: the name of my AAD tenant
ClientSecret: the secret from app registration C
Scopes: array of openid, profile and api://<appIdOfWebApiFromC>/access_as_user/.default
So, it turns out that the documentation for appending ./default is not quite clear enough:.You are not meant to append it to the scope, just to the "resource id". And with resource id they mean the "api://" parts without the name of the permission.
So where you normally request api://<webApiAppId>/access_as_user, for the client-credentials flow you have to request api://<webApiAppId>/.default

"Either scp or roles claim need to be present in the token" when using app permissions to access my OneDrive

I have a web application that allows me to sign in to my OneDrive account using delegated permissions to authorize the app to browse my drive files on my behalf. I'm now trying to build a server-side job that needs to work with these files and therefore needs application permissions granted with admin consent.
I've followed various instructions to achieve this, but no matter what I do I keep getting this 403 error:
Either scp or roles claim need to be present in the token
The application I've registered in Azure portal has the application permission Files.Read.All and I've granted admin consent. I'm obtaining my access token as follows:
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create("<client-id>")
.WithClientSecret("<secret>")
.WithAuthority(new Uri("https://login.microsoftonline.com/common"))
var apiUrl = "https://graph.microsoft.com/";
string[] scopes = { $"{apiUrl}.default" };
result = await app.AcquireTokenForClient(scopes).ExecuteAsync();
I'm then using the returned token to request "{apiUrl}v1.0/drives/<my-drive-id>/items/<drive-item-id>"
And this is where I get the access denied. Clearly the API expects my token to have either a roles claim or an scp claim.
After reading this related post on SO I did wonder if the problem is related to the fact I'm using the same app registration for the front end and back end operations (so it has a mix of delegated and application permissions) but I tried creating a new app registration with only the application permissions and it was the same error.
Also, that post suggests I should expect to see either and scp or a roles claim in my token (depending on choice of auth flow) but I get neither of these claims. That led me to this other SO post which suggests I need to explicitly include roles in my access token, but roles isn't listed as an optional claim in the Token configuration blade of the app registration.
So I'm stuck. Can anyone help?
UPDATE 1
I've tried constructing the auth request manually now using a POST to https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token and a form body that includes grant_type=client_credentials and now I'm finding the roles claim is returned in the token. So I guess the ConfidentialClientApplicationBuilder wasn't building a client credentials auth request as I had assumed. However, when I use this token to make the above Graph API request for a drive item I get the error:
Tenant does not have a SPO license
When I've encountered this before I've been told to use common instead of my tenant ID. However, when I make that change in this case I'm again left with a token that doesn't have a roles claim.
The absence of the roles claim indicates that you app (or service) hasn't been granted any application permissions (i.e. app roles) for the API (in this case, Microsoft Graph), in the tenant where the token request is being made.
You need to ensure the the application permissions (app roles) you expect the app to use to make the API call have been granted in the tenant where you are making the API call.

Web application requiring admin consent during token retrieval

My goal is to bypass the login screen and use Azure AD as the Identity Provider.
Given I am already logged in with my Azure AD user, I'd like to retrieve the authorization token using the silent flow and use this for resources that are protected.
I have a web application running on ASP.NET MVC 5 and a user-managed and backed by Azure AD (i.e. federated user). As a starting point, I have followed the steps in this article: Integrated Windows Authentication.
If I understand correctly, I should be able to use silent authentication since my users are federated and my application is registered as a public application.
In Azure AD, my app is registered with the following properties:
The code is straightforward, from the url.
var app = PublicClientApplicationBuilder.CreateWithApplicationOptions(
new PublicClientApplicationOptions()
{
ClientId = "<clientId>",
TenantId = "<tenantId>",
LogLevel = LogLevel.Verbose,
AzureCloudInstance = AzureCloudInstance.AzurePublic,
})
.Build();
var scopes = new [] { "User.Read" };
var result = await app.AcquireTokenByIntegratedWindowsAuth(scopes)
.WithUsername("<username>")
.ExecuteAsync();
The call for acquiring the token throws the following exception:
"AADSTS65001: The user or administrator has not consented to use the application with ID 'xxx' named 'xxx'. Send an interactive authorization request for this user and resource"
Per the documentation, User.Read doesn't need Admin Consent.
So what am I doing wrong here?
EDIT:
I have constructed a URL that prompts for user consent: https://login.microsoftonline.com/tenantId/oauth2/authorize?client_id=clientId&response_type=code&redirect_uri=<myApp>&nonce=1234&resource=User.Read&prompt=consent
It takes me to the screen where I need to pick my account, and after that, I get redirected to my app, where I get the same exception again.
So it doesn't show any consent screen, just asking me to pick the Microsoft account I want to use. Is this because of consent for User.Read is already granted by the admin?
But why am I still receiving the error? I'm a bit confused at this point.
You're mixing two different AAD OAuth mechanisms (aka v1 and v2 endpoints). The v1 endpoint uses Resources (https://graph.microsoft.com) while the v2 endpoint use Scopes (user.read). So when you request resource=User.Read, you are passing it an invalid resource name.
I would recommend using the v2 endpoint with the following URI prototype (line breaks for readability):
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id={clientId}
&response_type=id_token
&redirect_uri={your_app}
&response_mode=fragment
&scope=user.read
Because Integrated Windows Authentication is a silent flow, the user of your application must have previously consented to use the application or the tenant admin must have previously consented to all users in the tenant to use the application.
You can force user consent through a URL request with prompt=consent, the url will look like:
https://login.microsoftonline.com/<tenant-id>/oauth2/authorize?client_id=<client id>&response_type=code&redirect_uri=<Your-Redirect-URI-Https-Encoded>&nonce=1234&resource=<your-resource-Https-encoded>&prompt=consent
If it needs admin consent, use $prompt=admin_consent instead.(need to use admin account to sign in)
For more details about fix error AADSTS65001, you could refer to this article.
With delegated permissions, every user will need to consent to the application. If you do not want that requirement, a tenant administrator can consent for all users from the Azure Portal.

Create user using Microsoft Graph

How do I create a user using Microsoft graph? For I am having issues with regards to permission failures during a save.
I do have few questions in mind.
Where will the user be created by calling create user API in graph ? Is it in Azure AD or somewhere else ?
I tried calling create user api by passing json and required headers, below is the error I get
Where exactly do I need to set the permission, I have already added permissions in the Application Registration Portal
But when API is executed it shows that I don't have enough permission.
FYI, I have registered the app using the same email id that I am using to test the APIs here https://developer.microsoft.com/en-us/graph/graph-explorer#
If I am not the admin, where exactly do I need to set or request for it ?
In order to create a User via Microsoft Graph, you need to request either Directory.ReadWrite.All or Directory.AccessAsUser.All permission.
Important: Directory.ReadWrite.All and Directory.AccessAsUser.All both require Admin Consent before you can use them. If you're using Graph Explorer then the URI you need to provide your tenant Admin will be generated for you. If you're doing this in your own application, you'll need to construct an Admin Consent URI yourself. You can find more details on this at v2 Endpoint & Admin Consent.
Once you have the proper permissions configured (and consented), you'll want to POST the following JSON body/payload to https://graph.microsoft.com/v1.0/users:
{
"accountEnabled": true,
"displayName": "displayName-value",
"mailNickname": "mailNickname-value",
"userPrincipalName": "upn-value#tenant-name.onmicrosoft.com",
"passwordProfile" : {
"forceChangePasswordNextSignIn": true,
"password": "password-value"
}
}
This will create a user with a temporary password. The user will be asked to set a new password as after as they authenticate for the first time.
Where will the user be created by calling create user API in graph ?
Is it in Azure AD or somewhere else ?
Yes, the user created is in the Azure AD.
I tried calling create user api by passing json and required headers,
below is the error I get
For your error, have you added the request body like the following, and this required admin:
Where exactly do I need to set the permission, I have already added
permissions in the Application Registration Portal
The required permissions to create application:
For the details, please read here.

invalid_grant - type = password: user or admin has not consented to use the application

I am getting a consent error when trying to obtain a token. Because of our application, we can't show an interactive dialog to give consent.
"AADSTS65001: The user or administrator has not consented to use the
application with ID <'my native client app id'>. Send an
interactive authorization request for this user and resource.
AuthenticationContext ctx = new AuthenticationContext(
string.Format("https://login.microsoftonline.com/{0}","mytenant.onmicrosoft.com"));
UserPasswordCredential cred = new UserPasswordCredential("login#mytenant.onmicrosoft.com", "Password");
var result = ctx.AcquireTokenAsync("my api uri", "my native client id", cred);
We are using the grant_type=password and client_id is a Native app id, and resource is the Web API app URI.
Permissions-wise, from the client app, a delegated permission has been given to access the api app and have also tried setting oauth2AllowImplicitFlow : true in the manifest.
All applications have been created in the new preview Azure AD section of the new portal (portal.azure.com)
Unfortunately if your application needs access to certain resources like the Graph API, you will need to prompt for consent at least one time.
Even if your app doesn't have an interactive login experience, you should be able to prompt this once to unblock your scenario in your tenant.
Use the following URL:
https://login.microsoftonline.com/<TenantID>/oauth2/authorize?client_id=<AppID>&response_type=code&redirect_uri=<RedirectURI>&resource=<ResourceURI>&prompt=admin_consent
You can see here we have just simply generated the login URL which would be generated as part of an interactive login experience. You will need to fill out your own specific data like Reply URL, App ID, Resource URI, etc...
Note that we added a final query string at the end where we are forcing a "consent" prompt. This should be done by an Administrator, who would be able to consent on behalf of the whole tenant. Once you have done that, the username/password flow should start working for you.
Also, as an additional note, implicit grant flow has nothing to do with consent.
Please read this section in the OAuth 2 spec:
https://www.rfc-editor.org/rfc/rfc6749#section-1.3.2
You should only use this setting if you are creating a single-page application with something like JavaScript; Otherwise, there are significant security concerns with this setting on applications that should not have it.

Categories