0

I am just starting in with Azure and my first attempt is using the Graph client API for a simple data display. In simple terms, I want to get the Teams status of an employee and display it on a form in some graphical way.

I am trying to be as basic as can be so when I tried to download the sample I did not want the UWP project, just basic winform (console would work at the moment). I did borrow from the project and got something to compile but I get the error:

MsalUiRequiredException: No account or login hint was passed to the AcquireTokenSilent call.

This is the full code and I am obviously missing something...what? This is an App that should be able to access the Graph API for a get user read and a getPresence call to show current status with the nee to have a use log in. I can see that Graph Explorer has a token and looking at postman set up there is some way to do this without a interaction, but none of the documentation is clear. I'll continue to pok at this and maybe see if I can get postman to work which might help, but behind the scene's access is not clear to me.

    public partial class Form1 : Form

{
    //Set the scope for API call to user.read
    private string[] scopes = new string[] { "user.read" };

    private const string ClientId = "my client id";

    private const string Tenant = "my tenant id"; 
    private const string Authority = "https://login.microsoftonline.com/" + Tenant;

    // The MSAL Public client app
    private static IPublicClientApplication PublicClientApp;

    private static string MSGraphURL = "https://graph.microsoft.com/v1.0/";
    private static AuthenticationResult authResult;
    public Form1()
    {
        InitializeComponent();
        PublicClientApp = PublicClientApplicationBuilder.Create(ClientId).WithRedirectUri("https://login.microsoftonline.com/common/oauth2/nativeclient").Build();

        callMe();
    }
    private async void callMe()
    {
        // Sign-in user using MSAL and obtain an access token for MS Graph
        GraphServiceClient graphClient = await SignInAndInitializeGraphServiceClient(scopes);

        // Call the /me endpoint of Graph
        User graphUser = await graphClient.Me.Request().GetAsync();

        Console.WriteLine(graphUser.Id);

        var graphu2 = await graphClient.Users["my email address"].Request().GetAsync();

    }
    private async Task<GraphServiceClient> SignInAndInitializeGraphServiceClient(string[] scopes)
    {
    
            GraphServiceClient graphClient = new GraphServiceClient(MSGraphURL,
            new DelegateAuthenticationProvider(async (requestMessage) =>
            {
                requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", await getToken(scopes));
            }));

        return await Task.FromResult(graphClient);
    }
    public async Task<string> getToken(string[] scopes)
    {
        PublicClientApp = PublicClientApplicationBuilder.Create(ClientId)
                         .WithAuthority(Authority)
                         .WithLogging((level, message, containsPii) =>
                         {
                             Console.WriteLine($"MSAL: {level} {message} ");
                         }, LogLevel.Warning, enablePiiLogging: false, enableDefaultPlatformLogging: true)
                        .Build();

        IEnumerable<IAccount> accounts = await PublicClientApp.GetAccountsAsync().ConfigureAwait(false);
        IAccount firstAccount = accounts.FirstOrDefault();

        try
        {
            authResult = await PublicClientApp.AcquireTokenSilent(scopes, firstAccount)
                                              .ExecuteAsync();
        }
        catch (MsalUiRequiredException ex)
        {
            // A MsalUiRequiredException happened on AcquireTokenSilentAsync. This indicates you need to call AcquireTokenAsync to acquire a token
            Console.WriteLine($"MsalUiRequiredException: {ex.Message}");

            authResult = await PublicClientApp.AcquireTokenInteractive(scopes)
                                              .ExecuteAsync()
                                              .ConfigureAwait(true);

        }
        return authResult.AccessToken;

    }
2
  • So just to be clear, do you want to login with delegated permissions (i.e. a user, your application needs to access the API as the signed-in user) or application permissions (your application runs as a background service or daemon without a signed-in user)? Commented Oct 21, 2022 at 2:04
  • application permissions. This would be a background type of service with no user interaction. Commented Oct 21, 2022 at 16:56

1 Answer 1

2

Apologies but I'm going to ignore your code and break it back to something that's a lot more simple.

using Azure.Identity;
using Microsoft.Graph;

namespace StackoverflowAnswer
{
    internal class Program
    {
        static void Main(string[] args)
        {
            MainAsync().Wait();
        }

        static async Task MainAsync()
        {
            var tenantId = "YOUR_TENANT_ID";
            var clientId = "YOUR_CLIENT_ID";
            var clientSecret = "YOUR_CLIENT_SECRET";

            try
            {
                string[] scopes = { "https://graph.microsoft.com/.default" };

                ClientSecretCredential clientSecretCredential = new ClientSecretCredential(tenantId, clientId, clientSecret);

                GraphServiceClient graphClient = new GraphServiceClient(clientSecretCredential, scopes);

                var users = await graphClient.Users.Request().GetAsync();

                foreach (var user in users)
                    Console.WriteLine(user.UserPrincipalName);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

A lot of the above code was taken from the following documentation as once you've authenticated, the rest of the SDK is much the same. It can be tricky in points though depending on the specific nature of what you want to do ...

https://github.com/microsoftgraph/msgraph-sdk-dotnet/blob/dev/docs/tokencredentials.md

This also helps ...

https://learn.microsoft.com/en-us/graph/sdks/choose-authentication-providers?tabs=CS#client-credentials-provider

Also make sure that you've assigned the desired API permissions to the app in the Azure Portal ...

API Permissions

... and also make sure you've set a client secret for your app. If you have a client ID then you've clearly already gotten that far ...

https://learn.microsoft.com/en-us/azure/active-directory/develop/quickstart-register-app

Update

Now, in relation to working with the Presence API, this is a little more tricky.

Although it appears to, the Presence API doesn't support application permissions. There is an application permission for it but put simply, it doesn't work. This user voice link provides insight on that.

https://techcommunity.microsoft.com/t5/microsoft-365-developer-platform/graph-api-presence-should-support-application-permissions/idi-p/2276109

So what you need to do is apply the delegated permissions to your registered application.

App Permissions

Because of that, you need to use a UsernamePasswordCredential rather than a ClientSecretCredential in your code and replace it when instantiating the GraphServiceClient.

UsernamePasswordCredential usernamePasswordCredential = new UsernamePasswordCredential("<USERNAME>", "<PASSWORD>", tenantId, clientId);

Further to that, you'll need to make sure that the user in question has granted access to use that permission. If it was a user facing app, then they'd log in and be presented with the question to approve the permissions that you have set but because it's not, you need to go to the Enterprise Applications section in Azure AD, find your app, go to Permissions and press the Grant admin consent button for your tenant.

Someone may have a better approach than the above but it's the only way I could find to do it. It will mean if someone knows the client ID and how to authenticate, they can then execute the same API's as you.

Anyway, that will then allow you to get the presence of all users in your organisation.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you x 10 !!! That small piece of code did the trick and while I had looked through all that documentation I will say it is not well presented and clearly did not have that simple of an example. I was able to set the permissions for Users and could get specific data, but when I tried 'Presence' I got "Forbidden" as a code and no message. I verified the app has access so if you have any more insight...You got me started and I am grateful.
I want to add that I can run the Presence api in graph explorer (sign-in of course)
Ahhhhhhh, sorry, just cottoned on to your request. I'll update my answer but it's a bit more involved.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.