首页 > 解决方案 > Xamarin 身份验证 - IUser 已贬值,迁移到 IAccount 时出现问题

问题描述

我的第一个 Xamarin Forms 应用程序即将结束(哇!),并且处于学习身份验证并将其链接到 Azure 的阶段。

我已经下载了我能找到的最好的例子,在这里:链接到 Xamarin Azure 示例

我计划浏览 IdentityService.cs 文件并弄清楚它是如何工作的。

不幸的是,最近出现了贬值,如下所述:msal net 2 已发布

我已经尽可能地改变了,但仍然有两个错误:

msalResult = await msaClient.AcquireTokenSilentAsync(Scopes,

IdentityService.GetUserByPolicy(msaClient.GetAccountsAsync(),

SignUpAndInPolicy), UIBehavior.ForceLogin, null, null, Authority, UIParent);                                           

这显示一个错误:

严重性代码描述项目文件行抑制状态错误 CS1503 参数 1:无法从 'System.Threading.Tasks.Task>' 转换为 'System.Collections.Generic.IEnumerable' Reviewer.Core C:\Users\Administrator\Desktop\snppts\ xam-az-bw\src\Reviewer.Core\Services\IdentityService.cs 54 活动

和:

public void Logout()
    {
        foreach (var user in msaClient.GetAccountsAsync())
        {
            msaClient.Remove(user);
        }
    }         

这显示了以下相关错误:

严重性代码 描述 项目文件行抑制状态错误 CS1579 foreach 语句无法对“Task>”类型的变量进行操作,因为“Task>”不包含“GetEnumerator”Reviewer.Core C:\Users\Administrator\Desktop\ 的公共实例定义snppts\xam-az-bw\src\Reviewer.Core\Services\IdentityService.cs 120 活动

我怀疑这两个问题有相同的根本原因。任何人都可以帮助我以正确的方式提取帐户吗?我不想更多地摆弄原件,而且我基本上是在修补一个我一开始还不了解的身份验证过程!

这是完整的文件代码:

using System;
using Microsoft.Identity.Client;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using Reviewer.Core;
using System.Linq;

[assembly: Dependency(typeof(IdentityService))]
namespace Reviewer.Core
{
    public class IdentityService : IIdentityService
    {
        static readonly string Tenant = "b2cbuild.onmicrosoft.com";
        static readonly string ClientID = "adc26e3b-2568-4007-810d- 6cc94e7416de";

        static readonly string SignUpAndInPolicy = "B2C_1_Reviewer_SignUpIn";
        static readonly string AuthorityBase = 
$"https://login.microsoftonline.com/tfp/{Tenant}/";
        static readonly string Authority = $"{AuthorityBase} 
 {SignUpAndInPolicy}";
        static readonly string[] Scopes = { 
"https://b2cbuild.onmicrosoft.com/reviewer/rvw_all" };
        static readonly string RedirectUrl = $"msal{ClientID}://auth";

    readonly PublicClientApplication msaClient;

    public IdentityService()
    {
        msaClient = new PublicClientApplication(ClientID);
        msaClient.ValidateAuthority = false;
        msaClient.RedirectUri = RedirectUrl;
    }

    UIParent parent;
    public UIParent UIParent { get => parent; set => parent = value; }

    public async Task<AuthenticationResult> Login()
    {
        AuthenticationResult msalResult = null;

        // Running on Android - we need UIParent to be set to the main Activity
        if (UIParent == null && Device.RuntimePlatform == Device.Android)
            return msalResult;

        // First check if the token happens to be cached - grab silently
        msalResult = await GetCachedSignInToken();

        if (msalResult != null)
            return msalResult;

        // Token not in cache - call adb2c to acquire it
        try
        {
            msalResult = await msaClient.AcquireTokenSilentAsync(Scopes,
                                                       IdentityService.GetUserByPolicy(msaClient.GetAccountsAsync(),
                                                                       SignUpAndInPolicy),
                                                       UIBehavior.ForceLogin,
                                                       null,
                                                       null,
                                                       Authority,
                                                       UIParent);
            if (msalResult?.Account.HomeAccountId != null)
            {
                var parsed = ParseIdToken(msalResult.IdToken);
                DisplayName = parsed["name"]?.ToString();
            }
        }
        catch (MsalServiceException ex)
        {
            if (ex.ErrorCode == MsalClientException.AuthenticationCanceledError)
            {
                System.Diagnostics.Debug.WriteLine("User cancelled");
            }
            else if (ex.ErrorCode == "access_denied")
            {
                // most likely the forgot password was hit
                System.Diagnostics.Debug.WriteLine("Forgot password");
            }
            else
            {
                System.Diagnostics.Debug.WriteLine(ex);
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
        }

        return msalResult;
    }

    public async Task<AuthenticationResult> GetCachedSignInToken()
    {
        try
        {
            // This checks to see if there's already a user in the cache
            var authResult = await msaClient.AcquireTokenSilentAsync(Scopes,
                                                           IdentityService.GetUserByPolicy(msaClient.GetAccountsAsync(),
                                                                           SignUpAndInPolicy),
                                                           Authority,
                                                           false);
            if (authResult?.User != null)
            {
                var parsed = ParseIdToken(authResult.IdToken);
                DisplayName = parsed["name"]?.ToString();
            }

            return authResult;
        }
        catch (MsalUiRequiredException ex)
        {
            // happens if the user hasn't logged in yet & isn't in the cache
            System.Diagnostics.Debug.WriteLine(ex);
        }

        return null;
    }

    public void Logout()
    {
        foreach (var user in msaClient.GetAccountsAsync())
        {
            msaClient.Remove(user);
        }
    }

    public string DisplayName { get; set; }

    IAccount GetUserByPolicy(IEnumerable<IAccount> users, string policy)
    {
        foreach (var user in users)
        {
            string userIdentifier = Base64UrlDecode(user.HomeAccountId.Identifier.Split('.')[0]);

            if (userIdentifier.EndsWith(policy.ToLower(), StringComparison.OrdinalIgnoreCase)) return user;
        }

        return null;
    }

    string Base64UrlDecode(string s)
    {
        s = s.Replace('-', '+').Replace('_', '/');
        s = s.PadRight(s.Length + (4 - s.Length % 4) % 4, '=');
        var byteArray = Convert.FromBase64String(s);
        var decoded = Encoding.UTF8.GetString(byteArray, 0, byteArray.Count());
        return decoded;
    }

    JObject ParseIdToken(string idToken)
    {
        // Get the piece with actual user info
        idToken = idToken.Split('.')[1];
        idToken = Base64UrlDecode(idToken);
        return JObject.Parse(idToken);
    }
}

}

标签: .netazureauthenticationxamarin.forms

解决方案


要回答您的问题,我认为您await在 msaClient.GetAccountsAsync() 前面缺少

msalResult = await msaClient.AcquireTokenSilentAsync(Scopes,
      IdentityService.GetUserByPolicy(await msaClient.GetAccountsAsync(),
                                      SignUpAndInPolicy),     
      UIBehavior.ForceLogin, null, null, Authority, UIParent);

public void Logout()
{
 var accounts = await msaClient.GetAccountsAsync();
 foreach (var account in accounts)
 {
  msaClient.RemoveAsync(account);
  accounts = await msaClient.GetAccountsAsync();
 }
}

(这真的是一个ClearCache()

我建议你看看这个示例:https ://github.com/Azure-Samples/active-directory-b2c-xamarin-native ,其中已经有更新的代码


推荐阅读