You are here:   Blog
Register   |  Login

Nov 1

Written by: Michael Washington
11/1/2013 6:23 PM  RssIcon

image

Creating a LightSwitch website and setting security is easy. However, you must set up all your users manually. In some cases you want to allow users to self-register.

LightSwitch uses the standard ASP.NET Membership Provider. You can integrate it with any security, (see: Integrating LightSwitch Into An ASPNET Application To Provide Single Sign On). However, if you only require a self-registration page, it is simple to set-up.

This topic has been covered in the article: Allowing Users To Self Register In Your LightSwitch Website. However, that article uses Web Forms (this article uses MVC) and does not allow users to change their passwords. Also, this article provides a landing page for the root of your LightSwitch application.

image

First, we start with the code from the article: Using MVC With Visual Studio LightSwitch.

Turn On Forms Authentication

image

Go into Properties and select Use Forms authentication.

image

This will cause LightSwitch to create LogIn and LogOut pages. These pages will redirect to the LightSwitch application.

If you have other non-LightSwitch pages, for example public pages for your website, you will want to use the pages we are about to create.

Add The Model

image

Add a class called UserDTO.cs to the Models folder and use the following code:

 

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace LightSwitchApplication.Models
{
    public class UserDTO
    {
        public string UserName { get; set; }
        public string Email { get; set; }
        public string Password { get; set; }
        public bool IsLockedOut { get; set; }
        public bool IsOnline { get; set; }
        public bool RememberMe { get; set; }
    }
    public class ChangePasswordDTO
    {
        [Display(Name = "Old Password")]
        public string OldPassword { get; set; }
        [Display(Name = "New Password")]
        public string NewPassword { get; set; }
        [Display(Name = "Confirm Password")]
        public string ConfirmPassword { get; set; }
    }
}

 

Add The Views

image

Create a folder under the Views folder and call it Account.

Right-click on the Account folder and select Add then MVC 5 View Page (Razor).

image

Repeat the process to create:

  • ChangePassword.cshtml
  • Login.cshtml
  • Register.cshtml

 

Use the following code for each page:

 

ChangePassword.cshtml

 

@{
    Layout = null;
}
@model LightSwitchApplication.Models.ChangePasswordDTO
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>Change Password</title>
</head>
<body>
    <h2>Change Password</h2>
    <div>
        @using (Html.BeginForm("ChangePassword", "Account",
            FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
        {
            @Html.AntiForgeryToken()
            @Html.ValidationSummary()
            <div class="form-group">
                @Html.LabelFor(model => model.OldPassword,
                new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.PasswordFor(model => model.OldPassword,
                    new { @class = "form-control" })
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(model => model.NewPassword,
                new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.PasswordFor(model => model.NewPassword,
                    new { @class = "form-control" })
                </div>
            </div>
            <div class="form-group">
                @Html.LabelFor(model => model.ConfirmPassword,
                new { @class = "col-md-2 control-label" })
                <div class="col-md-10">
                    @Html.PasswordFor(model => model.ConfirmPassword,
                    new { @class = "form-control" })
                </div>
            </div>
            <br />
            <div class="form-group">
                <div class="col-md-offset-2 col-md-10">
                    <input type="submit"
                           value="Change password"
                           class="btn btn-default" />
                </div>
            </div>
        }
        <br />
        <div>
            @Html.ActionLink("Back", "Index", "Home")
        </div>
    </div>
</body>
</html>

 

 

Login.cshtml

 

@{
    Layout = null;
}
@model LightSwitchApplication.Models.UserDTO
<!DOCTYPE html>
<html>
<head>
    <meta name="HandheldFriendly" content="true" />
    <meta name="viewport" content="width=device-width, initial-scale=1,
          minimum-scale=1, maximum-scale=1, user-scalable=no" />
    <title>Log in</title>
</head>
<body>
    <h2>Log In</h2>
    <div class="row">
        <div class="col-md-8">
            <section id="loginForm">
                @using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl },
                FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
                {
                    @Html.AntiForgeryToken()
                    @Html.ValidationSummary(true)
                    <div class="form-group">
                        @Html.LabelFor(model => model.UserName, new { @class = "col-md-2 control-label" })
                        <div class="col-md-10">
                            @Html.TextBoxFor(model => model.UserName, new { @class = "form-control" })
                            @Html.ValidationMessageFor(model => model.UserName)
                        </div>
                    </div>
                    <div class="form-group">
                        @Html.LabelFor(model => model.Password, new { @class = "col-md-2 control-label" })
                        <div class="col-md-10">
                            @Html.PasswordFor(model => model.Password, new { @class = "form-control" })
                            @Html.ValidationMessageFor(model => model.Password)
                        </div>
                    </div>
                    <br />
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <div class="checkbox">
                                @Html.CheckBoxFor(model => model.RememberMe)
                                @Html.LabelFor(model => model.RememberMe)
                            </div>
                        </div>
                    </div>
                    <br />
                    <div class="form-group">
                        <div class="col-md-offset-2 col-md-10">
                            <input type="submit" value="Log in" class="btn btn-default" />
                        </div>
                    </div>
                    <p>
                        @Html.ActionLink("Register", "Register") if you don't have a local account.
                    </p>
                }
            </section>
        </div>
    </div>
</body>
</html>

 

Register.cshtml

 

@{
    Layout = null;
}
@model LightSwitchApplication.Models.UserDTO
<!DOCTYPE html>
<html>
<head>
    <meta name="HandheldFriendly" content="true" />
    <meta name="viewport" content="width=device-width, initial-scale=1,
          minimum-scale=1, maximum-scale=1, user-scalable=no" />
    <title>Register</title>
</head>
<body>
    <h2>Register</h2>
    <div>
        @using (Html.BeginForm())
        {
            @Html.AntiForgeryToken()
            <div class="control-group">
                @Html.LabelFor(model => model.UserName, new { @class = "control-label" })
                <div class="controls">
                    @Html.EditorFor(model => model.UserName)
                    @Html.ValidationMessageFor(model => model.UserName, 
                    null, new { @class = "help-inline" })
                </div>
            </div>
            <div class="control-group">
                @Html.LabelFor(model => model.Password, new { @class = "control-label" })
                <div class="controls">
                    @Html.PasswordFor(model => model.Password)
                    @Html.ValidationMessageFor(model => model.Password, 
                    null, new { @class = "help-inline" })
                </div>
            </div>
            <div class="control-group">
                @Html.LabelFor(model => model.Email, new { @class = "control-label" })
                <div class="controls">
                    @Html.EditorFor(model => model.Email)
                    @Html.ValidationMessageFor(model => model.Email, 
                    null, new { @class = "help-inline" })
                </div>
            </div>
            <br />
            <div class="form-actions no-color">
                <input type="submit" value="CreateUser" class="btn" />
            </div>
            @Html.ValidationSummary(true)
        }
        <br />
        <div>
            @Html.ActionLink("Back", "Index", "Home")
        </div>
    </div>
</body>
</html>

 

Add The Controller

image

Add a class called AccountController.cs to the Controllers folder and use the following code:

 

using System;
using System.Text;
using System.Web.Mvc;
using System.Web.Security;
using LightSwitchApplication.Models;
using Microsoft.LightSwitch.Security.ServerGenerated.Implementation;
using Microsoft.LightSwitch.Server;
namespace LightSwitchApplication.Controllers
{
    public class AccountController : Controller
    {
        // =============================
        // Register - Create a new user
        // =============================
        public ActionResult Register()
        {
            return View(new UserDTO());
        }
        [HttpPost]
        public ActionResult Register(FormCollection collection)
        {
            try
            {
                var UserName = collection["UserName"];
                var Password = collection["Password"];
                var Email = collection["Email"];
                if (UserName == "")
                {
                    throw new Exception("No UserName");
                }
                if (Password == "")
                {
                    throw new Exception("No Password");
                }
                // Keep our UserName as LowerCase
                UserName = UserName.ToLower();
                // Create LightSwitch user
                MembershipUser objMembershipUser = Membership.CreateUser(UserName, Password, Email);
                // Log User in
                // Create a new instance of the LightSwitch Authentication Service
                using (var authService = new AuthenticationService())
                {
                    var LoggedInUser = authService.Login(
                        UserName,
                        Password,
                        false,
                        null);
                    // Successful login?  If so, return the user
                    if (LoggedInUser != null)
                    {
                        return Redirect("~/Home");
                    }
                    else
                    {
                        ModelState.AddModelError(string.Empty, "Login failed.");
                        return View();
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(
                    string.Empty, "Error: " + ex);
                return View();
            }
        }
        // ========================================================
        // ChangePassword - Change the password of an existing user
        // ========================================================
        [Authorize]
        public ActionResult ChangePassword()
        {
            return View(new ChangePasswordDTO());
        }
        [Authorize]
        [HttpPost]
        public ActionResult ChangePassword(FormCollection collection)
        {
            try
            {
                using (var authService = new AuthenticationService())
                {
                    if (collection["NewPassword"] != collection["ConfirmPassword"])
                    {
                        throw new Exception("New Password and Confirm Password must match");
                    }
                    if (!Membership.GetUser()
                        .ChangePassword(collection["OldPassword"], collection["NewPassword"]))
                    {
                        throw new Exception("Password change failed.");
                    }
                    return Redirect("~/Home");
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, "Error: " + ex);
                return View();
            }
        }
        // ===================================================
        // Login - Log a user in, return authentication cookie
        // ===================================================
        public ActionResult Login()
        {
            return View(new UserDTO());
        }
        [HttpPost]
        public ActionResult Login(FormCollection collection)
        {
            try
            {
                // Create a new instance of the LightSwitch Authentication Service
                using (var authService = new AuthenticationService())
                {
                    // Log User in
                    var user = authService.Login(
                        collection["UserName"].ToLower(),
                        collection["Password"],
                        Convert.ToBoolean(collection["Persistent"]),
                        null);
                    // Successful login?  If so, return the user
                    if (user != null)
                    {
                        return Redirect("~/Home");
                    }
                    else
                    {
                        ModelState.AddModelError(string.Empty,
                            "Login failed.  Check User Name and/or Password.");
                        return View();
                    }
                }
            }
            catch (Exception ex)
            {
                ModelState.AddModelError(string.Empty, "Error: " + ex.Message);
                return View();
            }
        }
        // ============================================================
        // LogOff - Clears the cookie, logging a user out of the system
        // ============================================================
        public ActionResult LogOff()
        {
            // Create a new instance of the LightSwitch Authentication Service
            using (var authService = new AuthenticationService())
            {
                var user = authService.Logout();
                return Redirect("~/Home");
            }
        }
    }
}

 

Update the Index Page

image

Open the Index.cshtml page in the Views / Home folder and change the code to the following:

 

@{
    Layout = null;
}
@using Microsoft.AspNet.Identity
<!DOCTYPE html>
<html>
<head>
    <meta name="HandheldFriendly" content="true" />
    <meta name="viewport" content="width=device-width, 
          initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
    <title>Log In</title>
</head>
<body>
    <div>
        @if (Request.IsAuthenticated)
        {
            using (Html.BeginForm("LogOff", "Account", 
                FormMethod.Post, new { id = "logoutForm", @class = "navbar-right" }))
            {
                @Html.AntiForgeryToken()
                <p>
                    Hello @User.Identity.GetUserName() |
                    @Html.ActionLink("Change Password", 
                    "ChangePassword", "Account", 
                    routeValues: null, htmlAttributes: new { id = "changepassword" }) |
                    <a href="javascript:document.getElementById('logoutForm').submit()">Log off</a>
                </p>
                <a href="HTMLClient">LightSwitch Application</a>
            }
        }
        else
        {
            <p>
                @Html.ActionLink("Register", "Register", "Account", 
                routeValues: null, htmlAttributes: new { id = "registerLink" }) |
                @Html.ActionLink("Log in", "Login", "Account", 
                routeValues: null, htmlAttributes: new { id = "loginLink" })
            </p>
        }
    </div>
</body>
</html>

 

Special Thanks

This article would not be possible without the information provided by Dale Morrison (blog.ofanitguy.com):

 

More On LightSwitch and MVC

Creating an AngularJS CRUD Application Using Visual Studio LightSwitch

Using JayData to Consume the Visual Studio LightSwitch OData Business Layer in a AngularJs CRUD Application

 

Download Code

The LightSwitch project is available at http://lightswitchhelpwebsite.com/Downloads.aspx

(you must have Visual Studio 2013 (or higher) installed to run the code)

Tags: MVC
Categories:

9 comment(s) so far...


Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

Michael, excellent article and something I am looking forward to implementing in an up coming project. Thanks for your continued work in guiding people like me along. I personally really appreciate it!

By Paul Pitchford on   11/8/2013 6:40 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

@Paul Pitchford - Thank you for the feedback, it is much appreciated :)

By Michael Washington on   11/8/2013 6:41 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

Hi Michael, with regard to adding the MVC functionality to a Lightswitch project, I have found that you cannot add the Global.asax directly to the Server project, as it would not compile due to the fact that it does not create the code behind file automatically.

So in order to solve this one may download your sample and copy the Global.asax file to the project, or otherwise create two files, one is "Global" without any file type and one is "Global.asax.cs".

In the file "Global" add the following script:



And then in the "Global.asax.cs" add the rest of the code as per mentioned in your blog.

If you are in VS2013 you can right click the Global.asax and then open with Notepad to edit the "Global" file, whilst open directly would let you edit the "Global.asax.cs".

I have googled a bit and found that in the past we may add the "Global.asax" as the Global class file in the new item in VS2010, but it is deprecated in VS2013. This might be due to the fact that Microsoft does not expect you to create the Global.asax directly, but being scaffolded when creating a new MVC project.


By Cliff Lo on   12/25/2013 6:58 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

Hi Michael,

With regard to adding roles for the self-registered user, add the following line below:

//Create LightSwitch user
MembershipUser objMembershipUser = Membership.CreateUser(UserName, Password, Email);

//Add this line
Roles.AddUserToRole(UserName, "Your Role");

However, remember to create your role with your administrator account first.

By Cliff Lo on   12/25/2013 8:52 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

Hi I have missed the script for global.asax. The script is below:



Add this to "Global" to bind "Global" to "Global.asax.cs".

By Cliff Lo on   12/25/2013 8:55 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

@Cliff Lo - Thanks. To post your code it may be better to post it on the official LightSwitch forums (http://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=lightswitch) and then post a link to your post. I am going to leave the article as it is because I have presented it numerous times going though it step-by-step so I am confidant that what I have presented works.

By Michael Washington on   12/25/2013 11:46 AM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

Hi Micheal
I am using vs2013 with this project and would like get the logged on username in my startup Main application? I have looked everywhere including adding you GetUserName.ashx routine which fails on this project. Could you please help me.

By pp8357 on   5/19/2014 4:07 PM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

@pp8357 - I don't know why it is not working for you. It works for me. It may be better to post it on the official LightSwitch forums (http://social.msdn.microsoft.com/Forums/vstudio/en-US/home?forum=lightswitch).

By Michael Washington on   5/19/2014 6:38 PM
Gravatar

Re: Allow LightSwitch Users To Self-Register and Change Passwords Using MVC

@Michael Faraday - See Creating an AngularJS CRUD Application Using Visual Studio LightSwitch (http://lightswitchhelpwebsite.com/Blog/tabid/61/EntryId/2230/Creating-an-AngularJS-CRUD-Application-Using-Visual-Studio-LightSwitch.aspx) for a real world example. The MVC end points call ServerApplicationContext and this is a secure LightSwitch API that enforces row level security.

By Michael Washington on   7/6/2014 2:38 PM

Your name:
Gravatar Preview
Your email:
(Optional) Email used only to show Gravatar.
Your website:
Title:
Comment:
Security Code
CAPTCHA image
Enter the code shown above in the box below
Add Comment   Cancel 

Microsoft Visual Studio is a registered trademark of Microsoft Corporation / LightSwitch is a registered trademark of Microsoft Corporation