Azure Active Directory Application Permission Change Delay - azure

I am using Azure Active Directory to give my application access to the Microsoft Graph API.
When I make permission changes (e.g., read/write access for various types of data) I am noticing a delay from when the changes are saved and when I am able to access the new data through the API. I do notice, however, that after some time my API calls start to work. My questions are
Is this expected behavior?
Is there documentation somewhere that explains what permissions are needed for each Microsoft Graph API request?
Note that I am requesting a new token after making each permission change, before making the relevant API request.

When you changed your scopes (if you use Azure to manage thoses Autorizations) you have to request new consent from your users. Be sure to be able to call "one time" the ADAL AcquireTocken method, with the PromptBehavior.Always parameter.
I think it will be enough to refresh your consents and make your new scopes availables.
Here is a macro code I use :
if (mustRefreshBecauseScopesHasChanged)
{
authResult = await authContext.AcquireTokenAsync(GraphResourceId, ClientId, AppRedirectURI, PromptBehavior.Always);
}
else
{
authResult = await authContext.AcquireTokenSilentAsync(GraphResourceId, ClientId);
if (authResult.Status != AuthenticationStatus.Success && authResult.Error == "failed_to_acquire_token_silently")
authResult = await authContext.AcquireTokenAsync(GraphResourceId, ClientId, AppRedirectURI, PromptBehavior.Auto);
}
if (authResult.Status != AuthenticationStatus.Success)
{
if (authResult.Error == "authentication_canceled")
{
// The user cancelled the sign-in, no need to display a message.
}
else
{
MessageDialog dialog = new MessageDialog(string.Format("If the error continues, please contact your administrator.\n\nError: {0}\n\n Error Description:\n\n{1}", authResult.Error, authResult.ErrorDescription), "Sorry, an error occurred while signing you in.");
await dialog.ShowAsync();
}
}
For the scopes permissions détails, you will find them here :
http://graph.microsoft.io/en-us/docs/authorization/permission_scopes

Related

Azure AD CustomEvents causing MsalUiRequiredException

using Microsoft.Identity.Web;
using Microsoft.Identity.Web.UI;
services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp()
.EnableTokenAcquisitionToCallDownstreamApi(new string[] { "a" })
.AddDistributedTokenCaches();
IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
If i remove OpenIdConnectOptions events, i dont see any error. Is this a bug ?
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Events = new OpenIdConnectEvents
{
OnRedirectToIdentityProvider = (context) =>
{
if (context.Request.Headers.ContainsKey("X-Forwarded-Host"))
{
context.ProtocolMessage.RedirectUri = "https://" + context.Request.Headers["X-Forwarded-Host"] + Configuration.GetSection("AzureAd").GetValue<String>("CallbackPath");
}
return Task.FromResult(0);
}
};
});
MsalUiRequiredException
Error Code: AADSTS65001: The user or administrator has not consented
to use the application with ID '{appId}' named '{appName}'. Send an
interactive authorization request for this user and resource.
Mitigation
Get user consent first. If you aren't using .NET Core (which doesn't have any Web UI), call (once only) AcquireTokeninteractive. If you are using .NET core or don't want to do an AcquireTokenInteractive, the user can navigate to a URL to give consent: https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id={clientId}&response_type=code&scope=user.read. to call AcquireTokenInteractive: app.AcquireTokenInteractive(scopes).WithAccount(account).WithClaims(ex.Claims).ExecuteAsync();
And also Try with enabling Multi-Factor Authentication on your tenant.
For more details refer this document

Permissions from Graph API seem to be empty

Another Microsoft Graph API question this time I'm curious about the result.
Why does this return a 200 and with nothing in the value object.
What I've tried:
Add different permissions in the Modify permissions tab
Test different accounts and other SharePoint environments ( I am global admin on those accounts and its no personal account but work account)
I've tested before with the query params such as select, filter and expand. So ive tried things like ?expand=all, expand=items and expand=children and a few more.
Use name or id in the sites/{site name or site id}
Usually I've solved all of my problems with repeating step 1 or 3 but now it seem to give me nothing. Since it's part of the docs im curious what I'm missing here
https://learn.microsoft.com/en-us/graph/api/site-list-permissions?view=graph-rest-1.0&tabs=http
What could be the missing piece here? :)
Edit:
I've tried to solve this issue in a c# mvc 5 app by doing the following code but it still returns the exact same result:
IConfidentialClientApplication app = MsalAppBuilder.BuildConfidentialClientApplication();
var account = await app.GetAccountAsync(ClaimsPrincipal.Current.GetAccountId());
string[] scopes = { "Sites.FullControl.All" };
AuthenticationResult result = null;
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/sites/{site_id_or_name}/permissions");
try
{
//Get acccess token before sending request
result = await app.AcquireTokenSilent(scopes, account).ExecuteAsync().ConfigureAwait(false);
if (result != null)
{
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", result.AccessToken);
//Request to get groups
HttpResponseMessage response = await client.SendAsync(request);
if (response.IsSuccessStatusCode)
{
ViewBag.Permissions = response.Content.ReadAsStringAsync().Result;
}
}
}
catch (Exception ex)
{
//Something went wrong
}
Any idea what is wrong here?
The GitHub project im using: https://github.com/Azure-Samples/ms-identity-aspnet-webapp-openidconnect just add a client id and secret from your app reg and you can copy my method above :)
The reason is very simple, because it does not support delegated permissions, so don't try to have a user login Graph Explorer for testing, because it uses delegated permissions by default.
You need to grant Sites.FullControl.All application permissions to the application in the Azure portal, and then use the client credential flow to obtain an access token. Then you can use postman to call that api.

How do I Call Microsoft Teams OnlineMeeting endpoints via Microsoft Graph API using a console app?

I have followed the code example given in the following link by Microsoft and was successfully able to get the list of users.
My registered app in the Azure Active Directory also have the "OnlineMeeting.ReadWrite.All" application permission.
But when I am trying to call the create meeting call by posting the request in the endpoint "https://graph.microsoft.com/v1.0/me/onlineMeetings". I am getting a 403 forbidden error. Any idea why I am getting this?
For the graph api create online meetings https://graph.microsoft.com/v1.0/me/onlineMeetings, we can see the tutorial shows it doesn't support "Application permission" to call it. It just support "Delegated permission", so we can just request it by password grant flow but not client credential flow.
Update:
For your requirement to request the graph api of creating online meeting, we can just use password grant flow or auth code flow. Here provide a sample of password grant flow(username and password) for your reference, use this sample to get the token and request the graph api by this token. You can also find this sample in this tutorial.
static async Task GetATokenForGraph()
{
string authority = "https://login.microsoftonline.com/contoso.com";
string[] scopes = new string[] { "user.read" };
IPublicClientApplication app;
app = PublicClientApplicationBuilder.Create(clientId)
.WithAuthority(authority)
.Build();
var accounts = await app.GetAccountsAsync();
AuthenticationResult result = null;
if (accounts.Any())
{
result = await app.AcquireTokenSilent(scopes, accounts.FirstOrDefault())
.ExecuteAsync();
}
else
{
try
{
var securePassword = new SecureString();
foreach (char c in "dummy") // you should fetch the password
securePassword.AppendChar(c); // keystroke by keystroke
result = await app.AcquireTokenByUsernamePassword(scopes,
"joe#contoso.com",
securePassword)
.ExecuteAsync();
}
catch(MsalException)
{
// See details below
}
}
Console.WriteLine(result.Account.Username);
}

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.

Azure App Services (Mobile Apps) AAD authentication token refresh

I am trying to use Azure Active Directory to perform login functions on my uwp app. This happens successfully however I cannot get it to refresh the token when it expires and always receive the error "Refresh failed with a 403 Forbidden error. The refresh token was revoked or expired." and so I have to bring up the login window again. I am using the version 2.1.0 and the following code to authenticate:
private async Task<bool> AuthenticateAsync(bool forceRelogon = false)
{
//string message;
bool success = false;
// Use the PasswordVault to securely store and access credentials.
PasswordVault vault = new PasswordVault();
PasswordCredential credential = null;
//Set the Auth provider
MobileServiceAuthenticationProvider provider = MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory;
MobileServiceUser user = null;
try
{
// Try to get an existing credential from the vault.
var credentials = vault.FindAllByResource(provider.ToString());
credential = credentials.FirstOrDefault();
}
catch (Exception ex)
{
// When there is no matching resource an error occurs, which we ignore.
Debug.WriteLine(ex);
}
if (credential != null && !forceRelogon)
{
// Create a user from the stored credentials.
user = new MobileServiceUser(credential.UserName);
credential.RetrievePassword();
user.MobileServiceAuthenticationToken = credential.Password;
// Set the user from the stored credentials.
App.MobileService.CurrentUser = user;
//message = string.Format($"Cached credentials for user - {user.UserId}");
// Consider adding a check to determine if the token is
// expired, as shown in this post: http://aka.ms/jww5vp.
if (RedemptionApp.ExtensionMethods.TokenExtension.IsTokenExpired(App.MobileService))
{
try
{
await App.MobileService.RefreshUserAsync();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
success = true;
}
else
{
try
{
// Login with the identity provider.
user = await App.MobileService
.LoginAsync(provider);
// Create and store the user credentials.
if (credential != null)
vault.Remove(credential);
credential = new PasswordCredential(provider.ToString(),
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
success = true;
//message = string.Format($"You are now logged in - {user.UserId}");
}
catch (MobileServiceInvalidOperationException)
{
//message = "You must log in. Login Required";
}
}
//var dialog = new MessageDialog(message);
//dialog.Commands.Add(new UICommand("OK"));
//await dialog.ShowAsync();
return success;
}
Can anyone see something wrong with what I am doing, or need to do anything within the AAD service provider?
You might be able to get more accurate information by taking a look at the server-side application logs. Token refresh failure details will be logged there automatically. More details on application logs can be found here: https://azure.microsoft.com/en-us/documentation/articles/web-sites-enable-diagnostic-log/. I recommend setting the trace level to Informational or Verbose.
Also, if you haven't done this already, Azure AD requires a bit of extra configuration to enable refresh tokens. Specifically, you need to configure a "client secret" and enable the OpenID Connect hybrid flow. More details can be found in this blog post: https://cgillum.tech/2016/03/07/app-service-token-store/ (scroll down to the Refreshing Tokens section and see where it describes the process for AAD).
Besides what has been said about mobile app configuration, I can spot this.
You have:
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
It should be:
user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
new Dictionary<string, string>() {{ "response_type", "code id_token" }});
Maybe this will help:
https://azure.microsoft.com/en-us/blog/mobile-apps-easy-authentication-refresh-token-support/

Resources