Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I have an Asp.Net MVC webapplication sitting inside a website still largely managed by delphi. Security is currently managed by delphi which creates cookies.

It has been decided to authenticate users within the ASP.Net application by extracting the cookie details and passing them to an imported Delphi DLL which returns true or false depending on whether the user is valid.

My plan was to use Forms authentication, but instead of redirecting the user to a form instead call the delphi wrapper and if successful, redirect the user to the original url. This gives the benefit that when security is migrated to .Net, the authentication framework will already exist, just the implementation will need to change.

public ActionResult LogOn(SecurityCookies model, string returnUrl)
    {
        try
        {
            if (model != null)
            {
                Log.DebugFormat("User login: Id:{0}, Key:{1}", model.UserId, model.Key);
                if (authenticator.UserValid(model.UserId, model.Key, 0))
                {
                    FormsService.SignIn(model.UserId, false);
                    return Redirect(returnUrl);
                }
            }
...

Note that the SecurityCookies are generated by a custom binding class from the delphi generated cookie - this works well.

The call to the delphi dll also works ok.

The issue I have to overcome is that nearly all of the calls to the .Net application are ajax requests. However when the user is not logged in, the browser makes 3 calls due to the redirects: 1) Original ajax request 2) Redirect to ~/Account/Logon (code above) 3) Redirect back to original ajax request

Although tracking the responses posted back to the client, show that Step 3 returns the correct data, overall the process fails for an as yet undetermined reason. Simply clicking refresh on the client works because now the user is authenticated and the redirect to ~/account/Logon doesn't occur.

Note my client jQuery code is as follows: $.getJSON(requestString, function (data) { //do something with the data });

Is there a way of changing the Forms Authentication process so that instead of redirecting to a Url when the User is not authenticated, I can run some other code instead? I'd like the fact that authentication has taken place to be completely invisible to the User's browser.

share|improve this question
    
could you rewrite your last statement? –  Shawn Mclean May 19 '11 at 14:09
    
have you considered wrapping the authentication call in a membership provider MSDN? –  Menahem May 19 '11 at 14:16
    
Sorry Shawn, is that clearer now? –  BonyT May 19 '11 at 14:22
    
@Menahem: I have, however I'm not clear WHERE I would authenticate in that case? Putting it within the Controller method would be poor as caching would bypass security checks, and I'm not keen on the idea of putting it in Global.asax. As I see it Custom Membership providers still work with a Logon.aspx form -it's just how you managed the credentials that changes. –  BonyT May 19 '11 at 14:36
    
if you are using a provider, you only need to define access to your pages in the web.config , and the user will get redirected / authenticated without putting any code in global.asax or the controller. –  Menahem May 19 '11 at 14:52

3 Answers 3

up vote 7 down vote accepted

If you want to authenticate the request, the place to do this is in global.asax.cs by defining the Application_AuthenticateRequest method. Here you can read out the custom cookies with the imported delphi dll and set the Context.User. All authorization in asp.net is based on the user that is set in the HttpContext. An example of an implementation of the Application_AuthenticateRequest method:

protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookies[FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        //CustomIdentity implements System.Web.Security.IIdentity
        CustomIdentity id = GetUserIdentity(authTicket.Name);
        //CustomPrincipal implements System.Web.Security.IPrincipal
        CustomPrincipal newUser = new CustomPrincipal();
        Context.User = newUser;
    }
}

If the cookie is not valid then you won't set a user in the context.

You can then create a BaseController which all your controllers will inherit from that checks if the user that is provided in the context is authenticated. If the user is not authenticated you can return a HttpUnauthorizedResult.

public class BaseController : Controller
{
    protected override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (User == null || (User != null && !User.Identity.IsAuthenticated))
        {
           filterContext.Result = new HttpUnauthorizedResult();
        }
        else
        {
            // Call the base
            base.OnActionExecuting(filterContext);
        }
    }
}

And in your web.config:

<authentication mode="None"/>

because you don't want the request to be redirected to a login page.

share|improve this answer
    
Thanks - that looks exactly what I need! –  BonyT May 19 '11 at 14:50

I'm going to answer a part of this specific question

Is there a way of changing the Forms Authentication process so that instead of redirecting to a Url when the User is not authenticated, I can run some other code instead? I'd like the fact that authentication has taken place to be completely invisible to the User's browser.

No there is not. I wrote a login system that is similar to yours being that it is built ontop of forms authentication and the fact there is no extensibility point made it a complete PITA to write.

The way I was able to override the built in behavior was using a HttpModule that monitors for redirects to the forms auth URL and then to intercept that action and lets me handle it.

internal static bool IsFormsAuthenticationLogin(HttpResponseBase response)
{
    if (!response.IsRequestBeingRedirected) return false;
    if (string.IsNullOrWhiteSpace(response.RedirectLocation)) return false;

    Uri formsAuthUri;
    try
    {
        formsAuthUri = new Uri(FormsAuthentication.LoginUrl);
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct formsAuthUri", ex);
    }


    Uri redirctUri;
    try
    {
        redirctUri = new Uri(response.RedirectLocation, UriKind.RelativeOrAbsolute);
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct redirctUri", ex);
    }

    try
    {
        //Check if the request was redirected by Forms Auth
        bool isFormsAuthenticationLogin = redirctUri.IsAbsoluteUri &&
                                            redirctUri.Host == formsAuthUri.Host
                                            && redirctUri.PathAndQuery.Contains(formsAuthUri.PathAndQuery); 

        return isFormsAuthenticationLogin;
    }
    catch (Exception ex)
    {
        throw new XifCriticalException("Could not construct isFormsAuthenticationLogin", ex);
    }
}

One other note, to be able to rely on this code in MVC3 you may also need to specify the app settings

 <add key="enableSimpleMembership" value="false" />
 <add key="autoFormsAuthentication" value="false" />
share|improve this answer

Couple things.

First, since you're apparently in a controller action, you should replace

Server.Transfer(returnUrl, true);

with

return Redirect(returnUrl);

Second, I generally prefer to not use ajax calls to do authentication. Users should explicitly move from unauthenticated to authenticated states.

Third, if you must do ajax calls where you would normally do redirects, you can do one of two things. In your controller, you can determine it's an ajax request by calling the extension method IsAjaxRequest() (returns true if it is) and branching a different result if it is an ajax request. Second, if you return a Redirect, then it will return to the client a HTTP redirect, which the Ajax client code should be able to read and respond to (e.g. by reading the Location and then doing an ajax fetch on that). Again, I don't recommend this course, but it's possible.

Finally, as a complete left turn... have you considered leaving forms auth alone and instead using a custom MembershipProvider? You can use that to validate membership via Delphi and then set the client cookies using the normal FormsAuth objects like they do in the sample AccountController in MVC.

share|improve this answer
    
OK yes - I was originally using Redirect - referred to in my comments, but the code I posted was from when I tried Server.Transfer to see if it helped - then I realised I still had the Redirect to the Account/Logon method to deal with anyway –  BonyT May 19 '11 at 14:24
    
I'm sorry don't understand how your 2nd comment applies to what I'm doing here. The user is already authenticated on the web server, just not within the .Net code. The .Net application is currently only serving a small number of ajax requests, but needs to be secure. –  BonyT May 19 '11 at 14:32
    
They're not authenticated in the application yet (the ".net code"). But to each their own, that was advice not answer. –  Paul May 19 '11 at 14:36
    
Sorry Paul, I didn't mean to offend - the advice is welcome. It's just that I'm rather constrained by the legacy app in terms of what I can do here. –  BonyT May 19 '11 at 14:44
    
Understood, and no offense taken. The "Third" paragraph should be relevant for you. –  Paul May 19 '11 at 15:35

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.