使用个人用户帐户在 Blazor 服务器端应用程序上访问 MS Graph 数据的正确方法

Correct way to access MS Graph data on blazor server-side app with Individual User Acconts

我在 Blazor(ASP.NET 核心)服务器端构建了一个 Web 应用程序。该应用程序是多租户的,我需要控制谁有访问权限,因此我使用存储在 SQL 数据库中的自己的用户数据库 (DefaultIdentity)。

现在我需要访问 Microsoft Graph(对于某些用户)。我通过使用应用程序级访问来实现这一点,但我想避免让本地 IT 管理员在他们的 Azure AD 中设置它——我想遵守“最小特权原则”。

所以我希望能够添加一个登录 page/functionality 特定用户(在他们使用我的本地用户数据库登录后)可以给我的应用授权+同意检索 Calendar.Read 通过 Microsoft Graph 获得许可。

我应该怎么做?

我认为我学到的是

我能找到的所有示例都使用 Azure AD 来登录用户。有没有我可以查看的“手动”实现授权代码流的示例?

我现在有一个可行的解决方案,但想听听你们中的一些人是否有任何意见 - 我没有看到的任何警告或陷阱。

我的解决方案如下:

我正在使用“标准流程”获取 PublicClientApplication

        IPublicClientApplication app = PublicClientApplicationBuilder
            .Create(publicAppClientId)
            .WithRedirectUri("http://localhost")                
            .Build();
        
        TokenCacheHelper.EnableSerialization(app.UserTokenCache);
        
        var accounts = await app.GetAccountsAsync();
        IAccount firstAccount = accounts.FirstOrDefault();
                              
        AuthenticationResult authResult = null;
        try
        {
            authResult = await app.AcquireTokenSilent(scopesList, firstAccount)
                        .ExecuteAsync();                
        }
        catch (MsalUiRequiredException)
        {                
            try
            {
                authResult = await app.AcquireTokenInteractive(scopesList)
                    .ExecuteAsync();                 
            }
            catch (MsalException msalex)
            {
                System.Diagnostics.Debug.WriteLine($"Error acquiring Token:{System.Environment.NewLine}{msalex}");
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine($"Error acquiring token silently:{System.Environment.NewLine}{ex}");
            return;
        }

        if (authResult != null)
        {
            System.Diagnostics.Debug.WriteLine($"Access token:{System.Environment.NewLine}{authResult.AccessToken}");                
                           
            // Use the token
            InteractiveAuthenticationProvider authProvider = new InteractiveAuthenticationProvider(app, scopesList);
            GraphServiceClient = new GraphServiceClient(authProvider);                
        }
    }

我使用 TokenCacheHelper

添加了分布式 SQL 缓存
 public class TokenCacheHelper
 {
    private IDistributedCache _cache;
    private string lookUpAccount = string.Empty;
    private byte[] token = null;

    public TokenCacheHelper(IDistributedCache cache, string user)
    {
        _cache = cache;
        lookUpAccount = user;
    }

    public void EnableSerialization(ITokenCache tokenCache)
    {
        tokenCache.SetBeforeAccess(BeforeAccessNotification);
        tokenCache.SetAfterAccess(AfterAccessNotification);
    }
    
    private void BeforeAccessNotification(TokenCacheNotificationArgs args)
    {
        if (token == null)
        {
            token = _cache.Get(lookUpAccount);
        }
        args.TokenCache.DeserializeMsalV3(token);
    }

    private void AfterAccessNotification(TokenCacheNotificationArgs args)
    {
        if (args.HasStateChanged)
        {
            token = args.TokenCache.SerializeMsalV3();
            _cache.Set(lookUpAccount, token);
        }
    }
}

在 startup.cs 中添加 DistributedSqlServerCache,对令牌设置“无限”超时

services.AddDistributedSqlServerCache(options =>
        {
            options.ConnectionString = Configuration.GetConnectionString("TestDbContext");
            options.SchemaName = "dbo";
            options.TableName = "TestCache";
            options.DefaultSlidingExpiration = TimeSpan.FromDays(300);
            options.ExpiredItemsDeletionInterval = TimeSpan.FromDays(300);
        });

这一切似乎都奏效了。有什么想法吗?