Reset Password REST call returns 403 using service principal - azure-active-directory

I am trying to execute the following API using a bearer token issued to a service principal NOT an AD User. Yes I know this is the AD Graph versus Microsoft Graph endpoint, but I have my reasons :)
https://graph.windows.net/GUID-REDACTED/users/GUID-REDACTED?api-version=1.6
I get a 403 error despite the fact that I have granted all Application Permissions for "Windows Azure Active Directory" (and Microsoft Graph) to that principal. I also applied admin consent (via Grant Permissions) in the portal. Note that a request to read all users (i.e., remove last GUID off URL above) DOES succeed.
The bearer token contains the following seven claims (curiously in AD, EIGHT permissions are granted):
"Device.ReadWrite.All",
"Directory.Read.All",
"Member.Read.Hidden",
"Directory.ReadWrite.All",
"Domain.ReadWrite.All",
"Application.ReadWrite.OwnedBy",
"Application.ReadWrite.All"
The token was acquired via:
var context = new AuthenticationContext($"https://login.microsoftonline.com/{tenantId}");
var m = new HttpRequestMessage()
var accessToken = context.AcquireTokenAsync("https://graph.windows.net", credentials).Result.AccessToken;
m.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
I have tried the analog of this via the Microsoft Graph endpoint, but with the ADAL AuthenticationContext and get the same 403 result. If I use the Microsoft Graph Explorer though, it works. In that case I am logged in as a user though. Upon comparing the scopes in the tokens (scp) there are differences (because the user has certain 'user' scopes), but nothing that immediately looks suspicious.
Directory.AccessAsUser.All is on the user scope but not the application identity scope, but that makes sense to me, unless that scope is (incorrectly?) required for the operation I am trying.
Any ideas what I am missing here? Is there a reference that maps the scopes/roles required to the actual API operations? Does the SP need a directory role, like a user would need?

As #MarcLaFleur said, it's not a good idea to give an application permissions to reset users' password. But if you don't have other choices you can still use client credentials flow to achieve this. But this is not recommeded unless you don't have other choices.
Solution:
You can assign Company Administrators Role to your Service principal. You can refer to this document to do that.
Use AAD Powershell to Connect AAD:
Connect-AzureAD
Get the Role of Company Administrator:
$role = Get-AzureADDirectoryRole | Where-Object {$_.displayName -eq 'Company Administrator'}
Assign the role to your SP:
Add-AzureADDirectoryRoleMember -ObjectId $role.ObjectId -RefObjectId $yoursp.ObjectId

Related

Microsoft OAuth authorization for specific user roles

I have to limit which users can access an Azure App. For now, only Global Admins can access using this link:
login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=client_id_goes_here&scope=user.read.all&response_type=code&redirect_uri=https://myredirectbacklink.com/aad/auth&response_mode=query&state=portal&prompt=consent
After redirect back I get Token using
https://login.microsoftonline.com/common/oauth2/v2.0/token and the query contains the same scope as the authorized link.
The flow of the app is the same as documented in Microsoft identity platform and OAuth 2.0 authorization code flow.
My problem is that client wants to allow Billing Admins to access their app. I went through all Graph Permission Scopes, but could not find any related to Billing Admin.
My knowledge of Microsoft's authorization is somewhat limited. I do know that limitations are done by scope. But if it's just link change or is it in Authentication App in Azure (there is one but I don't know how it's related to actual login permissions).
Should I look in a different direction or is it just simply changing the link? Microsoft's documentation didn't help a lot because it's mostly about calendars and other simple stuff.
Careful, requesting a permission that normally requires admin consent and causing the user consent prompt is not the proper way to ensure the user signing in is actually an admin. A non-admin user could simply modify the URL to scope=User.ReadBasic.All and remove prompt=consent. If the user is allowed to consent for User.ReadBasic.All (which is true in many organizations), they'd be able to continue the sign-in. (Or if the organization had granted admin consent for "User.Read.All", the non-admin user would only need to remove prompt=consent.)
If you need to ensure the user is an administrator, you need to explicitly check for directory role assignments.
You can choose from one of three different ways to do this:
You can configure your app to receive the wids claim, which will include the role template IDs of the directory roles for which the user has an active assignment. This is probably the simplest approach.
Using the Azure portal, you can do this under App registrations > (choose your app) > Token configuration > + Add groups claim. You must include "Directory roles" in your selection:
Another option is to a Microsoft Graph API request to check which of a given list of directory roles the user has been assigned:
POST https://graph.microsoft.com/v1.0/me/checkMemberObjects
Content-type: application/json
{
"ids": [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
}
A third option is to make a Microsoft Graph API request to list the directory role assignments granted to the user:
GET https://graph.microsoft.com/beta/roleManagement/directory/roleAssignments
?$filter=principalId eq '{id}'
All three of these approaches involve using directory role template IDs to identify the directory role you are checking for. They're all listed here: https://learn.microsoft.com/azure/active-directory/roles/permissions-reference
Some examples you may be interested in:
Application administrator: 9b895d92-2cd3-44c7-9d02-a6ac2d5ea5c3
Cloud application administrator: 158c047a-c907-4556-b7ef-446551a6b5f7
Global administrator: fdd7a751-b60b-444a-984c-02652fe8fa1c
Privileged role administrator: e8611ab8-c189-46e8-94e1-60213ab1f814
Billing administrator: b0f54661-2d74-4c50-afa3-1ec803f12efe
(I included the first four because those are the directory roles which would, by default, be allowed to grant consent for User.Read.All.)
If someone needs solution and uses php:
You can use https://github.com/microsoftgraph/msgraph-sdk-php
$accessToken = 'token from redirect back, called access_token';
$body = [
"ids" => [
"fdd7a751-b60b-444a-984c-02652fe8fa1c",
"b0f54661-2d74-4c50-afa3-1ec803f12efe"
]
];
$graph = new Graph();
$graph->setAccessToken($accessToken);
$user = $graph->createRequest("post", "/me/checkMemberObjects")
->attachBody($body)
->execute();

Not able to get access_token for Microsoft Graph API OAuth 2.0 using username & password

I am trying to get access_token using
POST https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token
endpoint using username and password where tenant = {some tenant id}
The parameters that I am using to make the request are:
client_id:{client_id}
scope:https://graph.microsoft.com/Calendars.ReadWrite
client_secret:{client_secret}
username:{username}
password:{password}
grant_type:password
I am getting the following error in response:
error: invalid_request
error_description : AADSTS90002: Tenant '' not found. This may happen if there are no active subscriptions for the tenant. Check with your subscription administrator.
I have the following permissions for my application available on Azure:
The documentation for this is available here : https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth-ropc
On my side, it works. You should check your tenant whether your application is registered in this tenant.
I solved this issue by purchasing a subscription and adding my user as an administrator.
After that, I added two permissions to my application to get the delegate access for using ROPC (Resource Owner Password Credential)authentication method and granted them with the administrator consent.

Does what you send in Scope Governs whether you can login with Microsoft Account using Azure AD V2 Endpoints

I have registered a application using the App Registration (Preview) Blade and added the Azure Service Management API as API Permissions I downloaded the MSAL based Sample from
https://github.com/azure-samples/active-directory-dotnet-webapp-openidconnect-v2
Now in startup.auth.cs if i change the Scope i.e keep openid and add https://management.azure.com and then run and try and Login with a Microsoft Account i get the following error
This Doesn't Look like a Work or School Email you cant Sign-in here with Personal Account use your work or School Account Instead.
if i remove the Scope for https://managment.azure.com and just keep Openid profile offline_access i get the Consent Screen and Login
new OpenIdConnectAuthenticationOptions
{
// The Authority represents the v2.0 endpoint - https://login.microsoftonline.com/common/v2.0
// The Scope describes the initial permissions that your app will need. See https://azure.microsoft.com/documentation/articles/active-directory-v2-scopes/
ClientId = clientId,
Authority = String.Format(CultureInfo.InvariantCulture, aadInstance, "common", "/v2.0"),
RedirectUri = redirectUri,
Scope = "openid https://management.azure.com/.default",
PostLogoutRedirectUri = redirectUri,
I am Expecting to have the user Login and Obtain a Token for management API , i am Looking for Reasons for getting the above Error is this Expected ? The Account that i am using exists in my directory as a Member . this works if i use a Managed user(user#tenant.onmicrosoft.com) to Login
Since personal MS accounts cannot be used to manage Azure subscriptions unless they are added to an Azure AD, you should use the organizations endpoint instead of common.
In v1 "common" meant any AAD tenant.
In v2 "common" means any AAD tenant + any personal MS account.
If you wanted only personal accounts, you can use "consumers".
https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-protocols-oidc#fetch-the-openid-connect-metadata-document
You can use "organizations" to allow any AAD tenant but disallow MS accounts.
Of course a user can just edit the URL and login with a personal MS account anyway, so you'll want to check the tenant id of the logged in user.
The tenant id for MS accounts is always 9188040d-6c67-4c5b-b112-36a304b66dad, per the docs: https://learn.microsoft.com/en-us/azure/active-directory/develop/id-tokens#payload-claims.
So check the idp claim.

Connecting to exchange online using Microsoft Graph APIs through a Demon application

I'm trying to connect to exchange online and do certain operations with the emails using Microsoft Graph API 1.0 and this is all done in a demon program. I'm using Client Credential workflow for authentication, below is the small piece of code
AuthenticationContext authenticationContext = new AuthenticationContext(string.Format(CultureInfo.InvariantCulture, azureEndPoint, tenant));
ClientCredential clientCredential = new ClientCredential(clientId, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resource, clientCredential);
But for this code to return the authentication token I have to get Application Permissions to the azure app id against microsoft graph api. The caveat here is if the permission is granted, the application id will have access to read emails of all users in the organisation and due to this reason tenant admin has strictly refused to grant the permission.
I tried my luck with consent framework but that requires user intervention to enter his/her id and password which is not possible in case of a demon program. I read few blogs like below but they all end up entering the user id password to get to the redirect url which defeats the whole demon thing https://blogs.msdn.microsoft.com/exchangedev/2015/01/21/building-daemon-or-service-apps-with-office-365-mail-calendar-and-contacts-apis-oauth2-client-credential-flow/
Is there any way I can give read/write access to azure application id for specific email ids in the tenant? Or alternatively any smart way to somehow get to the mailbox without user intervention?
Thanks in advance,
Vivek
You can only use app permissions with client credential grant flow.
To access only specific users' emails, you'd have to do a different approach.
This does require each user to consent individually.
Have the users login to your app, require consent for access to their email.
Upon returning to your app, acquire a refresh token and store it securely.
A refresh token is user-specific.
Then in your daemon service you acquire an access token for each user using their refresh token.
If the acquire fails because the refresh token has been invalidated,
the user will need to be notified to login again.
This is now resolved as microsoft has introduced a new concept of limiting application permissions to specific mailboxes or set of mailboxes using Group Policies. Check here https://learn.microsoft.com/en-us/auth-limit-mailbox-access

How to programatically create applications in Azure AD

I'm currently creating my applications on Azure Active directory manually whenever there is a request for a new environment. I was exploring ways to create these applications from the code via REST API. I had success in creating users and groups on existing applications by using 'client_credentials' as shown.
ClientCredential clientCred = new ClientCredential(clientID, clientSecret);
AuthenticationResult authenticationResult = await authenticationContext.AcquireTokenAsync(resAzureGraphAPI, clientCred);
In similar fashion I tried to use the 'access_token' generated from above to create a new application
adClient.Applications.AddApplicationAsync(newApplication).Wait()
But this throws an error- "Insufficient privileges to complete the operation."
I looked at other threads and the Azure AD msdn page and turns out the client_credentials flow does not support creating/updating applications.
Adding Applications programmatically in Azure AD using Client Credentials Flow
The above thread also mentioned that way to workaround it was by using the 'grant_type=password' flow. I tried it as suggested but I keep getting the following error which doesn't make sense to me.
"error": "invalid_grant",
"error_description": "AADSTS50034: To sign into this application the account must be added to the 1283y812-2u3u-u293u91-u293u1 directory.\r\nTrace ID: 66da9cf9-603f-4f4e-817a-cd4774619631\r\nCorrelation ID: 7990c26f-b8ef-4054-9c0b-a346aa7b5035\r\nTimestamp: 2016-02-21 23:36:52Z",
"error_codes": [
50034
],
Here is the payload and the endpoint that I'm hitting. The user that is passed is the owner of the AD where I want to create the application
endpoint:https://login.windows.net/mytenantID/oauth2/token
post data
resource 00000002-0000-0000-c000-000000000000
client_id id
client_secret secret
grant_type password
username principal#mydomain.com
password password
scope openid
Any thoughts or suggestions of where I might be going wrong would be appreciated.
You can use PowerShell to create your apps:
$servicePrincipalName =”Your Client App Name”
$sp = New-MsolServicePrincipal -ServicePrincipalNames $servicePrincipalName -DisplayName $servicePrincipalName -AppPrincipalId “Your Client ID"
New-MsolServicePrincipalCredential -ObjectId $sp.ObjectId -Type Password -Value “Your client secret”
Add-MsolRoleMember -RoleObjectId “62e90394-69f5-4237-9190-012177145e10" -RoleMemberType ServicePrincipal -RoleMemberObjectId $sp.ObjectId
The role denoted by 62e90394-69f5-4237-9190-012177145e10 is the Admin role, and this can be adjusted as required to the ObjectId of any other role. Run Get-MsolRole to get a list of roles and ObjectIds.
You could then run this code from your App or run it manually. You will also need to run your connection code before the above, something along the lines of:
$loginAsUserName = "Your Tenancy Admin Account"
$loginAsPassword = "Your Tenancy Admin Account Password"
$secpasswd = ConvertTo-SecureString $loginAsPassword -AsPlainText -Force
$creds = New-Object System.Management.Automation.PSCredential ($loginAsUserName, $secpasswd)
Connect-MsolService -Credential $creds
I was able to create the application in my tenant. The AD tenant which I was using to create the application under was verified for a different domain. Basically I ended up plugging in an user from that domain and using the resource_type=password flow was able to generate an access token. Next, firing the following lines of code did the trick
ActiveDirectoryClient adClient = new ActiveDirectoryClient(
serviceRoot,
AccessToken);
adClient.Applications.AddApplicationAsync(newApplication).Wait();
Check the following things which seem to be a little off in your POST to the OAuth Token endpoint:
When wanting access to the Graph API of your Azure AD, you will need to pass https://graph.windows.net as the resource body parameter; this is (imho) not well documented, but that's what you need to do
As client_id and client_secret you need to pass the Client ID and the Key of a predefined Application inside your Azure AD which in turn you have granted permissions on a per user level to; these need to be sufficient to add applications
See here: https://msdn.microsoft.com/Library/Azure/Ad/Graph/howto/azure-ad-graph-api-permission-scopes?f=255&MSPPError=-2147217396
The scope parameter is not used, I think; you will get the claims you defined inside the Azure AD management portal back (the assigned permissions for your application)
This should render you an access token you can then subsequently use on the https://graph.windows.net/tenantId/ end points.

Resources