0

I have an ASP.NET Core MVC application that uses Azure AD to authenticate users and allows users to upload and access documents in a shared onedrive folder. I currently have the permissions for this application set to delegated permissions and use access token cached on the backend to use these permissions and make MS Graph calls.

However, I may be moving away from Azure AD towards Okta so I am planning to switch to application permissions and the backend will just be the user saving to the shared folder and such.

However, I am just curious is there any forseen issues with a service account saving to a real users shared drive? Will the access token just be issued for the service account then rather then when the user logs in?

My current code for setting up azure authentication in program.cs is as follows:

var initialScopes = builder.Configuration.GetValue<string>
("DownstreamApi:Scopes")?.Split(' ');

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
                .AddMicrosoftIdentityWebApp(options =>
                {
                    builder.Configuration.Bind("AzureAd", options);
                }).EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                        .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApi"))
                        .AddInMemoryTokenCaches();

I currently utilize MS Graph with these delegated tokens as follows in this example from my OneDrive service I created:

    public class OneDrive : IOneDrive
{

    private readonly GraphServiceClient _graphServiceClient;
    private readonly ITokenAcquisition _tokenAcquisition;
    private readonly string[] initialScopes;
    private readonly MicrosoftIdentityConsentAndConditionalAccessHandler _consentHandler;

    public OneDrive(GraphServiceClient graphServiceClient, ITokenAcquisition tokenAcquisition, IConfiguration configuration, MicrosoftIdentityConsentAndConditionalAccessHandler consentHandler)
    {
        _graphServiceClient = graphServiceClient;
        _tokenAcquisition = tokenAcquisition;
        initialScopes = configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');
        this._consentHandler = consentHandler;
    }

    public async Task<IDriveItemSearchCollectionPage> DriveItemSearchAsync(string DriveID, string SearchItem)
    {

        var tries = 0;
        var maxRetries = 1;

        IDriveItemSearchCollectionPage response = null;

        while (tries <= maxRetries)
        {
            tries++;
            try
            {

                var queryOptions = new List<QueryOption>()
                {
                    new QueryOption("select", "name,id,webUrl")
                };

                response = await _graphServiceClient.Me.Drives[DriveID].Root
                        .Search(SearchItem)
                        .Request(queryOptions)
                        .GetAsync();

                tries = maxRetries+1;

            }
            catch (ServiceException svcex) when (svcex.Message.Contains("Continuous access evaluation resulted in claims challenge"))
            {
                try
                {
                    Console.WriteLine($"{svcex}");
                    string claimChallenge = WwwAuthenticateParameters.GetClaimChallengeFromResponseHeaders(svcex.ResponseHeaders);
                    _consentHandler.ChallengeUser(initialScopes, claimChallenge);
                }
                catch (Exception ex2)
                {
                    _consentHandler.HandleException(ex2);
                }
            }
        }

        return response;

    }
}
Irish Redneck
  • 983
  • 7
  • 32

1 Answers1

0

Obtaining access token for delegated api permission and application api permission a re different. You can certainly generate delegated access token with your AddMicrosoftIdentityWebApp().EnableTokenAcquisitionToCallDownstreamApi(), but this can't generate application access token.

I'm not sure what requirement you want to realize, and how you call graph api now(you have AddMicrosoftGraph so you can use graph sdk) because you didn't show it. But if you want to use application api permission to generate access token and call graph API, you have to have code similar to this:

using Microsoft.Graph;
using Azure.Identity;

var scopes = new[] { "https://graph.microsoft.com/.default" };
var tenantId = "tenant_name.onmicrosoft.com";
var clientId = "aad_app_id";
var clientSecret = "client_secret";
var clientSecretCredential = new ClientSecretCredential(
                tenantId, clientId, clientSecret);
var graphClient = new GraphServiceClient(clientSecretCredential, scopes);
var res = await graphClient.Drives["{drive-id}"].Items["{driveItem-id}"].Children.PostAsync(requestBody);

And the drive item api supports application permission. Using service account to save to a real users shared drive won't bring any issue, because it's supported to do these operations. The access token delegated for a user is different for a daemon application(which is your backend).

Using code above to generate access token:

var tokenRequestContext = new TokenRequestContext(scopes);
var token = clientSecretCredential.GetTokenAsync(tokenRequestContext).Result.Token;
Tiny Wang
  • 10,423
  • 1
  • 11
  • 29
  • Hey thanks for the example code. So I added how I use the delegated token to my original post. Sorry that was an oversight! So does new ClientSecretCredential() get the application access token? For my original code above its almost as if the sdk handles getting the access token and stores it in the cache. It also manages a redirect when the access token is expired and gets a new token. For application permissions do I need to manage this myself? I am curious what my program.cs should look like. Lastly, my other concern is can a service account send emails from a shared email address? – Irish Redneck Jun 06 '23 at 14:40
  • We basically use OneDrive and we use a Shared Outlook email box to send emails. THe ondrive as you said using the service account should be okay. How about the email sending when switching to the application permissions? I couldnt find much on this topic online. – Irish Redneck Jun 06 '23 at 14:42
  • does new ClientSecretCredential() get the application access token --> yes. do I need to manage this myself? --> no need, you might reuse the code when ever you want to send the email. what my program.cs should look like--> no need to modify your program.cs because you don't have authentication scheme now, but if you want to write that code into a service so that you can DI, then you need to manage Program.cs. – Tiny Wang Jun 07 '23 at 11:07
  • can a service account send emails from a shared email address --> not sure about it. Using Application permission makes you could send email onbehalf any user in your tenant.. about sending email, you might take a look at [this answer](https://stackoverflow.com/a/70780395/14574199) – Tiny Wang Jun 07 '23 at 11:08