ADAL 到 MSAL 迁移保护 Web API

Posted

技术标签:

【中文标题】ADAL 到 MSAL 迁移保护 Web API【英文标题】:ADAL to MSAL Migration Securing Web API 【发布时间】:2021-07-16 03:50:09 【问题描述】:

我正在开展一个将 ADAL 迁移到 MSAL 的项目。 UI 部分是 Angular,Web API 是 .Net Framework 4.7.2。 在 Startup.Auth.cs 中,我看到代码在验证传入的 API 请求时非常有效。但是,当我在 Angular 中更改从 MSAL 获取的令牌并将熊令牌发送到 API 时,API 无法告知用户已通过身份验证,也无法告知用户是谁(它将始终返回 false)。

我看到很多来自 .net Core 的例子,有没有什么好的方法可以在 .net Framework 中进行更改?

        public void ConfigureAuth(IAppBuilder app)
        
            var constants = UnityConfig.GetConfiguredContainer().Resolve<IConstants>();
            app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                
                    Tenant = constants.TenantId,
                    TokenValidationParameters = new TokenValidationParameters
                    
                        ValidAudiences = new List<string>  constants.Audience, constants.ClientId
                    ,
                    AuthenticationType = "OAuth2Bearer",
                    AuthenticationMode = AuthenticationMode.Active
                );

        

【问题讨论】:

【参考方案1】:

你可以使用我的 nuget,MSALTokenGeneratorv3。

public static async Task ConfidentialClientBuilderApp(string resource = null, string clientId = null, 字符串 clientSecret = null,字符串权限 = null,IEnumerable scopes = null) // 我们将使用 MSAL.NET 获取令牌以代表当前用户调用 API 尝试 // 使用构建模式创建一个 ConfidentialClientApplication (https://github.com/AzureAD/microsoft-authentication-library-for-dotnet/wiki/Client-Applications) ExponentialRetryStrategy retryStrategy = new ExponentialRetryStrategy();

                _confidentialClientApp = ConfidentialClientApplicationBuilder.Create(clientId)
                    .WithAuthority(String.Format(CultureInfo.InvariantCulture, authority))
                    .WithClientSecret(clientSecret).WithLegacyCacheCompatibility(false)
                    .Build();

                // In memory distributed token cache
                _confidentialClientApp.AddInMemoryTokenCache();

                // Acquiring an AuthenticationResult for the scope user.read, impersonating the user represented by userAssertion, using the OBO flow
                await RetryHelper.RetryAsync(async () =>
                
                    _authenticationResult = await _confidentialClientApp.AcquireTokenForClient(scopes).ExecuteAsync().ConfigureAwait(false);
                , (exception) =>
                
                    return true;
                , retryStrategy.ShouldRetry(), new Action<Exception, RetryErrorType, int>(LogTokenGenerationFailure)).ConfigureAwait(false);

                if (_authenticationResult != null && !string.IsNullOrEmpty(_authenticationResult.AccessToken))
                    accessToken = _authenticationResult.AccessToken;

                if (accessToken == null)
                
                    throw new Exception("Access Token could not be acquired.");
                

                return accessToken;
            
            catch (MsalUiRequiredException)
            
                /*
                * If you used the scope `.default` on the client application, the user would have been prompted to consent for Graph API back there
                * and no incremental consents are required (this exception is not expected). However, if you are using the scope `access_as_user`,
                * this exception will be thrown at the first time the API tries to access Graph on behalf of the user for an incremental consent.
                * You must then, add the logic to delegate the consent screen to your client application here.
                * This sample doesn't use the incremental consent strategy.
                */
                throw;
            
            catch (Exception)
            
                throw;
            
    

public static async Task PublicClientBuilderApp(IEnumerable scope, string tenantId, string clientId = null, 字符串用户名 = null,字符串密码 = null) ConvertStringToSecureString convertToSecureString = new ConvertStringToSecureString(); ExponentialRetryStrategy retryStrategy = new ExponentialRetryStrategy();

        // We will use MSAL.NET to get a token to call the API On Behalf Of the current user
        try
        
            _publicClientApp = PublicClientApplicationBuilder.Create(clientId).WithAuthority(AzureCloudInstance.AzurePublic, 
                tenantId).WithLegacyCacheCompatibility(false).Build();

            // Acquiring an AuthenticationResult for the scope user.read, impersonating the user represented by userAssertion, using the OBO flow
            await RetryHelper.RetryAsync(async () =>
            
                //To clear the token cache every time the new request comes , else it uses cached userassertion
                var accounts = await _publicClientApp.GetAccountsAsync().ConfigureAwait(false);

                IEnumerator<IAccount> enumerator = accounts.GetEnumerator();
                while (enumerator.Current != null)
                
                    await _publicClientApp.RemoveAsync(enumerator.Current).ConfigureAwait(false);

                
                // Acquiring an AuthenticationResult for the scope user.read, impersonating the user represented by userAssertion, using the OBO flow
                _authenticationResult = await _publicClientApp.AcquireTokenByUsernamePassword(scope, userName, convertToSecureString.StringToSecureString(password)).ExecuteAsync().ConfigureAwait(false);
            , (exception) =>
            
                return true;
            , retryStrategy.ShouldRetry(), new Action<Exception, RetryErrorType, int>(LogTokenGenerationFailure)).ConfigureAwait(false);

            if (_authenticationResult != null && !string.IsNullOrEmpty(_authenticationResult.AccessToken))
                accessToken = _authenticationResult.AccessToken;

            if (accessToken == null)
            
                throw new Exception("Access Token could not be acquired.");
            

            return accessToken;
        
        catch (MsalUiRequiredException)
        
            /*
            * If you used the scope `.default` on the client application, the user would have been prompted to consent for Graph API back there
            * and no incremental consents are required (this exception is not expected). However, if you are using the scope `access_as_user`,
            * this exception will be thrown at the first time the API tries to access Graph on behalf of the user for an incremental consent.
            * You must then, add the logic to delegate the consent screen to your client application here.
            * This sample doesn't use the incremental consent strategy.
            */
            throw;
        
        catch (Exception)
        
            throw;
        
    

    public static void LogTokenGenerationFailure(Exception exception, RetryErrorType errorType, int retryCount)
    
    

【讨论】:

您的代码格式在这个答案中不是很一致(其中一些最终成为常规文本)。你能修复 C# 代码的降价吗?此外,您可能应该添加一些信息,说明您的包到底是做什么的,它拥有什么许可证,以及它是否是开源的等。在 Nuget 上,没有指向项目网站或 repo 的链接。谢谢! 非常好的反馈,周末会更新,非常感谢您的指出。

以上是关于ADAL 到 MSAL 迁移保护 Web API的主要内容,如果未能解决你的问题,请参考以下文章

ADAL 和 MSAL 混淆

使用 MSAL 保护 ASP.Net Core Web API 和 Angular App

ADAL.js 和 MSAL.js 有啥区别?

推荐用于 Web API 的 ADAL 令牌缓存?

使用 react-aad-msal 缺少范围从 react SPA 调用 web api

如何在不使用 ADAL.js 或 MSAL.js 的情况下将我的 activiti 应用程序(版本 6)与 Azure Active Directory 端点 v2.0 集成