This is a migrated thread and some comments may be shown as answers.

Login / LogOff example

9 Answers 3708 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
King Wilder
Top achievements
Rank 2
King Wilder asked on 02 Jan 2013, 12:35 AM
Hi,

Is there a demo application that demonstrates a best practice for detecting unauthenticated users so the application can point to a Login view or external page, and how to handle logging off?

This seems like it should be straight forward, but I'm having problems redirecting to different views properly.

Thanks,

King Wilder

9 Answers, 1 is accepted

Sort by
0
Alexander Valchev
Telerik team
answered on 02 Jan 2013, 03:48 PM
Hi King,

I am afraid that we do not have examples that demonstrates how to implement logging on/off.
Questions about user authentication are not directly related to the functionality and widgets that KendoUI Mobile offers. If you are able to perform logging on/off with standard html components and jQuery but have troubles integrating the approach in your mobile application, please send me a small project that demonstrates the exact issues and I will do by best to help you resolve them.

Regards,
Alexander Valchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
King Wilder
Top achievements
Rank 2
answered on 03 Jan 2013, 07:47 PM
Alexander,

I do plan on answering, I'm using my phone to post this, since I've been without service for two days.

I'm working on a sample app. My question is how to handle login and logoff scenarios with kendo, not authentication with webapi.

Hopefully i'm back online soon.

Thanks,

King Wilder
0
King Wilder
Top achievements
Rank 2
answered on 04 Jan 2013, 05:56 AM
Ok, I'm back online and have built a working sample application.  Here's the sequence of events that I presume to follow to make this work... please correct me if I'm wrong or if there is a better way of handling this.

To Login:
  1. Open Index.html which should display a list of items
  2. The datasource for this view calls a WebApi method for the list of items, GetItems()
  3. This GetItems() method checks if the user is authenticated and if not, it throws an "Unauthorized" exception, 401
  4. A jQuery function catches this 401 and redirects to the login.html page.  This is a separate file.
  5. On the login.html page, another javascript function sends the request to an Account controller which validates the credentials
  6. If it's valid, then it returns a Success flag to the javascript function
  7. The login javascript function then navigates back to the index.html page if successful.  If not, then it displays a message.

To LogOff:

  1. The user click a LogOff button which triggers a javascript function
  2. The javascript function sends a POST request to the Account controller's LogOff method which logs the user off
  3. Then the Success response from the controller is sent back to the javascript function which then navigates to the login.html page

Whew!

While this works, I want to know if this is a reliable method of authenticating with KendoUI?  In other words I want to know if there is some better means to authenticate a user with KendoUI that may be more reliable, or if I'm missing something inherent with KendoUI.

Any thoughts and/or suggestions is appreciated.

A sample application for VS 2012 can be downloaded from my website: http://www.kingwilder.com/downloads/kendouimobileloginlogoff.zip - 12.5 MB

For those who don't have VS 2012 yet, here's some code.

Index.html

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <link href="../Content/kendo/2012.3.1114/kendo.mobile.all.min.css" rel="stylesheet" />
</head>
<body>
    <div data-role="view" id="welcome-view" data-title="Welcome" data-model="itemsViewModel" data-layout="databinding">
        <ul data-role="listview" data-style="inset" data-type="group">
            <li>Welcome!
                <ul>
                    <li>You've logged on successfully!</li>
                </ul>
            </li>
        </ul>
        <ul data-role="listview" id="items-list" data-style="inset" data-template="itemsListViewTemplate" data-model="itemsViewModel" data-bind="source: itemsDataSource">
        </ul>
    </div>
 
    <script type="text/x-kendo-template" id="itemsListViewTemplate">
        #= ItemName #
    </script>
 
    <!-- *************************** Begin Layout Section ****************************************** -->
    <section data-role="layout" data-id="databinding">
        <header data-role="header">
            <div data-role="navbar">
                <a class="nav-button" data-role="backbutton" data-align="left" href="#" data-click="logoff">Log Off</a>
                <span data-role="view-title"></span>
            </div>
        </header>
    </section>
    <!-- *************************** End Layout Section ****************************************** -->
 
    <script src="../Scripts/kendo/2012.3.1114/jquery.min.js" type="text/javascript"></script>
    <script src="../Scripts/kendo/2012.3.1114/kendo.all.min.js" type="text/javascript"></script>
    <script>
        var app = new kendo.mobile.Application(document.body, { transition: "slide" });
    </script>
    <script>
        //Handle unauthorized Ajax errors by redirecting to login
        $(document).ajaxError(function (e, xhr, settings, exception) {
            if (xhr.status == 401) { app.navigate("login.html"); return; }
        });
 
 
        var validator,
            itemsViewModel = kendo.observable({
                itemsDataSource: new kendo.data.DataSource({
                    transport: {
                        read: {
                            url: "/api/dummy/getitems",
                            type: "GET",
                            dataType: "json"
                        },
                        parameterMap: function (options, operation) {
                            return kendo.stringify(options);
                        }
                    },
                    batch: false,
                    pageSize: 10,
                    schema: {
                        data: "Data",
                        model: {
                            id: "Id",
                            fields: {
                                Id: { editable: false, nullable: false, type: "number" },
                                ItemName: { type: "string" }
                            }
                        }
                    }
                })
            });
 
        function logoff() {
            $.post("/fakeaccount/_logoff", function (response) {
                if (response.Success) {
                    app.navigate("login.html");
                }
            });
        }
 
    </script>
</body>
</html>
Here's the login.html.

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <div class="km-modalview km-view" data-role="view" id="modalview-login" style="width: 100%; height: auto;" data-style="inset">
        <div class="km-header" data-role="header">
            <div class="km-navbar" data-role="navbar">
                <div class="km-view-title">
                    <span>Login</span>
                </div>
            </div>
        </div>
        <div class="km-listview-wrapper">
            <ul class="km-listview km-list km-listinset" data-role="listview" data-style="inset">
                <li>
                    <label for="username">Username:</label>
                    <input type="text" id="username" />
                </li>
                <li>
                    <label for="password">Password:</label>
                    <input type="password" id="password" />
                </li>
            </ul>
        </div>
        <div id="login-message"></div>
        <a class="km-button" data-click="authenticateUser" id="modalview-login-button" data-role="button"><span class="km-text">Login</span></a><br />
    </div>
 
    <script>
 
        function authenticateUser() {
            var userName = $("#username").val();
            var password = $("#password").val();
            var $msg = $("#login-message");
 
            $.ajax({
                url: "/fakeaccount/_login",
                data: { UserName: userName, Password: password, RememberMe: false },
                dataType: "json",
                type: "POST",
                success: function (data) {
                    if (data.Success) {
                        $msg.hide();
                        app.navigate("");
                    } else {
                        $msg.html(data.Message).show();
                    }
                },
                error: function (xhr, status, error) {
                    alert(xhr.responseText)
                }
            });
        }
 
    </script>
</body>
</html>
Here's the FakeAccountController which handles logging the user on and off.

using System;
using System.Linq;
using System.Web.Mvc;
using System.Web.Security;
using KendoUIMobileLoginLogoff.Models;
 
namespace KendoUIMobileLoginLogoff.Controllers
{
    /// <summary>
    /// This is a fake Account controller that will login and logoff users.
    /// </summary>
    public class FakeAccountController : Controller
    {
 
 
        #region Login Methods
 
        /// <summary>
        /// Log the user in via the Mobile application.
        /// </summary>
        /// <param name="userName">The userName</param>
        /// <param name="password">The password</param>
        /// <param name="rememberMe">The boolean value for remembering the users username.</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult _login(string userName, string password, bool rememberMe)
        {
            JsonResponse response = new JsonResponse();
            response.Success = false;
            response.Message = "Username or password is invalid.";
 
            if (userName == "admin" && password == "password")
            {
                FormsAuthentication.SetAuthCookie(userName, rememberMe);
                response.Success = true;
                response.Message = "";
            }
 
            return Json(response);
        }
 
        #endregion
 
        #region LogOff Methods
 
        /// <summary>
        /// Log the user off.
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public ActionResult _logoff()
        {
            FormsAuthentication.SignOut();
 
            JsonResponse response = new JsonResponse();
            response.Success = true;
            response.Message = "";
 
            return Json(response);
        }
 
        #endregion
 
    }
}
And here's the Dummy controller which returns the list of items to the index.html view.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.ModelBinding;
using Kendo.Mvc.Extensions;
using Kendo.Mvc.UI;
using KendoUIMobileLoginLogoff.Models;
 
namespace KendoUIMobileLoginLogoff.Controllers.Api
{
    /// <summary>
    /// This dummy Api controller fetches a list of data, but if first
    /// checks to see if the user is authenticated.  If the user is not,
    /// then it throws a 401 "Unauthorized" exception.  This is caught
    /// by the jQuery function in the index.html page and redirects
    /// to the login.html page.
    /// </summary>
    public class DummyController : ApiController
    {
        public DataSourceResult GetItems([ModelBinder(typeof(KendoUIMobileLoginLogoff.ModelBinders.DataSourceRequestModelBinder))]DataSourceRequest request)
        {
            if (!User.Identity.IsAuthenticated)
            {
                throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized));
            }
 
            var items = new List<ItemModel>()
            {
                new ItemModel() { Id = 1, ItemName = "Apple" },
                new ItemModel() { Id = 2, ItemName = "Bicycle" },
                new ItemModel() { Id = 3, ItemName = "Moon" },
                new ItemModel() { Id = 4, ItemName = "Kendo" },
            }.AsQueryable();
 
            return items.ToDataSourceResult(request);
        }
    }
}
I'd love to hear some feedback from the KendoUI team as well as the community.

Thanks,

King Wilder
0
Alexander Valchev
Telerik team
answered on 07 Jan 2013, 04:52 PM
Hi King,

Thank you for sharing this example. As a small sign of our appreciation I updated your Telerik points.

In terms of KendoUI your configuration looks OK.
I noticed one small issue - after clicking the "Log off" button there is a JavaScript error. In order to avoid that you may remove the href attribute of the button element.
<a class="nav-button" data-role="backbutton" data-align="left" data-click="logoff">Log Off</a>


Kind regards,
Alexander Valchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
King Wilder
Top achievements
Rank 2
answered on 07 Jan 2013, 10:49 PM
Alexander,

Thanks for the response and points.

I wanted to ask if there is a better practice between having the Login view as a separate page, or as a view in the same file.  Is there a security benefit of having one over the other?

Also, I looked at the Icenium Airlines demo app and it has a Login feature (but no log off), and it uses a boolean flag to determine whether the user is logged in before displaying the first view.  Is this a better way of detecting whether a user is authenticated, or is my scenario better, by going into the controller action and returning a 401 for unauthorized requests?

I really would like to implement real-world best practices into my mobile apps from the start.  And I am asking this in relationship to how it works with KendoUI.

  • Boolean flag or controller detection?
  • data-role="view" or external view?

Thanks,

King Wilder

0
Alexander Valchev
Telerik team
answered on 09 Jan 2013, 04:50 PM
Hello King,

The right approach depends on the specific requirements of your project and the server side technology that you are using. There are no universally best practices.

There is no security benefit for having the Login view as a separate page. I am not a security expert, but it makes more sense to define the Views that contain restricted information as remote. In this way you would be able to verify on the server whether the user is logged on before sending them.

In case you are interested it best practices guidelines for your particular case you may check our premium services.

Regards,
Alexander Valchev
the Telerik team
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
0
King Wilder
Top achievements
Rank 2
answered on 09 Jan 2013, 09:57 PM
Alexander,

Ok, I understand.  I'll play with it a bit and see if I can find any flaws in my thinking.

If I get really stuck, I might consider your Premium Services.

Thanks,

King Wilder
0
Ryan
Top achievements
Rank 1
answered on 14 Feb 2013, 09:03 PM
I cant seem to get this to work right?  

Specifically, if you get a 401, after you successfully login and app.navigate("") gets called, the application does not make the request to /api/dummy/getitems to pull the data.  How would you make it so that when you get a 401 and login, the resource for which you initially received the 401 gets requested once you login?
0
Piyush Bhatt
Top achievements
Rank 2
answered on 07 Apr 2014, 07:05 PM
Hi King,

Thank you for posting your code - and I have been thinking about same question for a while. Can you tell me what you think about the following approach:
1) The MVC controller will check whether user has logged in or not and uses the RedirectToAction to a Login page if needed
2) The KendoUI pages can be implemented as HTML pages within MVC View
3) Session will determine whether user has logged in or not 

ANOTHER 

This I am not sure - but we can implement HttpModule (or Mvc ActionFilter) to validate for each request if the current user has logged in or not.

Just a thought! 
Tags
General Discussions
Asked by
King Wilder
Top achievements
Rank 2
Answers by
Alexander Valchev
Telerik team
King Wilder
Top achievements
Rank 2
Ryan
Top achievements
Rank 1
Piyush Bhatt
Top achievements
Rank 2
Share this question
or