Try our conversational search powered by Generative AI!

Custom login page - localized

Vote:
 

I have a custom login page which the use is redirected to when an anonymous user tries to access a restricted page, via changing  web.config.


      

(Using SqlServerRoleProvider)

Two issues with this approach:

1. It disables the returnUrl for Administrators when loggin in via /Util/login.aspx

2. No good solution on a multi-lingual site

I can live with issue no 1, but I would really like to fix no 2. It's important to present the correct language to the user.
Any ideas?

#112009
Oct 22, 2014 10:12
Vote:
 

We've had a similar problem before. We went for the easy (hacky maybe?) way out:

Make a pagetype for the loginpage and a page of this type with translations. In our case we only had english and norwegian (master language). The loginUrl attribute in web.config links to this page's url. Then we made the following code-behind in the LoginPage's template:

    public partial class LoginPage : TemplatePage<LoginPageType>
    {
        private string EnglishUrlPrefix { get { return string.Format("/{0}/", Language.English.Key); } }
        private const string ReturnUrlKey = "ReturnUrl";
        private const string LoopBreakerKey = "lbrk";

        protected void Page_Load(object sender, EventArgs e)
        {
            var returnUrl = Request.QueryString[ReturnUrlKey];

            if (ShouldRedirectToEnglish(returnUrl))
            {
                var urlBuilder = new UrlBuilder(CurrentPage.LinkURL);
                urlBuilder.QueryLanguage = Language.English.Key;
                urlBuilder.QueryCollection.Add(ReturnUrlKey, returnUrl);
                urlBuilder.QueryCollection.Add(LoopBreakerKey, "1");
                Response.Redirect(urlBuilder.ToString());
            }
        }

        private bool ShouldRedirectToEnglish(string returnUrl)
        {
            return !string.IsNullOrEmpty(returnUrl) && 
                   IsEnglishUrl(returnUrl) && 
                   !CurrentPageIsEnglish() &&
                   IsNotLoopingRedirect();
        }

        private bool IsNotLoopingRedirect()
        {
            return Request.QueryString[LoopBreakerKey] == null;
        }

        private bool CurrentPageIsEnglish()
        {
            return CurrentPage.LanguageID.Equals(Language.English.Key, 
                StringComparison.InvariantCultureIgnoreCase);
        }

        private bool IsEnglishUrl(string returnUrl)
        {
            return returnUrl.StartsWith(EnglishUrlPrefix);
        }
    }

In this code we inspect the returnurl to determine whether the user wanted to visit an english page or not. If that's the case, and the current version of the login page is norwegian, we redirect to the same page that we're currently on, only with the /en/ prefix added. The "loop breaker" querystring is only there to ensure that we don't end up in an endless redirect loop (not sure if that could ever be the case or not, but heck - why not..).

The MVC version of this wouldn't be much different. Just put the Page_Load code in the Index action instead and return RedirectResult instead of Response.Redirect.

Hope this solves your problem :)

#112058
Oct 22, 2014 10:53
Vote:
 

Very interresting solution.

I'll take a look.

#112064
Oct 22, 2014 11:07
Vote:
 

Works as expected after minor justifications:

public class LoginPageController : PageControllerBase<LoginPage>
	{
		private readonly IAuthenticationManager _authenticationManager;

        private string EnglishUrlPrefix { get { return string.Format("/{0}/", EnglishString); } }
        private const string EnglishString = "en-GB";
        private const string ReturnUrlKey = "ReturnUrl";
        private const string LoopBreakerKey = "lbrk";
        private string CurrentLanguage { get; set; }

        

	public ActionResult Index(LoginPage currentPage, string returnUrl = "")
	{
		var model = PageViewModel.Create(currentPage);

        	CurrentLanguage = currentPage.LanguageID;

            if (ShouldRedirect(returnUrl))
            {
                var urlBuilder = new UrlBuilder(currentPage.LinkURL);
                urlBuilder.QueryLanguage = RequestedLanguageString(returnUrl);
                urlBuilder.QueryCollection.Add(ReturnUrlKey, returnUrl);
                urlBuilder.QueryCollection.Add(LoopBreakerKey, "1");
                Response.Redirect(urlBuilder.ToString());
           }

		return View(string.Format("{0}/Index", currentPage.GetOriginalType().Name), model);
	}

	[ValidateAntiForgeryToken]
	[HttpPost]
	public ActionResult Index(LoginInputModel userModel, string returnUrl = "")
	{
		... custom authentication code
	}

        private bool ShouldRedirect(string returnUrl)
        {
            return !string.IsNullOrEmpty(returnUrl) &&
                   CurrentPageIsInEnglish() &&
                   !IsEnglishUrl(returnUrl) &&
                   IsNotLoopingRedirect();
        }

        private bool IsNotLoopingRedirect()
        {
            return Request.QueryString[LoopBreakerKey] == null;
        }

        private bool CurrentPageIsInEnglish()
        {
            return CurrentLanguage.Equals(EnglishString, System.StringComparison.InvariantCultureIgnoreCase);
        }

        private bool IsEnglishUrl(string returnUrl)
        {
            return returnUrl.StartsWith(EnglishUrlPrefix);
        }
        private string RequestedLanguageString(string returnUrl)
        {
            Match match = Regex.Match(returnUrl, @"/(\w\w-\w\w)(/?)(.*)", //should match /sv-SE/url
            RegexOptions.IgnoreCase);

            if (match.Success)
            {
                return match.Groups[1].Value;
            }
            return string.Empty;
        }
}
#112073
Oct 22, 2014 13:23
This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.