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:
- Open Index.html which should display a list of items
- The datasource for this view calls a WebApi method for the list of items, GetItems()
- This GetItems() method checks if the user is authenticated and if not, it throws an "Unauthorized" exception, 401
- A jQuery function catches this 401 and redirects to the login.html page. This is a separate file.
- On the login.html page, another javascript function sends the request to an Account controller which validates the credentials
- If it's valid, then it returns a Success flag to the javascript function
- The login javascript function then navigates back to the index.html page if successful. If not, then it displays a message.
To LogOff:
- The user click a LogOff button which triggers a javascript function
- The javascript function sends a POST request to the Account controller's LogOff method which logs the user off
- 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