Windows Phone MVVM Login Page design pattern? - windows-phone-7

I want to create a login page where the users enters username/password then a web service authenticates and saves an authentication token retrieved from the server.
I want the page view to be notified when the authentication is done successfully.
my question is: how to implement this in MVVM pattern ? I created a class for the model, a class for the model view and a class for the calling and parsing of the web service.
I can't set my ModelView as a DataContext for the page cause there are no controls that bind to the Model's data.
is this pattern an overkill or it can be implemented in another way ? please suggest.
Thanks

I have a login page that is implemented as described here. The login page itself does not have a viewmodel, but it does use a service that I wrote that contains a callback when the login completes. the service also contains other useful info about the user. I think MVVM would have been overkill here.
private void LoginButton_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(EmailTextBox.Text)) return;
if (string.IsNullOrEmpty(PasswordTextBox.Password)) return;
Login();
}
private void Login()
{
if (DeviceNetworkInformation.IsNetworkAvailable == false)
{
MessageBox.Show("I'm having trouble connecting to the internet." + Environment.NewLine + "Make sure you have cell service or are connected to WiFi then try again");
}
else
{
LoginButton.Focus(); // Removes the keyboard
UserProfile.Name = EmailTextBox.Text;
UserProfile.Password = PasswordTextBox.Password;
UserProfile.Current.Login(result =>
{
// callback could be on another thread
Dispatcher.BeginInvoke(() =>
{
// Did the login succeed?
if (result.Result)
{
NavigationService.Navigate(new Uri("/MainPage.xaml", UriKind.Relative));
}
else
{
string message = "Sorry, but I was not able to log in that user. Please make sure the name and password were entered correctly.";
MessageBox.Show(message, "Login failed");
}
});
});
}
}

You need to put ICommands in your ViewModel that point to methods who perform calls your web service, and the elements in your View should bind to those commands to perform actions.
And you need one more boolean property in your viewmodel: IsLoggedIn, that you set to true when the Login call to your webservice returns a success.
Then in your view, you can bind to IsLoggedIn to give feedback to your users.
Note: don't forget to raise PropertyChanged for IsLoggedIn in its setter.

Related

What is the best solution for "reseting" the Authentication stack?

Currently in my application I have two Navigation stacks.
Authentication
Main
My Authentication stack looks like this:
Splash Page
Choose Create or Login Page
Login Page
After that I call:
CoreMethods.SwitchOutRootNavigation(NavigationContext.Main);
This all works fine.
When I call Logout from within the Main stack like this:
CoreMethods.SwitchOutRootNavigation(NavigationContext.Authentication);
I will currently be on "Login Page", but I really want it to be the first page "Splash Page".
Having the Navigation stacks remember the stack history is perfect for all other cases.
Question: What is the best solution for "reseting" the Authentication stack?
What I normally do in my apps is following.
I have IAuthenticationService which has a State property, which can be LoggedIn or LoggedOut. When session state changed due to explicit login, or for instance token expires, I set the State to LoggedOut. Also I fire a broadcast message SessionStateChanged through Messenger, so I can catch this message all around the app, and react correspondingly in UI level, like change screen states and so on.
If need to completely log the user, I mean show login page when State is LoggedOut, which is your case, I do the following. I use Xamarin.Forms, but the approach would be similar if you use native iOS or Android.
In my main App class (the one which derives from Xamarin.Forms.Application) I create a method call UpdateMainPage, something like this
private async void UpdateMainPage()
{
if (_authService.State == SessionState.LoggedIn)
MainPage = new NavigationPage(new RequestPage());
else
MainPage = new NavigationPage(new SignInPage());
}
What happens I just change the root page of the application to SignIn flow or Main flow depending on SessionState. Then in my constructor I do the following.
public FormsApp()
{
InitializeComponent();
_authService = Mvx.Resolve<IAuthenticationService>();
UpdateMainPage();
var messenger = Mvx.Resolve<IMvxMessenger>();
_sessionStateChangedToken = messenger.Subscribe<SessionStateChangedMessage>(HandleSessionStateChanged);
}
What I need to do, I need to setup main page beforehand, then I subscribe to SessionStateChanged event, where I trigger UpdateMainPage
private void HandleSessionStateChanged(SessionStateChangedMessage sessionStateChangedMessage)
{
UpdateMainPage();
}
I used this approach for several apps, and it work perfect for me. Hope this helps
I had the very same problem recently and this is what I did:
Navigation stacks:
public enum NavigationStacks {Authentication, Main}
In the App.xaml.cs:
//Navigation stack when user is authenticated.
var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());
//Navigation stack for when user is not authenticated.
var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());
here you can leverage James Montemagno's Settings Plugin
if (Settings.IsUserLoggedIn)
{
MainPage = mainNavigation;
}
else
{
MainPage = authenticationNavigation;
}
So far you had already done the code above. But the idea for the problem is to clear the authentication stack except the root page i.e splash Screen:
public static void PopToStackRoot(NavigationStacks navigationStack)
{
switch (navigationStack)
{
case NavigationStacks.Authentication:
{
var mainPage = FreshPageModelResolver.ResolvePageModel<MainPageModel>();
var mainNavigation = new FreshNavigationContainer(MainPage, NavigationStacks.Main.ToString());
break;
}
case NavigationStacks.Main:
{
var splashScreenPage= FreshPageModelResolver.ResolvePageModel<SplashScreenPageModel>();
var authenticationNavigation = new FreshNavigationContainer(splashScreenPage, NavigationStacks.Authentication.ToString());
break;
}
}
}
And finally here is the code inside Logout command:
private void Logout()
{
Settings.IsUserLoggedIn = false;
NavigationService.PopToStackRoot(NavigationStacks.Authentication);
CoreMethods.SwitchOutRootNavigation(NavigationStacks.Authentication.ToString());
}
I know there may be better and more efficient approaches. But that worked for me.

Display message to user on expired session when using wicket-auth-roles

Hi I have been unable to solve the following problem in Wicket 6.*:
In our webapp we are using wicket-auth-roles to manage authentication/authorization. When session expires, user should be redirected to a page set by getApplicationSettings().setPageExpiredErrorPage(SomePage.class) on his next action. However, if the user tries to access a page which doesn't allow guests, he is redirected to a login page skipping the PageExpiredPage altogether.
My question is - how can I display "Session has expired." message to the user?
Among other things, I have tried session.info("message") during onInvalidate phase of session's lifecycle, however the feedback message is then rendered on the first page after login (not on the login page).
Thank you for your anwsers.
You could use a RequestCycleListener to record when a PageExpiredException is thrown.
public class ExceptionMapperListener extends AbstractRequestCycleListener {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (ex instanceof PageExpiredException) {
// Record in session or request cycle
// OR
// Create a RenderPageRequestHandler yourself and add a page parameter
// See DefaultExceptionMapper#internalMap(Exception)
}
return null;
}
}
// In Application#init():
getRequestCycleListeners().add(new ExceptionMapperListener());
ORINAL ANSWER
(kept because it could still help...)
I haven't tried it myself since I don't use wicket-auth-roles, but try overriding the method AuthenticatedWebApplication#restartResponseAtSignInPage() with something like this:
if (isSessionExpired()) {
PageParameters params = new PageParameters();
params.add("showSessionExpired", true);
throw new RestartResponseAtInterceptPageException(getSignInPageClass(), params);
} else {
throw new RestartResponseAtInterceptPageException(getSignInPageClass());
}
And then in the SignInPageClass, display the desired message if the showSessionExpired page parameter is present.
I'm not sure how you implement isSessionExpired(), but you seem to have that part already covered.
OR
Depending on how you implemented isSessionExpired(), maybe you could do the following in your SignInPageClass:
if (sessionExpired()) {
session.info("message")
}
After bernie put me on the right path, I eventually figured out a solution to the problem:
First it is required to override RequestCycleListener:
public class SessionExpiredListener extends AbstractRequestCycleListener {
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) {
if (handler instanceof IPageRequestHandler) {
IPageRequestHandler pageHandler = (IPageRequestHandler) handler;
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
//check whether the requested session has expired
boolean expired = request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
//check whether the requested page can be instantiated with the current session
boolean authorized = Session.get().getAuthorizationStrategy().isInstantiationAuthorized(pageHandler.getPageClass());
if (expired && !authorized) {
throw new PageExpiredException("Session has expired!");
}
}
super.onRequestHandlerResolved(cycle, handler);
}
}
Check for authorized prevents the session-expired message from displaying on log-out or when accessing unprotected pages.
Finally, you must register your listener and PageRequestHandlerTracker in your WebApplication:
getRequestCycleListeners().add(new SessionExpiredListener());
getRequestCycleListeners().add(new PageRequestHandlerTracker());

display message on LogOn page when session time out in asp.net mvc3

i want to display session time out message on my LogOn page.
i have override asp.net mvc Authorize attribute to naviagate the user to logOn page on session timeOut but my problem is that i am not able to set ViewData or ViewBag or Session["Message"]
kind of thing inside that override method.
i would request you all to please help me ...
how to set ViewData inside that Override method.
below is my CustomAuthorizeAttribute class
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.HttpContext.User.Identity.IsAuthenticated || SessionCache.User_ID == 0)
{
// User not logged in, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
}
please suggest.
thanks,
You want to look into using Session_End event in your Global.asax.
It's important to realize that Session_End will only be called on InProc sessions.

How to show different pages when app launches time in windows phone 7?

When app launches time need to show the registration page.once user registered it shouldn't goes to registration page need to go log in page.
How to achieve this?
You can navigate to the start page of a Windows Phone app from code.
Remove the "DefaultTask" entry from the WMAppManifest
Remove the NavigationPage attribute from the "DefaultTask" in WMAppManifest, and in the Launching event of your app use the something like the example below to navigate to the page of choice upon launch.
private void Application_Launching(object sender, LaunchingEventArgs e)
{
if (registered)
{
((App)Application.Current).RootFrame.Navigate(new Uri("/<your start page>.xaml", UriKind.Relative));
}
else
{
((App)Application.Current).RootFrame.Navigate(new Uri("/<your registration page>.xaml", UriKind.Relative));
}
}
You just have to decide how you want to determine that someone already registered.
I guess you haven't put a lot of thought to this, the setup is pretty easy! When a user registers you could set a variable in the settings defining that a user already has registered. When the application starts, evaluate this setting and if the user registered you show the register-page, otherwise the login-page. Example:
//After (succesful) registration
Properties.Settings.Default.HasRegistered = true;
Properties.Settings.Default.Save();
//Check the value
var hasRegistered = Properties.Settings.Default.HasRegistered;
if(hasRegistered)
//show Login
else
//show Registration
You can also use the IsolatedStorageSettings.ApplcationSettings to do this. The code below is just sample code, you'll have to provide validation if the settings already exist on the first startup of the app and set a default value 'false' for the setting if no registration has occured yet.
//After registration
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("HasRegistered"))
settings["HasRegistered"] = true;
settings.Save();
//Check value
var settings = IsolatedStorageSettings.ApplicationSettings;
if (settings.Contains("HasRegistered"))
{
var registered = bool.Parse(settings["HasRegistered"]);
if(registered)
//show login
else
//show registration
}
Hope this helps!

How can I show Authenticated but UNAUTHORIZED users an unauthorized page MVC 3?

I have an application where some users belong to a Role, but may not actually have access to certain data within a URL. For instance the following url is open to all users
/Library/GetFile/1
However, some users may not have access to file1, but I can't use the Authorize attribute to detect that. I want instead to redirect those users to an unauthorized or accessdenied page. I'm using Forms Authentication and my config is set up like this
<authentication mode="Forms">
<forms loginUrl="~/Home/Index" timeout="2880" />
</authentication>
my custom errors block is like this
<customErrors mode="On" defaultRedirect="Error" redirectMode="ResponseRewrite" >
<error statusCode="401" redirect="Unauthorized"/>
</customErrors>
I am attempting to return the HttpUnauthorizedResult if the user does not have access, but I just get redirected to the login page, which isn't valid here because the User is Authenticated already.
It appears that the HttpUnauthorizedResult is setting the HTTP Response Code to 401 which Forms Authentication is hijacking and sending the user to the Login page.
Throwing the UnauthorizedAccessException doesn't seem to work either always redirecting the user to an IIS Error page even though I've updated my RegisterGlobalFilters to
filters.Add(new HandleErrorAttribute
{
ExceptionType = typeof(UnauthorizedAccessException),
View = "Unauthorized",
Order = 3
});
If I change UnauthorizedAccessException to a custom Exception the redirect works and for now that's what I've done.
Your solution is similar to mine except that I did this:
Create a custom exception, UnauthorizedDataAccessException.
Create a custom exception filter (so that it could log the invalid access attempt).
Register my custom exception attribute as a global filter in App_start.
Create a marker interface, ISecureOwner and added it to my entity.
Add a secure 'Load' extension method to my repository, which throws the exception if the current user is not the owner of the entity that was loaded. For this to work, entity has to implement ISecureOwner that returns the id of the user that saved the entity.
Note that this just shows a pattern: the details of how you implement GetSecureUser and what you use to retrieve data will vary. However, although this pattern is okay for a small app, it is a bit of hack, since that kind of security should be implemented deep down at the data level, using ownership groups in the database, which is another question :)
public class UnauthorizedDataAccessException : Exception
{
// constructors
}
public class UnauthorizedDataAccessAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() == Typeof(UnauthorizedDataAccessException))
{
// log error
filterContext.ExceptionHandled = true;
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "UnauthorizedDataAccess" }));
}
else
{
base.OnException(filterContext);
}
}
// marker interface for entity and extension method
public interface ISecureOwner
{
Guid OwnerId { get; }
}
// extension method
public static T SecureFindOne<T>(this IRepository repository, Guid id) where T : class, ISecureOwner, new()
{
var user = GetSecureUser();
T entity = repository.FindOne<T>(id);
if (entity.OwnerId != user.GuidDatabaseId)
{
throw new UnauthorizedDataAccessException(string.Format("User id '{0}' attempted to access entity type {1}, id {2} but was not the owner. The real owner id is {3}.", user.GuidDatabaseId, typeof(T).Name, id, entity.OwnerId));
}
return entity;
}
// Register in global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var filter = new UnauthorizedDataAccessAttribute { ExceptionType = typeof(UnauthorizedDataAccessException) };
filters.Add(filter);
filters.Add(new HandleErrorAttribute());
}
// Usage:
var ownedThing = myRepository.SecureFindOne<myEntity>(id))
You can restrict access to certain roles. If an unauthorized role tries to access a resource you can redirect them to a specific url.
Look at this other SO question: attribute-for-net-mvc-controller-action-method, there are good answers there.
You can check in your code if a user belongs to a role:
User.IsInRole("RoleToTest");
you can also apply attributes to your controllers/action methods. Anyhow it is all explained in the link I specified above.
* EDIT *
You could override OnException in your base Controller. Implement a custom exception, e.g., AccessNotAuthorizedAccessException.
In OnExcepton, if you detect your custom exception, just redirect to a friendly url that shows the 'Not authorized...' message.

Resources