locked
Location for login.aspx code behind? RRS feed

  • Question

  • I added a Login.aspx.cs file in the Server Project with some code in Page_Load() I need to implement, and edited the first line of login.aspx this way:

    <%@ Page Language="C#" CodeBehind="Login.aspx.cs" Inherits="Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase" %>

    What I added, exactly, are the Language and CodeBehind properties.

    But when I deploy and run the application, I get this error:

    The type 'Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase' is ambiguous: it could come from assembly 'C:\testing\Sandbox\bin\Microsoft.LightSwitch.Server.DLL' or from assembly 'C:\testing\Sandbox\bin\Application.Server.DLL'. Please specify the assembly explicitly in the type name.

    I understand this is because Login.aspx.cs already exists in some assembly.

    Is there any way to Access Login.aspx code behind?

    If not, how can I add my own login page to an HTML Client App without losing the already configured Forms security, roles, permissions, etc.?

    thanks.

    Nicolás.


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Monday, January 12, 2015 6:48 PM

Answers

  • Hi Nicolás,

    If you're simply looking for the current user's name, we're using the LoggedIn event. 

    The following are some snippets from one of our test apps showing this approach: -

    Start of LogIn.aspx (adding the server-side script block): -

    <%@ Page Inherits="Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase" Language="C#" %>
    
    <%@ Import Namespace="LightSwitchApplication" %>
    <%@ Import Namespace="Microsoft.LightSwitch" %>
    <%@ Import Namespace="Microsoft.LightSwitch.Server" %>
    <%@ Import Namespace="System.Diagnostics" %>
    <%@ Import Namespace="System.Linq" %>
    
    <script runat="server">
        protected void LoginUser_Load(object sender, EventArgs e)
        {
            Page.Form.DefaultFocus = LoginUser.FindControl("Username").ClientID;
        }
        protected void LoginUser_LoggedIn(object sender, EventArgs e)
        {
            this.Log(String.Format("LoginUser_LoggedIn User [{0}]", ((System.Web.UI.WebControls.Login)sender).UserName));
        }
        private void Log(String logText)
        {
            EventLog el = new EventLog("Application");
            el.Source = "Application";
            if (EventLog.SourceExists(el.Source))
            {
                el.WriteEntry(logText, EventLogEntryType.Error);
            }
        }
    </script>


    Then, in the body of the LogIn.aspx (adding the event references): -

    <asp:Login ID="LoginUser" runat="server" EnableViewState="false" RenderOuterTable="false" OnLoad="LoginUser_Load" OnLoggedIn="LoginUser_LoggedIn">

    Whilst this example is writing the details to the web server's event log, you could take the ((System.Web.UI.WebControls.Login)sender).UserName and write it to a LightSwitch table using the SkipAuthentication approach.

    HTH,

    Chris


    Tuesday, January 20, 2015 1:11 AM
  • Hi Nico,

    I think you want to make your Login class  in the code behind be in the LightSwitchApplication Namespace and also inherit .LoginPageBase then change your first line of login.aspx to inherit that class.  For example, the following works for me in vb:

    Login.aspx:

    %@ Page Title="Log In" Language="vb" AutoEventWireup="true"
        CodeBehind="Login.aspx.vb"Inherits="LightSwitchApplication.Login" %>

    Login.aspx.vb:

    Imports System.Web.Security
    
    Namespace LightSwitchApplication
    
        Public Class Login
            Inherits Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase
    
            Protected Sub Page_Load(ByVal sender As Object,
                                ByVal e As System.EventArgs) _
                            Handles Me.Load
    
                    Me.Response.Write("DO SOMETHING")
    
            End Sub
    
        End Class
    
    End Namespace

    Have a great day!

    HTH,

    Josh


    Wednesday, January 14, 2015 3:08 PM

All replies

  • Hi Nico,

    I think you want to make your Login class  in the code behind be in the LightSwitchApplication Namespace and also inherit .LoginPageBase then change your first line of login.aspx to inherit that class.  For example, the following works for me in vb:

    Login.aspx:

    %@ Page Title="Log In" Language="vb" AutoEventWireup="true"
        CodeBehind="Login.aspx.vb"Inherits="LightSwitchApplication.Login" %>

    Login.aspx.vb:

    Imports System.Web.Security
    
    Namespace LightSwitchApplication
    
        Public Class Login
            Inherits Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase
    
            Protected Sub Page_Load(ByVal sender As Object,
                                ByVal e As System.EventArgs) _
                            Handles Me.Load
    
                    Me.Response.Write("DO SOMETHING")
    
            End Sub
    
        End Class
    
    End Namespace

    Have a great day!

    HTH,

    Josh


    Wednesday, January 14, 2015 3:08 PM
  • Excellent, I'll give it a try, I just can't right now.

    Thank you Josh.

    have a great one!


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Wednesday, January 14, 2015 7:09 PM
  • Josh:I tried your suggestion, edited Login.aspx like you said, and the code for Login.aspx.cs converted from vb is this:

    using System;
    using System.Collections.Generic;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.Security;
    using Microsoft.LightSwitch;
    using LightSwitchApplication;
    using LightSwitchApplication.Helpers;
    
    namespace LightSwitchApplication
    {
        public partial class Login : Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase
        {
            protected void Page_Load(object sender, EventArgs e)
            {
                    AuditHelper.CreateAuditTrailForLogin();            
            }
            public Login()
    		{
    			Load += Page_Load;
    		}
        }
    }


    As you can see, all I want to do is call AuditHelper.CreateAuditTrailForLogin() on Page_Load()

    But I'm getting this exception, wich is obvious what it means, but I don't know how to fix. Does it mean we're overriding Server generated Page_Load() so I have to write all the code that handles authentication?:

    The method or operation is not implemented.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.             
    Exception Details: System.NotImplementedException: The method or operation is not implemented.
    Source Error:

    An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.                  

    Stack Trace:
    [NotImplementedException: The method or operation is not implemented.]
       LightSwitchApplication.Login.Page_Load(Object sender, EventArgs e) +36
       Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase.OnLoad(EventArgs e) +90
       System.Web.UI.Control.LoadRecursive() +71
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3178

    Any help appreciated.


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.



    Friday, January 16, 2015 3:34 PM
  • Hi Nicolás,

    Whilst the approach outlined by Josh has worked for us in the past, you could try implementing the code without a code-behind file e.g. simply make the following changes to the standard LogIn.aspx: -

    Start of LogIn.aspx (adding the server-side script block): -

    <%@ Page Inherits="Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase" Language="C#" %>
    
    <script runat="server">
        protected void LoginUser_Load(object sender, EventArgs e)
        {
            LightSwitchApplication.AuditHelper.CreateAuditTrailForLogin();
        }
    </script>


    In the body of the LogIn.aspx (adding the OnLoad reference): -

    <asp:Login ID="LoginUser" runat="server" EnableViewState="false" RenderOuterTable="false" OnLoad="LoginUser_Load">

    Then you'd have your method in your AuditHelper.cs: -

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Web;
    
    namespace LightSwitchApplication
    {
        public static class AuditHelper
        {
            public static void CreateAuditTrailForLogin()
            {
                // Write code here.
            }
        }
    }

    Maybe this approach will avoid the exception.

    HTH,

    Chris


    Monday, January 19, 2015 10:15 PM
  • Thank you Chris. 

    I discovered that the NotImplementedException was thrown because I forgot to include a required parameter when calling CreateAuditTrailForLogin(). Somehow LS autogenerated it.

    I've corrected it and tried again:

    protected void Page_Load(object sender, EventArgs e)
    {
                using (var serverContext = ServerApplicationContext.CreateContext())
                {
                    AuditHelper.CreateAuditTrailForLogin(serverContext.DataWorkspace.ApplicationData.AuditTrails);
                }
    
    }

    But now I'm getting an Authentication error:

    Unable to authenticate.  Access is denied.

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.Security.Authentication.AuthenticationException: Unable to authenticate.  Access is denied.

    Source Error:
    An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

    Stack Trace:
    [AuthenticationException: Unable to authenticate.  Access is denied.]
       Microsoft.LightSwitch.Utilities.ServerGenerated.Internal.ServerApplicationContextFactoryCore.AuthenticateUser() +141
       Microsoft.LightSwitch.Server.ServerApplicationContextFactory.CreateContext(ServerApplicationContextCreationOptions options) +235
       LightSwitchApplication.Login.Page_Load(Object sender, EventArgs e) +37
       Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase.OnLoad(EventArgs e) +90
       System.Web.UI.Control.LoadRecursive() +71
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +3178
    

    I'll try your approach later.

    Thanks again!


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.


    Monday, January 19, 2015 11:38 PM
  • When page_Load fires, you're not authenticated yet.  Therefore I believe ServerApplicationContext.CreateContext would not work.

    Unless you specify SkipAuthentication option like so:

    Using Context As ServerApplicationContext = ServerApplicationContext.CreateContext(ServerApplicationContextCreationOptions.SkipAuthentication) 
    ' allow in unauthenticated users 
    ' your code goes here 
    End Using

    using (ServerApplicationContext Context = ServerApplicationContext.CreateContext(ServerApplicationContextCreationOptions.SkipAuthentication)) {
    // allow in unauthenticated users 
    // your code goes here 
    }

    HTH,

    Josh

     
    Tuesday, January 20, 2015 12:00 AM
  • Yeah, I'm aware of that. Thanks Josh.

    Unfortunately, it is pointless, since I'm trying to create an Audit Trail for the User logging in. The method CreateAuditTrailForLogin() actually makes use of Application.Current.User, and if the user is not authenticated, it returns Unknown. Which is useless. I discover that when I tried it in Global.asax, on Application_Start(), and thought that if I could place the code in Login.aspx it might work.

    This is a user request and I've been struggling ever since.

    I'm frustrated.

    Thanks anyway. And, since your reply actually answers the question, I will mark it as answer.


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Tuesday, January 20, 2015 1:03 AM
  • Hi Nicolás,

    If you're simply looking for the current user's name, we're using the LoggedIn event. 

    The following are some snippets from one of our test apps showing this approach: -

    Start of LogIn.aspx (adding the server-side script block): -

    <%@ Page Inherits="Microsoft.LightSwitch.Security.ServerGenerated.Implementation.LogInPageBase" Language="C#" %>
    
    <%@ Import Namespace="LightSwitchApplication" %>
    <%@ Import Namespace="Microsoft.LightSwitch" %>
    <%@ Import Namespace="Microsoft.LightSwitch.Server" %>
    <%@ Import Namespace="System.Diagnostics" %>
    <%@ Import Namespace="System.Linq" %>
    
    <script runat="server">
        protected void LoginUser_Load(object sender, EventArgs e)
        {
            Page.Form.DefaultFocus = LoginUser.FindControl("Username").ClientID;
        }
        protected void LoginUser_LoggedIn(object sender, EventArgs e)
        {
            this.Log(String.Format("LoginUser_LoggedIn User [{0}]", ((System.Web.UI.WebControls.Login)sender).UserName));
        }
        private void Log(String logText)
        {
            EventLog el = new EventLog("Application");
            el.Source = "Application";
            if (EventLog.SourceExists(el.Source))
            {
                el.WriteEntry(logText, EventLogEntryType.Error);
            }
        }
    </script>


    Then, in the body of the LogIn.aspx (adding the event references): -

    <asp:Login ID="LoginUser" runat="server" EnableViewState="false" RenderOuterTable="false" OnLoad="LoginUser_Load" OnLoggedIn="LoginUser_LoggedIn">

    Whilst this example is writing the details to the web server's event log, you could take the ((System.Web.UI.WebControls.Login)sender).UserName and write it to a LightSwitch table using the SkipAuthentication approach.

    HTH,

    Chris


    Tuesday, January 20, 2015 1:11 AM
  • Chris, that was awesome. 

    The only difference is that you're storing in the Windows Log (accessible via Event Viewer) and I'm trying to do it on the application database.

    So are you suggesting that if I mix this with a Context where authentication was skipped, it might work?

    thx!


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Tuesday, January 20, 2015 1:32 AM
  • Chris, that was awesome. 

    The only difference is that you're storing in the Windows Log (accessible via Event Viewer) and I'm trying to do it on the application database.

    So are you suggesting that if I mix this with a Context where authentication was skipped, it might work?

    thx!


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Yes, we're doing exactly that in some projects to update client records with their last log in date/time.

    HTH,

    Chris

    Tuesday, January 20, 2015 1:44 AM
  • THANK YOU BOTH! It works as expected. 

    To me this is a highly "Senior" trick. You rock!


    Nicolás Lope de Barrios
    If you found this post helpful, please "Vote as Helpful". If it actually answered your question, please remember to "Mark as Answer". This will help other people find answers to their problems more quickly.

    Tuesday, January 20, 2015 2:13 AM