Navigating from Platform Specific (UWP) PageRenderer and back to PCL Page - xamarin.forms

I have a MainPage in PCL which then navigates to a Platform Specific
Login Page on LoginButton Click Event
The LoginPage is inherited from
platform specific PageRenderer as it needs platform specific
Authentication to Social Providers (Facebook, Google, Twitter,
Microsoft, etc.)
I am using Xamarin.Auth to do the authentication.
Inside the OnElementChanged event of the LoginPage, it instantiates
the OAuth2Authenticator object.
Upon successful instantiation (based
on provider and app details), it needs to call the UI of the specific
provider.
To do that, I call the auth.GetUI where auth is
Xamarin.Auth.OAuth2Authenticator object.
I have two questions:
In UWP, how do I navigate to the provider login UI? More specifically, what is the equivalent in UWP of the following code snippets in iOS and Android? In iOS, the following code is used:
PresentViewController(auth.GetUI(), true, null);
where auth is Xamarin.Auth.OAuth2Authenticator object.
In Android the following is used:
activity.StartActivity(auth.GetUI(activity));
I am looking for the equivalent code in UWP. Please bear in mind that these calls are made from the LoginPage which is derived from Platform specific PageRenderer
How do I navigate back to my MainPage (which is in PCL) upon successful authentication?
The code is based off of a sample from the following source:
http://www.c-sharpcorner.com/article/oauth-login-authenticating-with-identity-provider-in-xamarin-forms/
Here is my code for the LoginPage:
using System;
using Valufy;
using Xamarin.Forms.Platform.UWP;
using System.ComponentModel;
using Valufy.UWP;
using Valufy.AuthConfiguration;
using Xamarin.Forms;
[assembly: ExportRenderer(typeof(ProviderLoginPage), typeof(LoginRenderer))]
namespace Valufy.UWP
{
class LoginRenderer: PageRenderer
{
protected override void OnElementChanged (ElementChangedEventArgs<Xamarin.Forms.Page> e)
{
base.OnElementChanged(e);
//Get and Assign ProviderName from ProviderLoginPage
ProviderLoginPage loginPage = (ProviderLoginPage)Element;
string providername = loginPage.ProviderName;
//Create OauthProviderSetting class with Oauth Implementation .Refer Step 6
OAuthProviderSetting oauth = new OAuthProviderSetting();
Xamarin.Auth.OAuth2Authenticator auth = oauth.LoginWithProvider(providername);
// After facebook,google and all identity provider login completed
auth.Completed += Auth_Completed;
Type page_type = auth.GetUI();
//////THIS IS WHERE I AM STUCK...HOW DO I GO TO THE PROVIDER AUTH UI ////////////
//this.Frame.Navigate(page_type, auth);
//parentPage.Navigation.PushModalAsync(auth.GetUI());
}
}
private void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
OAuthConfig.User = new UserDetails();
// Get and Save User Details
OAuthConfig.User.Token = e.Account.Properties["oauth_token"];
OAuthConfig.User.TokenSecret = e.Account.Properties["oauth_token_secret"];
OAuthConfig.User.TwitterId = e.Account.Properties["user_id"];
OAuthConfig.User.ScreenName = e.Account.Properties["screen_name"];
/////NOW, HOW GO I GO BACK TO THE CALLING PAGE IN PCL ///////////////////
}
else
{
// The user cancelled
/////NOW, HOW GO I GO BACK TO THE CALLING PAGE IN PCL ///////////////////
}
}
}
}

Here is the code to navigate to the provider login for UWP:
WindowsPage windowsPage = new WindowsPage();
_frame = windowsPage.Frame;
if (_frame == null)
{
_frame = new Windows.UI.Xaml.Controls.Frame
{
Language = global::Windows.Globalization.ApplicationLanguages.Languages[0]
};
windowsPage.Content = _frame;
SetNativeControl(windowsPage);
}
Type pageType = auth.GetUI();
_frame.Navigate(pageType, auth);
To navigate back to my page upon successful authentication, here is the code:
private async void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
if (e.IsAuthenticated)
{
var request = new OAuth2Request("GET", new Uri("https://login.microsoftonline.com/common/oauth2/V2.0/token?oauth2_access_token=" + e.Account.Properties["access_token"]), null, e.Account);
try
{
string response = await MSGetUserInfo(e.Account);
}
catch (System.OperationCanceledException)
{
}
catch (Exception ex)
{
}
this.Element.Navigation.PushModalAsync(new MainPage());
}
else
{
// The user cancelled
}
}

1- In UWP, how do I navigate to the provider login UI
You need to create a UWP Page object, the same object you are will use to display in the renderer is the one you will use to do the navigation.
Type page_type = auth.GetUI();
page = new MyUWPLoginPage();
page.Frame.Navigate(page_type, auth);
2- How do I navigate back to my MainPage (which is in PCL) upon successful authentication?
There are many ways to do this, the easiest one is creating a public method in your ProviderLoginPage class and from the renderer classes call this method and pass-in the parameters.
public class ProviderLoginPage: ContentPage
{
......
public void AuthenticationCompleted(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
// Do your logic
}
}
In your Renderer using the Element:
private void Auth_Completed(object sender, Xamarin.Auth.AuthenticatorCompletedEventArgs e)
{
var xamElement = Element as ProviderLogin;
xamElement?.AuthenticationCompleted(sender, e);
}
Move all the logic to the PCL class so you don't have to repeat it on each renderer.
This should help.

Related

xamarin.forms authorization without 3rd party libraries

I'm trying to make authorization in xamarin forms and i dont know how to structure it. I'm using MVVM and for my authorization i wanna use JWT . I want to check if the token is valid then go to certain page . when i put the validation code inside the onappearing method the page is still visible for a very small amount of time and when i put it inside the constructor of the page the navigation doesn't work. How should this authorization should be done?(should i make another transition page with something like an activity indicator ?)
this is the code i use for the token validation
public async Task CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
}
}
when i put it inside the constructor of the page the navigation
doesn't work.
You can call the push service like this in Main thread:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new myViewModel();
CheckIfUserIsLoggedIn();
}
public async void CheckIfUserIsLoggedIn()
{
if (!await ValidateToken())
{
Device.BeginInvokeOnMainThread(async () => {
await _dependencyService.Get<INavigationService>().PushAsync(ViewNames.LOGINVIEW);
});
}
}
}

Xamarin Forms - How to get Android.Support.V4.App.Fragment Manager from View Renderer in Android project?

Xamarin Forms - I have created one adaptive card custom renderer in my android project. In order to call the adaptive card renderer function, I need to pass in Fragment Manager. How can I get fragment manager from View Renderer?
In Xamarin.Android project, I can access fragment manager from fragment class. But Xamarin.Forms I have no idea of how to doing that.
[assembly: ExportRenderer(typeof(BaseTemplate.CustomViews.AdaptiveCardLayout), typeof(BaseTemplate.Droid.Renderers.DroidAdaptiveCardLayoutRenderer))]
namespace BaseTemplate.Droid.Renderers
{
public class DroidAdaptiveCardLayoutRenderer : ViewRenderer<AdaptiveCardLayout, Android.Views.View>, ICardActionHandler
{
public DroidAdaptiveCardLayoutRenderer(Context context) : base(context)
{
}
public void OnAction(BaseActionElement p0, RenderedAdaptiveCard p1)
{
throw new NotImplementedException();
}
public void OnMediaPlay(BaseCardElement p0, RenderedAdaptiveCard p1)
{
throw new NotImplementedException();
}
public void OnMediaStop(BaseCardElement p0, RenderedAdaptiveCard p1)
{
throw new NotImplementedException();
}
protected override void OnElementChanged(ElementChangedEventArgs<AdaptiveCardLayout> e)
{
CardRendererImplementation cardRenderer = new CardRendererImplementation();
ICardActionHandler cardActionHandler;
Context context = Android.App.Application.Context;
cardRenderer.Context = context;
//how to get fragment manager here?
}
}
}
You can use the Context to cast into activity. As your DroidAdaptiveCardLayoutRenderer have the default constructor with the context as a parameter.
Try this Code:
var activity = Context as Activity;
activity.FragmentManager.BeginTransaction().Replace(Resource.Id.container, BasicFragment.NewInstance()).Commit();
For V4 use:
using Android.Support.V7.App;
activity = Context as AppCompatActivity;
activity.SupportFragmentManager.BeginTransaction().Replace(Resource.Id.container, BasicFragment.NewInstance()).Commit();
That is quite easy actually!
You should be using CurrentActivity plugin setup here
Once you are done with this use the Activity property
var appcompatActivity= CrossCurrentActivity.Current.Activity as AppCompatActivity;
var mFragManager= appcompatActivity.SupportFragmentManager;

Passing the search term from SearchHandler to ContentPage in Xamarin Forms 4

I'm trying to make use of the new SearchHandler implemented as part of Xamarin Forms 4. I've found it pretty easy so far to get suggestions populated but now I want to raise an event, or follow the suggested method of handling when a search is confirmed.
public class FoodSearchHandler: SearchHandler
{
IFoodDataStore dataStore = new FoodDataStore();
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
// What to do here?
}
protected override void OnQueryChanged(string oldValue, string newValue)
{
base.OnQueryChanged(oldValue, newValue);
if(!string.IsNullOrWhiteSpace(newValue)
{
// Populate suggestions
ItemsSource = dataStore.GetSuggestions(newValue);
}
else
{
ItemsSource = null;
}
}
}
public partial class FoodsPage : ContentPage
{
ObservableCollection<Food> Foods = new ObservableCollection<Food>();
public ItemsPage()
{
InitializeComponent();
// Wire up the search handler
Shell.SetSearchHandler(this, new FoodSearchHandler());
BindingContext = this;
}
}
Unfortunately, althought the alpha docs mention the search handler they don't contain any details on how to use it and the sample apps only demonstrate populating the suggestions.
Does anyone out there have a pointer to offer on how I should be notifying my ContentPage that my SearchHandler confirmed a search?
So, after reading the Shell docs some more, it seems what I want to do in this situation is use of Shell's new Navigation and navigate to a route passing the search text as a query, for example:
protected override void OnQueryConfirmed()
{
base.OnQueryConfirmed();
var shell = Application.Current.MainPage as Shell;
shell.GoToAsync($"app:///fructika/search?query={Query}", true);
}
N.B. It doesn't look like passing data works right now or if it does I'm doing it wrong but I'll raise a separate question about that.

Save & Restore Page in Xamarin Forms

I'm looking to save the current navigation stack on the OnSleep Event in my Xamarin Forms page and restore it on the OnResume Event. Is it possible to do this?
Cheers!
I think you should not memorize all navigation stack. Your device decide to kill your app or to restart from the last page you have seen when it comes up from background. I think you can memorize if you are "Logged in" or not: if you are "Logged in" you can restart from the first page "after the login", otherwise start "from the login".
For this case you can take a look to this link and use Properties
public class App : Xamarin.Forms.Application
{
public App ()
{
}
protected override void OnStart()
{
// Handle when your app starts
Debug.WriteLine ("OnStart");
checkLogin();
}
protected override void OnSleep()
{
// Handle when your app sleeps
Debug.WriteLine ("OnSleep");
}
protected override void OnResume()
{
// Handle when your app resumes
Debug.WriteLine ("OnResume");
checkLogin();
}
}
void checkLogin(){
if (Application.Current.Properties.ContainsKey("IsLogged"))
{
var IsLogged = Application.Current.Properties ["IsLogged"] as bool;
// do something with IsLogged
if(IsLogged)
MainPage = new MyFirstPage();
else
MainPage = new MyLoginPage();
}
else
MainPage = new MyLoginPage();
}
then, when you have logged in
Application.Current.Properties ["IsLogged"] = true;

Umbraco :: Catching or Extending User Login

I am trying to catch the login event of Umbraco Users (login in the CMS).
I have tried to extend from MembersMembershipProvider and override the ValidateUser method. I also changed the web.config to use my class.
When i put a breakpoint in this overrited method it doesnt stop and logsin the user as usual.
public class CustomUmbracoMembershipProvider : Umbraco.Web.Security.Providers.UsersMembershipProvider
{
public override bool ValidateUser(string username, string password)
{
return base.ValidateUser(username, password);
}
}
Thanks in advance.
As Jannik said, Umbraco uses ASP.NET Identity in the latest couple of versions, so the MembershipProvider is not used to validate and authenticate the user anymore.
So after a couple hours of research, i found a workaround solution:
1º - Create a custom UserManager, and override the method CheckPasswordAsync:
public class CustomBackOfficeUserManager : BackOfficeUserManager
{
public CustomBackOfficeUserManager(
IUserStore<BackOfficeIdentityUser, int> store,
IdentityFactoryOptions<BackOfficeUserManager> options,
MembershipProviderBase membershipProvider) :
base(store, options, membershipProvider)
{
}
/// <summary>
/// Returns true if the password is valid for the user
/// </summary>
/// <param name="user"/><param name="password"/>
/// <returns/>
public override Task<bool> CheckPasswordAsync(BackOfficeIdentityUser user, string password)
{
//Your implementation
var result = base.CheckPasswordAsync(user, password).Result;
return Task.FromResult(result);
}
}
2º - Then in your owin startup, you can use this block of code for your ConfigureUserManagerForUmbracoBackOffice:
var appCtx = ApplicationContext;
app.ConfigureUserManagerForUmbracoBackOffice<BackOfficeUserManager, BackOfficeIdentityUser>(
appCtx,
(options, context) =>
{
var membershipProvider = MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider();
var store = new BackOfficeUserStore(
appCtx.Services.UserService,
appCtx.Services.ExternalLoginService,
membershipProvider);
return new CustomBackOfficeUserManager(store, options, membershipProvider);
});
This solution ill keep using the umbraco UserStore and the base methods of UserManager.
I have a similar situation. This worked for me in Umbraco 7.6.5. I know it's not elegant. It's only a workaround:
public class MyCustomEvents: ApplicationEventHandler
{
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
base.ApplicationStarted(umbracoApplication, applicationContext);
UserService.SavedUser += UserServiceSaved;
}
private void UserServiceSaved(IUserService sender, SaveEventArgs<IUser> e)
{
foreach(IUser user in e.SavedEntities)
{
if (!user.IsNewEntity()) //Is not creating a new user
{
IUser alreadyLoggedUser = UmbracoContext.Current.Security.CurrentUser;
if (alreadyLoggedUser == null) //Is not a user management via backoffice
{
if (user.FailedPasswordAttempts == 0) //Is a successful login?
{
DateTime justNow = DateTime.Now.AddSeconds(-5);
if (user.LastLoginDate.CompareTo(justNow) >= 0) //Logged in just now?
{
//Do your stuff
}
}
}
}
}
}
}
As the Umbraco backend uses ASP.NET Identity in the latest couple of versions, it may also be relevant to ask which exact version you are using.
I think ASP.NET Identity has an OnLoggedIn event you could try: https://msdn.microsoft.com/en-us/library/system.web.ui.webcontrols.login.onloggedin.aspx
But I'm not 100% sure the link is current.

Resources