Flutter: How to handle screens that require authentication? - android

I'm building a Flutter app where some screens can be shown to anonymous users, and other screens require a user to be logged in.
For the authenticated screens, they should automatically navigate to (push) a login screen if the user is not logged in. A user session can expire at any time and if the user is viewing one of these authenticated screens then the login screen should be shown immediately at that time.
In Flutter, how can I achieve this notion of authenticated screens that automatically navigate to/from a login screen when the user is not authenticated?

Currently, there's nothing that flutter do with Authentification and Authentified routes.
The problem is that dart:mirror is disabled, which prevents from doing a more automated solution.
You could try to :
Put Anonymous routes in MaterialApp's routes property
Put Authentified routes in MaterialApp's onGenerateRoute property
And make sure inside onGenerateRoute that the user is logged. If he is, build that route. If not, build the Login route with the original destination passed as parameter (to later redirect to that page)
A code generator may be a good solution too ; although more complex.

There is a really good package auto_route for flutter.
It gives you a lot of power to implement Route Guards for Authenticated routes.
I'm encouring you to use the latest version of the library (on this time I'm writting the ansewer https://pub.dev/packages/auto_route/versions/1.0.0-beta.8 is the latest one)
The documentation is missleading but https://github.com/Milad-Akarie is actively working on that library and respond on any question under Issues tab on GitHub.
There is an example solution: https://github.com/Milad-Akarie/auto_route_library/tree/master/example
AppRouter.dart
...
AutoRoute(path: RoomPage.path, guards: [AuthGuard],page: RoomPage),
...
AuthGuard.dart
class AuthGuard extends AutoRouteGuard {
#override
Future<bool> canNavigate(
List<PageRouteInfo> pendingRoutes, StackRouter router) async {
bool isAuthenticated = await checkUserAccess();
if (!isAuthenticated) {
router.root.push(LoginRoute(onLoginResult: (success) {
if (success) {
router.replaceAll(pendingRoutes);
}
}));
return false;
}
return true;
}
}

Related

(MSAL) Force require password login with Microsoft account - Android Studio

I'm working on an app for android phones to be used by multiple users, where they can log in with google or microsoft accounts, to connect the app info to microsoft teams and/or sharepoint if desired.
I'm coding on Android Studio, using MSAL supporting multiple accounts.
Underneath is a method I have to remove accounts from the current PublicClientApplication.MultipleAccountPublicClientApplication. It also returns the result for each removal, if they were removed or not, in a list of booleans.
When testing, all the accounts are removed successfully, but when signing in again and the microsoft sign in intent is opened, the accounts can just be clicked to sign in without password. Signing out seems kind of pointless because of this, since one can just select their user and be logged in again right away. Is it possible to require or force the Microsoft intent to log in with password?
public CompletableFuture<List<Boolean>> signOutAll() {
List<Boolean> removedList = new ArrayList<>();
CompletableFuture<List<Boolean>> future = new CompletableFuture();
for (IAccount account : accountList) {
mPCA.removeAccount(account,
new IMultipleAccountPublicClientApplication.RemoveAccountCallback() {
#Override
public void onRemoved() {
removedList.add(true);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
#Override
public void onError(#NonNull MsalException exception) {
removedList.add(false);
if (accountList.size() == removedList.size()) {
future.complete(removedList);
}
}
});
}
return future;
}
--
Thank you,
Didrik
This is happening because MSAL automatically refreshes your token after expiration. When user opens your app it checks if that token is already present and valid. So you can remove the token from the Android KeyStore in onStop().
So yes you also need to remove the cache as well to remove the account from the cache, find the account that need to be removed and then call PublicClientApplication.removeAccount()
Set<IAccount> accounts = pca.getAccounts().join();
IAccount accountToBeRemoved = accounts.stream().filter(
x -> x.username().equalsIgnoreCase(
UPN_OF_USER_TO_BE_REMOVED)).findFirst().orElse(null);
pca.removeAccount(accountToBeRemoved).join();
Read more here.
On Android we basically don't have any control on the cookies because they are shared with external Chrome app and because of that it is not accessible. If you want the user to enter the password again then you should do this: AcquireTokenInteractive(scopes).WithPrompt(Prompt.ForceLogin);

Firebase SDK Auth Package Trouble with Automatically Loading Scene Once Authenticated Unity

I am stuck with a functionality of the Firebase SDK (Auth package) regarding the Scenes and it's integration. Here's how they work:
1st: Loading Scene
Here, I just added the FirebaseInit code EXACTLY as suggested by Patrick, which it's only function is to call the next scene (Login/Register) once everything loads correctly.
2nd: Login/Register Scene
Here I do all the Login AND ALSO the register logic. I set up a button that alternates between the two (Activating the respective parent gameObject within the Canvas). Once the user log's in, the 3rd scene comes into play.
3rd: App's Main Screen Scene
Main Screen of the app, where the user can LOGOUT and return to the Login Scene.
Problem
I added the 'LoadSceneWhenUserAuthenticated.cs' in the 2nd Scene, and it works (kind of).
It actually does what it is supposed to. If I log in, quit the game without loging out, and open it again, it does come back directly to the 3rd scene. BUT some things are happening and they aren't supposed to.
First
When I Sign Up a user, I call the method 'CreateUserWithEmailAndPasswordAsync()'. Once it completes, it should activate the login screen and stay there, waiting for the user to fill in the password, but the 'FirebaseAuth.DefaultInstance.StateChanged' comes into play and forces the 3rd screen to be loaded, skipping several other steps that should be taken (email registration for example).
Second
As I mentioned in the end of number 1 above, if I try to log in to an account that does not have it's email verified, it works! (due to the 'LoadSceneWhenUserAuthenticated.cs' which is added in the scene). Code:
var LoginTask = auth.SignInWithEmailAndPasswordAsync(_email, _password);
LoginTask.ContinueWithOnMainThread(task =>
{
if (task.IsCanceled || task.IsFaulted)
{
Firebase.FirebaseException e =
task.Exception.Flatten().InnerExceptions[0] as Firebase.FirebaseException;
GetErrorMessage((AuthError)e.ErrorCode, warningLoginText);
return;
}
if (task.IsCompleted)
{
User = LoginTask.Result;
if (User.IsEmailVerified == true)
{
UIControllerLogin.instance.MainScreenScene();
}
else
{
warningLoginText.text = Lean.Localization.LeanLocalization.GetTranslationText($"Login/VerifyEmail");
}
I know that it's possible to fix this issue by adding an extra scene just before the login scene (as Patrick does in the youtube video) but it doesn't make any sense in my app. It would actually only harm the UX of it.
Patrick's Video:
https://www.youtube.com/watch?v=52yUcKLMKX0&t=264s
I'm glad my video helped!
My architecture won't work for every game, and I tried to boil it down to the bare minimum to get folks started. You may be able to get the functionality you want by adding an additional check in HandleAuthStateChanged:
private void HandleAuthStateChanged(object sender, EventArgs e)
{
if (_auth.CurrentUser != null && !_auth.CurrentUser.IsAnonymous && _auth.CurrentUser.IsEmailVerified)
{
SceneManager.LoadScene(_sceneToLoad);
}
}
but it does sound like, at this point, you'll want to build out a more robust registration/sign in flow that fits your use case.
If you need more help, I might suggest re-posting on the community mailing list or the subreddit. Those resources may be more better suited to discussing various pros/cons of different architectures or spitballing ideas (and feel free to link to any new posts in a comment so myself or others interested can follow along).

How to verify user is authenticated entire app Xamarin Form

I'm new with Xamarin form, my question is how can I check user is logged in for entire app, example every time when users go to a new page, it checks for authent. I tried successfully for every page check for auth but is there any another ways to do it? I did some research on internet and some said that i have to auth on the App.cs on OnStart() but the event is not raise when i go to the next page, it starts only when user open the app.
Here is my code, I'm using Google auth.
On the logged in page (HomePage):
public HomePage(NetworkAuthData networkAuthData)
{
if (string.IsNullOrEmpty(networkAuthData.Id))
{
//Always require user authentication
//Application.Current.MainPage.Navigation.PopAsync();
Application.Current.MainPage = new NavigationPage(new SocialLoginPage(oAuth2Service));
}
else
{
BindingContext = networkAuthData;
InitializeComponent();
}
}
It works but when i move these code to app.cs on OnStart() it just run once when opened the app.

How do you configure local_auth in flutter correctly if you want to secure the whole app with it (e.g. bank apps)?

We have the problem that we want to protect the complete app with a fingerprint/TouchID/FaceID. I will only mention fingerprint here, which always means all biometric features of local_auth. Partly this works quite well with the function "didChangeAppLifecycleState", but only partly.
Since under iOS the app becomes inactive while the fingerprint is being queried, the query comes in a continuous loop when queried at "AppLifecycleState.resumed". Therefore we set a timestamp at "AppLifecycleState.pause" and query the fingerprint at resume only if x seconds have passed since the app was paused. We have now also set this to a slightly higher time, so that the lock does not go on immediately if you have to switch to the camera from the app. Of course there are more checks, but that's the basic idea right now.
Here is the problem that flutter takes some time to execute paused. Both iOS and Android. So if you now quickly minimize the app and open it again (because minimizing was perhaps an accident?), the fingerprint comes directly. Nothing serious, but unpleasant.
Our real problem, which I can't really understand, since I don't know how to debug it either. If the app hasn't been used for a long time, especially on a Monday after the weekend, then work colleagues have the problem that they can simply start the app without a fingerprint. So far only noticed under iOS, since the colleagues use all iOS, but does not exclude that it could happen under Android as well.
I just don't know how to fix it. Has anyone had any experience with this? Or is there a very simple way to use the fingerprint in flutter in a way that you can open the app only with fingerprint, maybe at a completely different place than "didChangeAppLifecycleState"?
Kind regards,
Jakob
As far as I understand you are using lifecycle hook to prompt for biometric verification and you are having issues. The following is the solution if I understand your query right.
First instead of using lifecycle to verify the biometric check. Can you not use initialization widget which gets called every time before opening up the app. Here is an example.
class Initialize extends StatefulWidget {
_InitializeState createState() => _InitializeState();
}
class _InitializeState extends State<Initialize> {
void initState() {
super.initState();
_initialize();
}
Future<void> _initialize() async {
final userDetails = await getUserSavedDetailsFromStroage();
await doAnyOtherTask();
final user = authService.checkIfPrevLoggedIn(authDetails);
if (user == null) {
_gotoLogin();
} else {
if (userDetails.biometricEnabled) {
final verified = await biometricService.onBiometricVerify();
if (!verified) {
_gotoLogin();
return;
}
}
authService.refreshUser();
_gotoHomePage();
}
}
#override
Widget build(BuildContext context) {
return SplashView();
}
}
And your main.dart file will pretty much look like the following, I am assuming you are using Provider package for state management.
#override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (_) => AuthService.instance,
child: MaterialApp(
home: Initialize(),
title: 'App Title',
),
);
}
}
you can set a flag that local auth request is pending.
If you got a response then set the flag back to false with a Futuredelay of 150ms.
Request the local auth in your AppLifecycleState.resumed only if the request is not pending.

How to avoid XMLHttpRequest credentials (NTLM) from being cached in phonegap/cordova?

I've been wondering where (in Android/ iOS) the cookie received from an XMLHttpRequest gets stored...
The situation:
I perform an XHR-request to authenticate. For some reason this starts a kind of a session and all other requests I perform do not need credentials anymore. This situation is wanted, but there is a section in the application where other credentials are needed. When I perform another XHR-request, it does not matter which credentials I use, it will keep using the credentials I entered at first.
What I use:
jQueryMobile
Angular
What I noticed [ANDROID]:
The credentials or the session gets killed on app restart!
(NOT WHEN IN BACKGROUND - Like when backbutton is pressed - IT NEEDS TO BE CLOSED COMPLETELY). So then I have to login again.
What I tried without success:
Adding a param to the URL when I want new credentials to be used.
var xhr = new XMLHttpRequest;
xhr.open('GET', 'test.html?_=' + new Date().getTime());
xhr.send();
The InAppBrowser functionality:
window.open("index.html", "_self",
"location=no,clearsessioncache=yes");
Deleted the applicationCache from the Cordova File-plugin.
A plugin which I thought would help: https://github.com/bez4pieci/Phonegap-Cookies-Plugin
What I want:
I have no clue where to find that session anymore... I really want to know where it's stored. I need a way to delete/ clear it so I can perform another succesful call with other XMLHttpCredentials.
Remember I still want to use the cache functionality the XMLHttpRequest automatically provides.
Any help from experts would be appreciated.
As far as I know, the only way to clear these credentials on Android is by restarting the app. It's possible to do this programmatically if you really need to.
On iOS you can manage saved credentials in the sharedCredentialStorage. For example, to remove all credentials:
NSDictionary* credentialsDict = [[NSURLCredentialStorage sharedCredentialStorage] allCredentials];
for (NSURLProtectionSpace* protectionSpace in credentialsDict){
NSDictionary* userNameDict = credentialsDict[protectionSpace];
for (NSString* userName in userNameDict){
NSURLCredential* credential = userNameDict[userName];
[[NSURLCredentialStorage sharedCredentialStorage] removeCredential:credential forProtectionSpace:protectionSpace];
}
}
In order to access the native APIs, you'll have to build a custom plugin.
The solution I found for Android was not to restart the entire app, but to isolate the login activity in a separate process, then override the onDestroy() method by adding Process.killProcess(Process.myPid()).
In manifest:
<activity
android:name=".Activities.AdfsLoginActivity"
android:noHistory="true"
android:excludeFromRecents="true"
android:process=":adfsLoginProcess"
android:windowSoftInputMode="stateHidden|adjustResize" />
In LoginActivity:
#Override
protected void onDestroy() {
super.onDestroy();
//kills current process - adfsLoginProcess
Process.killProcess(Process.myPid());
}
This solution is not perfect (as I am not a fan of Process.killProcess()), but it seems far better than killing the entire app and scheduling a restart. If anyone has a better solution, please share.

Categories

Resources