需要将脱机和触摸ID添加到Microsoft身份验证层

时间:2019-09-24 14:48:06

标签: c# authentication xamarin msal

我有一些Xamarin C#代码可以根据公司目录对用户进行身份验证,基本上是在Microsoft tutorial中找到的代码,现在我们仅使用iOS:

App.xaml.cs

PublicClientApplicationOptions options = new PublicClientApplicationOptions()
{
    ClientId = MyAppClientId,
    TenantId = MyAppTenantId
};
var builder = PublicClientApplicationBuilder.CreateWithApplicationOptions(options);
if (!string.IsNullOrEmpty(iOSKeychainSecurityGroup))
{
    builder = builder.WithIosKeychainSecurityGroup(iOSKeychainSecurityGroup);
}
PCA = builder.Build();

ViewModel.cs

string Scopes = "User.Read";
var scopes = Scopes.Split(' ');  // Yeah, overkill

// First, attempt silent sign in
// If the user's information is already in the app's cache,
// they won't have to sign in again.
string accessToken = string.Empty;
try
{
    var accounts = await App.PCA.GetAccountsAsync();
    // PCA.GetAccountsAsync() returned [List<IAccount> #=0]
    if (accounts.Count() > 0)
    {
       var silentAuthResult = await App.PCA
          .AcquireTokenSilent(scopes, accounts.FirstOrDefault())
          .ExecuteAsync();
       accessToken = silentAuthResult.AccessToken;
    }
 }
 catch (MsalUiRequiredException)
 {
    // This exception is thrown when an interactive sign-in is required.
    // Don't need to do anything, we will notice the empty access token later
 }

 if (string.IsNullOrEmpty(accessToken))
 {
     // Prompt the user to sign-in
     var interactiveRequest = App.PCA.AcquireTokenInteractive(scopes);
     // PCA.AcquireTokenInteractive(scopes) returned Microsoft.Identity.Client.AcquireTokenInteractiveParameterBuilder
     if (authUiParent != null)
     {
        interactiveRequest = interactiveRequest
           .WithParentActivityOrWindow(authUiParent);
     }

     try
     {
        var authResult = await interactiveRequest.ExecuteAsync();
     }
     catch (MsalClientException clientException)
     {
        // When I entered the wrong password, and then hit cancel:
        // Or, when I got the "you need admin permissions" error and then hit cancel:
        /*
        interactiveRequest.ExecuteAsync() threw MsalClientException [error code "authentication_canceled"]
        Exception MsalClientException: User canceled authentication.
        */
     }
  }

效果很好,现在我需要稍有不同(当然)。

1)首先,我们需要“离线”模式。如果用户要在没有Internet的地方访问应用程序,我们希望他们输入其用户名和密码,并将其与该用户的最新已知有效值进行比较。现在,我们正在使用内部加密数据库来存储最后一个已知的好值以进行比较。是的,这里有一个安全漏洞,如果我们在服务器上禁用了用户帐户,只要他们在移动设备上禁用了Internet,他们就可以继续登录-我们有其他方法可以最大程度地减少这一问题,而我不会不要进入这里。

2)其次,我们要允许触摸ID。但是,即使指纹验证了用户的身份,我们仍然要检查该用户是否已在服务器上被禁用-因此我们需要将该用户的“最后已知正确值”发送到服务器以进行验证。当然,如果没有互联网,指纹就足够了。我假设这意味着我们需要在调用AcquireTokenInteractive()之前需要一个屏幕,在该屏幕中,我们为用户提供了使用Touch ID的机会,并带有一个按钮:“不,我想输入我的用户名和密码”

3)最后,即使我们拥有Internet且用户选择不使用Touch ID,我们也希望他们每次都输入密码。我们想记住最近登录用户的用户名,并填写该用户名以加快操作速度,但是出于安全原因,我们每次都需要输入密码。

1 个答案:

答案 0 :(得分:1)

您的应用程序的安全性似乎是您最大的担忧。因此,我将详细介绍安全性和身份,以帮助您做出决策。

问题1:离线模式

恐怕您不建议采用离线访问方法。实际上,在MSAL中,我们通过使用系统浏览器来积极缓解这种情况。

嵌入式浏览器的安全问题和移动应用程序中基于表单的身份验证

当用户使用嵌入式Web视图或基于表单的身份验证登录到移动应用程序时,该应用程序可以访问输入到该应用程序中的纯文本用户名和密码。如果用户从您的公司下载行为类似于合法应用程序的应用程序,则这些凭据可能会在用户不知情的情况下被盗。这是一个安全漏洞,严重程度足以使Google采取blocking all apps that use embedded webview or forms based authentication的步骤。

系统浏览器和身份验证

为防止应用程序对凭据的这种存储,Google,Microsoft和其他公司已切换到系统浏览器以收集凭据。我们使用移动设备上操作系统的新功能来在您的应用程序之上显示Web登录体验。这似乎是用户的本机,但实际上是操作系统的浏览器。因为应用程序和Microsoft都无法访问操作系统的浏览器,所以用户输入的凭据是安全的。您将看到使用此模式的大多数现代应用程序。

这也将阻止您存储用户名和密码,这是设计使然。请不要将用户的凭据存储在任何地方。

如何允许应用程序的脱机访问

为了正确实现您的方案,我们已经看到了应用程序使用的两个选项:

  • 我们看到的最流行的模式是应用程序要求用户在首次登录时或在“设置”中作为选项来设置PIN来访问应用程序。如果Touch ID / Face ID失败或被重置,通常会要求用户设置PIN以及Touch ID / Face ID。然后,用户在每次启动时都使用PIN来访问该应用程序,并且在无法连接互联网时也可以使用。此PIN码已安全存储并在设备上加密。互联网可用后,应用程序应调用acquireTokenSilently()以确保用户仍然可以访问资源。如果没有,则应提示用户再次登录。许多银行和其他受到严格监管的行业都将这种模式用作用户体验和安全性之间的最佳折衷。

  • 下一个选择是对那些希望用户在发生故障时保持对资源的访问的公司和应用程序开发人员,使用我们在库中内置的弹性。对于访问令牌和刷新令牌,我们允许公司和应用程序开发者configure a token lifetime的使用时间比我们默认的令牌生存时间长。当用户无法访问Internet以获得新的访问令牌时,延长的生存期可以使用户继续使用该应用程序。

    如果您的API在您的环境中是本地的,但是身份提供者是基于云的,或者如果Azure AD发生故障而Internet的其余部分仍在工作,则这具有工作的额外好处。您的内部API将接受令牌,因此即使无法访问Internet,您的用户也可以继续工作。 API和您的应用程序都将需要使用MSAL,以使您的API遵守令牌中指定的延长的生存期。请记住,一天的访问令牌具有最大价值。如果您的互联网中断时间长于此时间,我建议使用上面的选项1。

问题2:使用Touch ID时,请向用户提供身份服务

这个答案很简单。使用Touch ID后,只需使用acquireTokenSilently()。这将为您请求令牌的资源获取一个新令牌。对于默认令牌值,访问令牌将每1小时刷新一次。我不建议强行对每个Touch ID进行身份验证,因为这会导致速度显着降低和移动电话使用率提高。如果您只想检查用户的存在,我建议您使用上面讨论的本地存储的PIN。

问题3:如果未启用Touch ID,则强制进行身份验证

您可以随时通过使用带有适当值的acquireToken()来强制进行身份验证。请参见此处的method acquireTokenInteractively()代码示例。

但是,这里有一些关于可用性的警告:

频繁使用密码来测试用户访问权限的问题

身份服务用于检查用户是否有权访问资源,而不是检查用户是否有权访问设备或该用户是否仍然是上一个会话的用户。为此,您应该使用上面讨论过的PIN方法以及Touch ID / Face ID(如果可用)。为什么?

为了真正的安全,密码应与2FA结合使用。研究表明,99% of illegal access can be mitigated by 2FA。但是,当用户必须使用2FA进行登录时,摩擦会增加,他们必须忍受登录。实际上,relying on passwords at all is problematic

您的用例似乎表明您想要您的应用程序的最大安全性。如有可能,必须使用2FA。但是,要使应用程序达到最佳安全状态,也会使用户每次启动应用程序时都很难使用密码。

在更改用户对资源的访问权限后或很长一段时间后,建议的模式还是PIN或Touch ID / Face ID以及密码提示。