导航到基于MVVM的页面时,Azure AD B2C身份验证丢失

时间:2019-01-12 17:16:13

标签: xamarin mvvm xamarin.forms azure-active-directory azure-ad-b2c

两部分问题:在启动时,My Xamarin.Forms app.cs使用按钮导航到登录页面(ContentPage)。我单击按钮,然后使用后面的应用程序代码中的此事件处理程序成功登录:

async void OnLoginButtonClicked(object sender, EventArgs e)
    {
        try
        {
            bool authenticated = await App.AuthenticationProvider.LoginAsync();
            if (authenticated)
            {
                Application.Current.MainPage = new PapMobLandingPage();
            }
            else
            {
                await DisplayAlert("Authentication", "Authentication", "OK");
            }
        }
        catch (MsalException ex)
        {
            if (ex.ErrorCode == "authentication_canceled")
            {
                await DisplayAlert("Authentication", "Authentication was cancelled by the user.", "OK");
            }
            else
            {
                await DisplayAlert("An error has occurred", "Exception message: " + ex.Message, "OK");
            }
        }
        catch (Exception ex)
        {
            await DisplayAlert("Authentication", "Authentication failed in a big way. Exception: " + ex.Message, "OK");
        }
    }
}

然后我被重定向到第2页(PapMobLandingPage),该页面也有一个按钮。我单击该PapMobLandingPage按钮,然后重定向到TabbedPage,其中包含从我的Azure SQL数据库飞来的登录用户的详细信息,没有问题。直到现在一切都好!这是事件处理程序:

public async void GoToTabbedPage(object sender, EventArgs args)
        {
            await Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new BotInTabbedPage());
        }

问题1:BotInTabbedPage(TabbedPage)代码中没有OnAppearing(),该代码检查用户是否已登录... TabbedPage初始化中没有获取令牌,因此应用程序如何知道我已在此页面上登录?

我从@Creepin看了一个类似的问题,并且使用该链接我无法理解他最初的问题的答案,即您是否必须分别验证每个页面。链接到这里:

use of AcquireTokenSilentAsync

我问问题1的原因是上面的选项卡式页面是用户登录时选择的六个选项之一,因此我需要一个包含六个图块的仪表板页面(基于MVVM)。当我将其插入到Page 2(PapMobLandingPage)和BotInTabbedPage(TabbedPage)之间,然后单击TabbedPage磁贴时,链接到该磁贴的tap命令将我带到BotInTabbedPage(TabbedPage)...但是...

没有客户端数据!我已经登出!

所以总结一下:

登录页面-> PapMobLandingPage-> BotInTabbedPage =保持身份验证。 登录页面-> PapMobLandingPage->仪表板-> BotInTabbedPage =放弃身份验证。

如果我使用带有按钮的“原始” ContentPage:

登录页面-> PapMobLandingPage-> ContentPage-> BotInTabbedPage =保持身份验证!

第二个问题是:有人知道为什么吗?

当我说Dashboard基于MVVM时,我的意思是它具有XAML ContentPage,后面是.cs代码,具有对视图模型的值绑定,该视图模型也具有视图模型模板和模板库。这是代码:

仪表板XAML:

<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
             xmlns:local="clr-namespace:PapWine;assembly=PapWine" 
             xmlns:artina="clr-namespace:UXDivers.Artina.Shared;assembly=UXDivers.Artina.Shared"
             x:Class="PapWine.DashboardTaskMultipleTilesPage"
             BackgroundColor="Black"
             Title="{ artina:Translate PageTitleDashboardTaskMultipleTiles }">

    <ContentPage.Resources>
        <ResourceDictionary>
            <artina:BoolMemberTemplateSelector
                x:Key="Selector"
                MemberName="IsNotification">

                <artina:BoolMemberTemplateSelector.TrueDataTemplate>
                    <DataTemplate>
                        <local:DashboardAppNotificationItemTemplate
                            WidthRequest="145"
                            HeightRequest="145" />
                    </DataTemplate>
                </artina:BoolMemberTemplateSelector.TrueDataTemplate>

                <artina:BoolMemberTemplateSelector.FalseDataTemplate>
                    <DataTemplate>
                        <local:TaskTilesItemTemplate
                            ShowBackgroundImage="true"
                            ShowBackgroundColor="true"
                            ShowiconColoredCircleBackground="false"
                            TextColor="{ DynamicResource DashboardIconColor }"
                            WidthRequest="145"
                            HeightRequest="145"
                            />
                    </DataTemplate>
                </artina:BoolMemberTemplateSelector.FalseDataTemplate>

            </artina:BoolMemberTemplateSelector>
        </ResourceDictionary>
    </ContentPage.Resources>

    <ScrollView
        Orientation="Both">
        <artina:GridOptionsView
            WidthRequest="320"
            Margin="0"
            Padding="10"
            ColumnSpacing="10"
            RowSpacing="10"
            ColumnCount="2"
            ItemsSource="{Binding DashboardTaskMultipleTilesList}"
            ItemTemplate="{StaticResource Selector}"
            />
    </ScrollView>
</ContentPage>

仪表板XAML.cs

using Microsoft.Identity.Client;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xamarin.Forms;

namespace PapWine
{
    public partial class DashboardTaskMultipleTilesPage : ContentPage
    {
        public DashboardTaskMultipleTilesPage()
        {           
            InitializeComponent();
            NavigationPage.SetHasNavigationBar(this, true);
            BindingContext = new DashboardTaskMultipleTilesViewModel();  
        }

        protected override async void OnAppearing()
        {
            base.OnAppearing();

            PublicClientApplication PCA = new PublicClientApplication(Constants.ClientID, Constants.Authority);
            IEnumerable<IAccount> accounts = await PCA.GetAccountsAsync();
            AuthenticationResult authenticationResult = await PCA.AcquireTokenSilentAsync(Constants.Scopes, GetAccountByPolicy(accounts, Constants.PolicySignUpSignIn), Constants.Authority, false);
            JObject user = ParseIdToken(authenticationResult.IdToken);
            var currentuseroid = user["oid"]?.ToString();
        }

        private IAccount GetAccountByPolicy(IEnumerable<IAccount> accounts, string policy)
        {
            foreach (var account in accounts)
            {
                string userIdentifier = account.HomeAccountId.ObjectId.Split('.')[0];
                if (userIdentifier.EndsWith(policy.ToLower())) return account;
            }
            return null;
        }

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

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

仪表板视图模型:

using System.Collections.Generic;
using System.Globalization;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace PapWine
{
    public class DashboardTaskMultipleTilesViewModel : ObservableObject
    {
        private List<DashboardTaskMultipleTileItem> _dashboardTaskMultipleTilesList;

        public DashboardTaskMultipleTilesViewModel()
            : base(listenCultureChanges: true)
        {
            LoadData();
        }

        public List<DashboardTaskMultipleTileItem> DashboardTaskMultipleTilesList
        {
            get { return _dashboardTaskMultipleTilesList; }
            set { SetProperty(ref _dashboardTaskMultipleTilesList, value); }
        }

        protected override void OnCultureChanged(CultureInfo culture)
        {
            LoadData();
        }

        public async Task LogMeOut()
        {
            bool loggedOut = await App.AuthenticationProvider.LogoutAsync();

            if (loggedOut)
            {
                Application.Current.MainPage = new LandingPagePreLogin();
            }
        }

        private void LoadData()
        {
            DashboardTaskMultipleTilesList = new List<DashboardTaskMultipleTileItem> 
            {
                 //1 line
                new DashboardTaskMultipleTileItem
                {
                    Title = "Log Out",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeFont.Lock,
                    IconColour = "White"
                },
                new DashboardTaskMultipleTileItem
                {
                    Title = "User Settings",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeFont.Gear,
                    IconColour = "White"
                },
                 //2 line
                new DashboardTaskMultipleTileItem
                {
                    Title = "User Info",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeFont.User,
                    Badge = 12,
                    IconColour = "White"
                },

                new DashboardTaskMultipleTileItem
                {
                    Title = "Papillon Shop",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeWeb511Font.store,
                    Badge = 2,
                    IconColour = "White"
                },

                //3 line
                new DashboardTaskMultipleTileItem
                {
                    Title = "Check Bottles In",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeFont.Book,
                    IconColour = "White"
                },

                new DashboardTaskMultipleTileItem
                {
                    Title = "Lay Bottles Down",
                    Body = "",
                    Avatar = "",
                    BackgroundColor = "transparent",
                    ShowBackgroundColor = false,
                    IsNotification = false,
                    BackgroundImage = "Tiles/DarkBlackTile.jpg",
                    Icon = FontAwesomeFont.Bed,
                    Badge = 2,
                    IconColour = "White"
                },

            };
        }
    }

    public class DashboardTaskMultipleTileItem
    {
        public string Title { get; set; }
        public string Body { get; set; }
        public string Avatar { get; set; }
        public string BackgroundColor { get; set; }
        public string BackgroundImage { get; set; }
        public bool ShowBackgroundColor { get; set; }
        public bool IsNotification { get; set; }
        public string Icon { get; set; }
        public int Badge { get; set; }
        public string NavigPage { get; set; }


        private Xamarin.Forms.Command _tapCommand;
        public Xamarin.Forms.Command TapCommand
        {                

        get
            {
                if (_tapCommand == null)
                {
                    switch (this.Title) {

                        case "Log Out":
                            App.AuthenticationProvider.LogoutAsync();
                            _tapCommand = new Xamarin.Forms.Command(() =>
                            Xamarin.Forms.Application.Current.MainPage.Navigation.PushModalAsync(new LogoutSuccessPage()));
                            break;
                        case "User Settings":                            
                            _tapCommand = new Xamarin.Forms.Command(() =>
                             Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserSettingsPage()));
                            break;
                        case "User Info":
                            _tapCommand = new Xamarin.Forms.Command(() =>
                            Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new UserProfilePage()));
                            break;
                        case "Papillon Shop":
                            _tapCommand = new Xamarin.Forms.Command(() =>

                            Xamarin.Forms.Application.Current.MainPage.Navigation.PushAsync(new ProductFamilyMultipleTilesPage()));
                            break;
                        case "Check Bottles In":
                            _tapCommand = new Xamarin.Forms.Command(() =>

                            Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
                            break;
                        case "Check Bottles Out":
                            _tapCommand = new Xamarin.Forms.Command(() =>

                            Xamarin.Forms.Application.Current.MainPage = new SignBottlesIn());
                            break;
                    } 

                  }

                return _tapCommand;
            }
        }   
    }    

}

标题项模板:

using Xamarin.Forms;

namespace PapWine
{
    public partial class TaskTilesItemTemplate : TaskTilesItemTemplateBase
    {

        public TaskTilesItemTemplate()
        {
            InitializeComponent();                        
        }

        private async void OnTileTapped(object sender, ItemTappedEventArgs e)
        {
            if (e.Item == null)
                return;
            var content = e.Item as DashboardTaskMultipleTileItem;
            //await Navigation.PushAsync(new LayBottlesDown()); //pass content if you want to pass the clicked item object to another page
        }       
   }

}

标题项目模板库:

using System;
using System.Threading.Tasks;
using Xamarin.Forms;


namespace PapWine
{
    public class TaskTilesItemTemplateBase : ContentView
    {
        public uint animationDuration = 250;
        public bool _processingTag = false;

        public static readonly BindableProperty ShowBackgroundImageProperty =
            BindableProperty.Create(
                nameof(ShowBackgroundImage),
                typeof(bool),
                typeof(TaskTilesItemTemplate),
                true,
                defaultBindingMode: BindingMode.OneWay
            );

        public bool ShowBackgroundImage
        {
            get { return (bool)GetValue(ShowBackgroundImageProperty); }
            set { SetValue(ShowBackgroundImageProperty, value); }
        }

        public static readonly BindableProperty ShowBackgroundColorProperty =
            BindableProperty.Create (
                nameof( ShowBackgroundColor ), 
                typeof ( bool ),
                typeof ( TaskTilesItemTemplate ),
                false,
                defaultBindingMode  : BindingMode.OneWay
            );

        public bool ShowBackgroundColor {
            get { return ( bool )GetValue( ShowBackgroundColorProperty ); }
            set { SetValue ( ShowBackgroundColorProperty, value ); }
        }

        public static readonly BindableProperty ShowiconColoredCircleBackgroundProperty =
            BindableProperty.Create (
                nameof( ShowiconColoredCircleBackground ),
                typeof ( bool ),
                typeof (TaskTilesItemTemplate),
                true,
                defaultBindingMode  : BindingMode.OneWay
            );

        public bool ShowiconColoredCircleBackground {
            get { return ( bool )GetValue( ShowiconColoredCircleBackgroundProperty ); }
            set { SetValue ( ShowiconColoredCircleBackgroundProperty, value ); }
        }

        public static readonly BindableProperty TextColorProperty =
            BindableProperty.Create (
                nameof( TextColor ),
                typeof ( Color ),
                typeof (TaskTilesItemTemplate),
                defaultValue        : Color.White,
                defaultBindingMode  : BindingMode.OneWay
            );

        public Color TextColor {
            get { return ( Color )GetValue( TextColorProperty ); }
            set { SetValue ( TextColorProperty, value ); }
        }
        //added start
        public Color IconColour
        {
            get { return (Color)GetValue(TextColorProperty); }
            set { SetValue(TextColorProperty, value); }
        }
        //added end
        public async void OnWidgetTapped(object sender, EventArgs e)
        {
            if (_processingTag) 
            {
                return;
            }

            _processingTag = true;

            try{
                await AnimateItem (this, animationDuration  );

                await SamplesListFromCategoryPage.NavigateToCategory ((SampleCategory)BindingContext, Navigation);
            }finally{
                _processingTag = false;
            }
        }

        private async Task AnimateItem(View uiElement, uint duration ){
            var originalOpacity = uiElement.Opacity;

            await uiElement.FadeTo(.5, duration/2, Easing.CubicIn);
            await uiElement.FadeTo(originalOpacity, duration/2, Easing.CubicIn);

    }
}
}

LoginAsync看起来像这样:

public async Task<bool> LoginAsync(bool useSilent = false)
        {
            bool success = false;
            //AuthenticationResult authResult = null;

            try
            {
                AuthenticationResult authenticationResult;

                if (useSilent)
                {
                    authenticationResult = await ADB2CClient.AcquireTokenSilentAsync(
                        Constants.Scopes,
                        GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
                        Constants.Authority,
                        false);
                    UpdateUserInfo(authenticationResult);
                }
                else
                {
                    authenticationResult = await ADB2CClient.AcquireTokenAsync(
                        Constants.Scopes,
                        GetAccountByPolicy(await ADB2CClient.GetAccountsAsync(), Constants.PolicySignUpSignIn),
                        App.UiParent);
                }

                if (User == null)
                {
                    var payload = new JObject();
                    if (authenticationResult != null && !string.IsNullOrWhiteSpace(authenticationResult.IdToken))
                    {
                        payload["access_token"] = authenticationResult.IdToken;
                    }

                    User = await TodoItemManager.DefaultManager.CurrentClient.LoginAsync(
                        MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory,
                        payload);
                    success = true;
                }
            }

0 个答案:

没有答案