Single sign on inside asp.net mvc web application - asp.net

I have two domains ,on our internal network:-
DomainA
DomainB
Both domains can communicate with each other’s, but they do NOT trust each other.
So currently I have deployed my asp.net MVC web application inside domainA on IIS, but I need users who are on DomainB Active directory to be able to login to the asp.net mvc using their domainB AD credentials . I am open to both windows authentication and form authentication inside my asp.net mvc .
But the only requirement that came from the client is that they want users who access the asp.net mvc intranet application from their machine on domainB, to be able to lo-gin to the system without having a login page; they can either:-
enter the username and password through the browser pop-up,
or to sign in automatically.
So can anyone advice what are the approaches I can follow, to achieve this?
Thanks
EDIT
I have read the following article http://msdn.microsoft.com/en-us/library/ff650307.aspx, about how i can authenticate asp.net mvc users from multiple domains, so inside my asp.net mvc i did the following :-
I added the following to my web.config:-
<system.web>
<membership>
<providers>
<add name="TestDomain1ADMembershipProvider" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="TestDomain1ConnectionString" connectionUsername="ad-domainA.intra\it360ad.user" connectionPassword="$$$$$" />
</providers>
</membership>
&
<add name="TestDomain1ConnectionString" connectionString="LDAP://ad-domainA.intra/CN=Users,DC=ad-domainA,DC=intra" />
and i added the following Account.controller:-
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginModel model, string returnUrl)
{
MembershipProvider domainProvider;
domainProvider = Membership.Providers["TestDomain1ADMembershipProvider"];
// Validate the user with the membership system.
if (domainProvider.ValidateUser(model.UserName, model.Password))
{
// If there is a RequestUrl query string attribute, the user has
// been redirected to the login page by forms authentication after
// requesting another page while not authenticated.
if (Request.QueryString["ReturnUrl"] != null)
{
// RedirectFromLoginPage sets the authorization cookie and then
// redirects to the page the user originally requested.
// Set second parameter to false so cookie is not persistent
// across sessions.
FormsAuthentication.RedirectFromLoginPage(
model.UserName, false);
}
else
{
// If there is no RequestUrl query string attribute, just set
// the authentication cookie. Provide navigation on the login page
// to pages that require authentication, or user can use browser
// to navigate to protected pages.
// Set second parameter to false so cookie is not persistent
// across sessions.
FormsAuthentication.SetAuthCookie(model.UserName, false);
}
}
else
{
// Response.Write("Invalid UserID and Password");
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
////////////
//if (ModelState.IsValid && WebSecurity.Login(model.UserName, model.Password, persistCookie: model.RememberMe))
//{
return RedirectToLocal(returnUrl);
//}
// If we got this far, something failed, redisplay form
}
But currently when the user try to login , he will get always the following message
•The user name or password provided is incorrect.
so can you advice if my code is correct ?

You have to deploy a SSO solution like Active Directory Federation Services on a server that is joined to DomainB.
Then implement authentication in your application (for example, WS-Federation Passive Requestor) that targets that SSO solution and standard Windows Authentication that targets DomainA.

Related

Created a mvc5 app with Identity2, how do i set it up to use session cookies, so they expire when the browser closes

Created a mvc5 app with Identity2,using google login (pretty much the empty app, with google stuff turned on)
How do I set it up to use session cookies, so they expire when the browser closes.
The app will be used by students who may hot swap seats, so i need the login to expire when the browser closes.
I read an SO article that implies this is the default, but when i close the browser, and go back to the site, it remembers the google login.
Edit
Sorry to burst everyone bubble, but this isn't a duplicate.
It reproduced in Chrome after the settings in the supposed "answer" are changed, and it also reproduces in IE... This is an Asp.net Identity 2 +Google login issue, not a Chrome issue.
Edit
Adding Startup Auth file for Setup Help
using System;
using System.Configuration;
using Microsoft.AspNet.Identity;
using Microsoft.AspNet.Identity.Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security.Cookies;
using Microsoft.Owin.Security.Google;
using Owin;
using StudentPortalGSuite.Models;
namespace StudentPortalGSuite
{
public partial class Startup
{
// For more information on configuring authentication, please visit http://go.microsoft.com/fwlink/?LinkId=301864
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(
new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
validateInterval: TimeSpan.FromMinutes( 30 ),
regenerateIdentity: ( manager, user ) => user.GenerateUserIdentityAsync( manager )
)
},
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
// per https://learn.microsoft.com/en-us/aspnet/mvc/overview/security/create-an-aspnet-mvc-5-app-with-facebook-and-google-oauth2-and-openid-sign-on - EWB
//dev-jcsn email
app.UseGoogleAuthentication( new GoogleOAuth2AuthenticationOptions()
{
ClientId = "...",
ClientSecret = "..."
} );
//});
}
}
}
EDIT
The use case I'm trying to fix is, since our app is used in a classroom, that student A Closes his/her browser instead of logging out, and then next user tries to login. As it stands they are autologged into user A's account.
I'd also be up for a way to 100% log out the user when redirected to the login page, but all the ways I've tried that aren't working.
Maybe you can catch the window close event on page and call logout method
$(window).on("beforeunload", function() {
//ajax call to a post controller that logs the user out
})
Calling this at the top of the LogIn controller Method solved the issue.
Request.GetOwinContext().Authentication.SignOut( DefaultAuthenticationTypes.ApplicationCookie );// https://stackoverflow.com/questions/28999318/owin-authentication-signout-doesnt-seem-to-remove-the-cookie - stralos s answer
Request.GetOwinContext().Authentication.SignOut( DefaultAuthenticationTypes.ExternalCookie );

Can IIS require SSL client certificates without mapping them to a windows user?

I want to be able to map SSL client certificates to ASP.NET Identity users. I would like IIS to do as much of the work as possible (negotiating the client certificate and perhaps validating that it is signed by a trusted CA), but I don't want IIS to map the certificate to a Windows user. The client certificate is passed through to ASP.NET, where it is inspected and mapped to an ASP.NET Identity user, which is turned into a ClaimsPrincipal.
So far, the only way I have been able to get IIS to pass the client certificate through to ASP.NET is to enable iisClientCertificateMappingAuthentication and set up a many-to-one mapping to a Windows account (which is then never used for anything else.) Is there any way to get IIS to negotiate and pass the certificate through without this configuration step?
You do not have to use the iisClientCertificateMappingAuthentication. The client certificate is accessible in the HttpContext.
var clientCert = HttpContext.Request.ClientCertificate;
Either you enable RequireClientCertificate on the complete site or use a separate login-with-clientcertificate page.
Below is one way of doing this in ASP.NET MVC. Hopefully you can use parts of it to fit your exact situation.
First make sure you are allowed to set the SslFlags in web.config by turning on feature delegation.
Make site accept (but not require) Client Certificates
Set path to login-with-clientcertificate-page where client certificates will be required. In this case a User controller with a CertificateSignin action.
Create a login controller (pseudo-code)
[OutputCache(NoStore = true, Duration = 0, VaryByParam = "*")]
[AllowAnonymous()]
public ActionResult CertificateSignIn()
{
//Get certificate
var clientCert = HttpContext.Request.ClientCertificate;
//Validate certificate
if (!clientCert.IsPresent || !clientCert.IsValid)
{
ViewBag.LoginFailedMessage = "The client certificate was not present or did not pass validation";
return View("Index");
}
//Call your "custom" ClientCertificate --> User mapping method.
string userId;
bool myCertificateMappingParsingResult = Helper.MyCertificateMapping(clientCert, out userId);
if (!myCertificateMappingParsingResult)
{
ViewBag.LoginFailedMessage = "Your client certificate did not map correctly";
}
else
{
//Use custom Membersip provider. Password is not needed!
if (Membership.ValidateUser(userId, null))
{
//Create authentication ticket
FormsAuthentication.SetAuthCookie(userId, false);
Response.Redirect("~/");
}
else
{
ViewBag.LoginFailedMessage = "Login failed!";
}
}
return View("Index");
}

Prevent FormsAuthenticationModule of intercepting ASP.NET Web API responses

In ASP.NET the FormsAuthenticationModule intercepts any HTTP 401, and returns an HTTP 302 redirection to the login page. This is a pain for AJAX, since you ask for json and get the login page in html, but the status code is HTTP 200.
What is the way of avoid this interception in ASP.NET Web API ?
In ASP.NET MVC4 it is very easy to prevent this interception by ending explicitly the connection:
public class MyMvcAuthFilter:AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAjaxRequest() && !filterContext.IsChildAction)
{
filterContext.Result = new HttpStatusCodeResult(401);
filterContext.HttpContext.Response.StatusCode = 401;
filterContext.HttpContext.Response.SuppressContent = true;
filterContext.HttpContext.Response.End();
}
else
base.HandleUnauthorizedRequest(filterContext);
}
}
But in ASP.NET Web API I cannot end the connection explicitly, so even when I use this code the FormsAuthenticationModule intercepts the response and sends a redirection to the login page:
public class MyWebApiAuth: AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(System.Web.Http.Controllers.HttpActionContext actionContext)
{
if(actionContext.Request.Headers.Any(h=>h.Key.Equals("X-Requested-With",StringComparison.OrdinalIgnoreCase)))
{
var xhr = actionContext.Request.Headers.Single(h => h.Key.Equals("X-Requested-With", StringComparison.OrdinalIgnoreCase)).Value.First();
if (xhr.Equals("XMLHttpRequest", StringComparison.OrdinalIgnoreCase))
{
// this does not work either
//throw new HttpResponseException(HttpStatusCode.Unauthorized);
actionContext.Response = new System.Net.Http.HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
return;
}
}
base.HandleUnauthorizedRequest(actionContext);
}
}
What is the way of avoiding this behaviour in ASP.NET Web API? I have been taking a look, and I could not find a way of do it.
Regards.
PS: I cannot believe that this is 2012 and this issue is still on.
In case someone's interested in dealing with the same issue in ASP.NET MVC app using the Authorize attribute:
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class Authorize2Attribute : AuthorizeAttribute
{
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
if (filterContext.HttpContext.Request.IsAuthenticated)
{
filterContext.Result = new HttpStatusCodeResult((int) HttpStatusCode.Forbidden);
}
else
{
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.HttpContext.Response.SuppressFormsAuthenticationRedirect = true;
}
base.HandleUnauthorizedRequest(filterContext);
}
}
}
This way browser properly distinguishes between Forbidden and Unauthorized requests..
The release notes for MVC 4 RC imply this has been resolved since the Beta - which are you using?
http://www.asp.net/whitepapers/mvc4-release-notes
Unauthorized requests handled by ASP.NET Web API return 401 Unauthroized: Unauthorized requests handled by ASP.NET Web API now return a standard 401 Unauthorized response instead of redirecting the user agent to a login form so that the response can be handled by an Ajax client.
Looking into the source code for MVC there appears to be an functionality added via SuppressFormsAuthRedirectModule.cs
http://aspnetwebstack.codeplex.com/SourceControl/network/forks/BradWilson/AspNetWebStack/changeset/changes/ae1164a2e339#src%2fSystem.Web.Http.WebHost%2fHttpControllerHandler.cs.
internal static bool GetEnabled(NameValueCollection appSettings)
{
// anything but "false" will return true, which is the default behavior
So it looks this this is enabled by default and RC should fix your issue without any heroics... as a side point it looks like you can disable this new module using AppSettings http://d.hatena.ne.jp/shiba-yan/20120430/1335787815:
<appSettings>
<Add Key = "webapi:EnableSuppressRedirect" value = "false" />
</appSettings>
Edit (example and clarification)
I have now created an example for this approach on GitHub. The new redirection suppression requires that you use the two correct "Authorise" attribute's; MVC Web [System.Web.Mvc.Authorize] and Web API [System.Web.Http.Authorize] in the controllers AND/OR in the global filters Link.
This example does however draw out a limitation of the approach. It appears that the "authorisation" nodes in the web.config will always take priority over MVC routes e.g. config like this will override your rules and still redirect to login:
<system.web>
<authentication mode="Forms">
</authentication>
<authorization>
<deny users="?"/> //will deny anonymous users to all routes including WebApi
</authorization>
</system.web>
Sadly opening this up for some url routes using the Location element doesn't appear to work and the WebApi calls will continue to be intercepted and redirected to login.
Solutions
For MVC applications I am simply suggest removing the config from Web.Config and sticking with Global filters and Attributes in the code.
If you must use the authorisation nodes in Web.Config for MVC or have a Hybrid ASP.NET and WebApi application then #PilotBob - in the comments below - has found that sub folders and multiple Web.Config's can be used to have your cake and eat it.
I was able to get around the deny anonymous setting in web.config by setting the following property:
Request.RequestContext.HttpContext.SkipAuthorization = true;
I do this after some checks against the Request object in the Application_BeginRequest method in Global.asax.cs, like the RawURL property and other header information to make sure the request is accessing an area that I want to allow anonymous access to. I still perform authentication/authorization once the API action is called.

Getting Forms Authentication from an ASP.NET logon page used by Silverlight 4 application

This is supposed to just work. I've read all the articles I could find via google on the topic, tried to copy as much as I could from other articles on both StackOverflow and CodeProject and others, but regardless of what I try - it doesn't work.
I have a silverlight application that runs fine using Windows Authentication.
To get it running under Forms Authentication I've:
Edited the web.config file to enable Forms Authentication (and delete the Windows Authentication configuration):
<authentication mode="Forms">
<forms name=".ASPXAUTH" loginUrl="logon.aspx" defaultUrl="index.aspx" protection="All" path="/" timeout="30" />
</authentication>
Created a standard logon.aspx and logon.aspx.cs code behind page to take a user input name and password, and create a authentication cookie when the logon was successful, and then redirected the user to the root page of the web site, which is a silverlight application:
private void cmdLogin_ServerClick( object sender, System.EventArgs e )
{
if ( ValidateUser( txtUserName.Value, txtUserPass.Value ) )
{
FormsAuthentication.SetAuthCookie(txtUserName.Value, true);
var cookie = FormsAuthentication.GetAuthCookie(txtUserName.Value, true);
cookie.Domain = "mymachine.mydomain.com";
this.Response.AppendCookie(cookie);
string strRedirect;
strRedirect = Request["ReturnUrl"];
if ( strRedirect == null )
strRedirect = "index.aspx";
Response.Redirect( strRedirect, true );
}
}
So the redirect after successfully logging in launches my silverlight application.
However the user is not authenticated when executing the Silverlight startup code:
public App()
{
InitializeComponent();
var webContext = new WebContext();
webContext.Authentication = new FormsAuthentication();
ApplicationLifetimeObjects.Add( webContext );
}
private void ApplicationStartup( object sender, StartupEventArgs e )
{
Resources.Add( "WebContext", WebContext.Current );
// This will automatically authenticate a user when using windows authentication
// or when the user chose "Keep me signed in" on a previous login attempt
WebContext.Current.Authentication.LoadUser(ApplicationUserLoaded, null);
// Show some UI to the user while LoadUser is in progress
InitializeRootVisual();
}
The error occurs in the ApplicationUserLoaded method, which always has its HasError property set to true on entry to the method.
private void ApplicationUserLoaded( LoadUserOperation operation )
{
if((operation != null) && operation.HasError)
{
operation.MarkErrorAsHandled();
HandlerShowWebServiceCallBackError(operation.Error, "Error loading user context.");
return;
}
...
}
The error reported is as follows - from what it appears to me is that the user isn't considered authenticated on entry to the silverlight app, so it is directing the code to try to return the logon page, which is returning data unexpected by the silverlight app:
An exception occurred while attempting to contact the web service.
Please try again, and if the error persists, contact your administrator.
Error details:
Error loading user context.
Exception details:
Load operation failed for query 'GetUser'. The remote server returned an error: NotFound.
Any ideas?
Based on everything I read, this is supposed to be pretty simple and just work - so I'm obviously making a very basic error.
I'm wondering if after I authenticate the user on my logon.aspx web page, I need to somehow pass an authenticated WebContext instance over from the logon page to my silverlight application instead of creating a new instance in the silverlight app startup code - but have no idea how to do that.
Appreciate any or all suggestions.
I suspect the Response.Redirect("...", true);
According to this article you should pass false to keep the session.

FormsAuthentication.RedirectFromLoginPage to a custom page

Hi i'm using the FormsAuthentication.RedirectFromLoginPage for the user login and for redirect to default.aspx page.
I want that if a user called admin do the login is redirected to the page admin.aspx
Is it possible?
Try this, I think it's the closest you will get with a simple solution:
FormsAuthentication.SetAuthCookie(username, true);
Response.Redirect("mypage.aspx");
Authenticating Users
Assuming you have gone through my previous article mentioned above, you have a login page. Now when user clicks Login button Authenticate method fires, lets see code for that method.
protected void Login1_Authenticate(object sender, AuthenticateEventArgs e)
{
string userName = Login1.UserName;
string password = Login1.Password;
bool rememberUserName = Login1.RememberMeSet;
// for this demo purpose, I am storing user details into xml file
string dataPath = Server.MapPath("~/App_Data/UserInformation.xml");
DataSet dSet = new DataSet();
dSet.ReadXml(dataPath);
DataRow[] rows = dSet.Tables[0].Select(" UserName = '" + userName + "' AND Password = '" + password + "'");
// record validated
if (rows.Length > 0)
{
// get the role now
string roles = rows[0]["Roles"].ToString();
// Create forms authentication ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
userName, // Username to be associated with this ticket
DateTime.Now, // Date/time ticket was issued
DateTime.Now.AddMinutes(50), // Date and time the cookie will expire
rememberUserName, // if user has chcked rememebr me then create persistent cookie
roles, // store the user data, in this case roles of the user
FormsAuthentication.FormsCookiePath); // Cookie path specified in the web.config file in <Forms> tag if any.
// To give more security it is suggested to hash it
string hashCookies = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(FormsAuthentication.FormsCookieName, hashCookies); // Hashed ticket
// Add the cookie to the response, user browser
Response.Cookies.Add(cookie); // Get the requested page from the url
string returnUrl = Request.QueryString["ReturnUrl"];
// check if it exists, if not then redirect to default page
if (returnUrl == null) returnUrl = "~/Default.aspx";
Response.Redirect(returnUrl);
}
else // wrong username and password
{
// do nothing, Login control will automatically show the failure message
// if you are not using Login control, show the failure message explicitely
}
}
you can check it by placing hard core role name or by fetching user roll from database. i have modified this for my entity framework.
TestEntities entities = new TestEntities();
var user = (from s in entities.UserTables
where s.UserName == loginControl.UserName
&& s.Password == loginControl.Password
select s).SingleOrDefault();
and placed the user role as:
user.Role
Along this you have do some changes in the Global.asax file
Till now we have set the Forms Authentication ticket with required details even the user roles into the cookie, now how to retrive that information on every request and find that a request is coming from which role type? To do that we need to use Application_AuthenticateRequest event of the Global.asx file. See the code below.
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
// look if any security information exists for this request
if (HttpContext.Current.User != null)
{
// see if this user is authenticated, any authenticated cookie (ticket) exists for this user
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
// see if the authentication is done using FormsAuthentication
if (HttpContext.Current.User.Identity is FormsIdentity)
{
// Get the roles stored for this request from the ticket
// get the identity of the user
FormsIdentity identity = (FormsIdentity)HttpContext.Current.User.Identity;
// get the forms authetication ticket of the user
FormsAuthenticationTicket ticket = identity.Ticket;
// get the roles stored as UserData into the ticket
string[] roles = ticket.UserData.Split(',');
// create generic principal and assign it to the current request
HttpContext.Current.User = new System.Security.Principal.GenericPrincipal(identity, roles);
}
}
}
}
In this even, after checking if user exists, he/she is authenticated and the identy type of th user is FormsIdentity, I am getting the current Identity of the user and getting the ticket I have set at the time of Authentiacting. Once I have the authenticated ticket, I just got the UserData from the ticket and split it to get roles (remember, we had stored the roles as comma separated values). Now, we have current users roles so we can pass the roles of the current user into the GenericPrincipal object along with the current identity and assign this to the curent user object. This will enable us to use the IsInRole method to check if a particular user belongs to a particular role or not.
How to Check if user has a particular role?
To check if a user belong to a particulr role, use below code. This code will return true if the current record is coming from the user who is authenticated and has role as admin.
HttpContext.Current.User.IsInRole( "admin" )
How to check if user is authenticated?
To check if the user is authenticated or not, use below code.
HttpContext.Current.User.Identity.IsAuthenticated
To get UserName of the Authenticated User
HttpContext.Current.User.Identity.Name
Remember on thing .. this code require some webconfig settings in the forms tag as:
Add following Authentication setting into your web.config file under .
<authentication mode="Forms">
<forms defaultUrl="default.aspx" loginUrl="~/login.aspx" slidingExpiration="true" timeout="20" ></forms>
</authentication>
For every user if you want to secure a particular folder, you can place setting for them either in parent web.config file (root folder) or web.config file of that folder.
Specify Role settings for the folder in root web.config file (in this case for Admin)
<location path="Admin">
<system.web>
<authorization>
<allow roles="admin"/>
<deny users="*"/>
</authorization>
</system.web>
</location>
Write this code outside but under tag in the root's web.config file. Here, I am specifying that if the path contains the name of folder Admin then only user with "admin" roles are allowed and all other users are denied.
Specify Role settings for the folder in folder specific web.config file (in this case for User)
<system.web>
<authorization>
<allow roles="User"/>
<deny users="*"/>
</authorization>
</system.web>
Write this code into web.config file user folder. You can specify the setting for the user in root's web.config file too, the way I have done for the Admin above. This is just another way of specifying the settings. This settings should be placed under tag.
Specify setting for Authenticated user
<system.web>
<authorization>
<deny users="?"/>
</authorization>
</system.web>
Write this code into web.config file of the Secure folder. This is specifying that all anonymous users are denied for this folder and only Authenticated users are allowed irrespective of their roles.
hope this will give you little idea to solve your problem. it is working fine for me.
hope you will also solve your problem.
If you are using the ASP.NET MembershipProvider login control, you can write your logic in the LoggedIn event
<asp:Login id="Login1" runat="server" OnLoggedIn="OnLoggedIn"></asp:Login>
protecetd void OnLoggedIn(object sender, EventArgs e)
{
if(Roles.IsUserInRole(User.Identity.Name, "Administrators"))
{
//Redirect to admin page
Response.Redirect("~/Admin.aspx");
}
}
Don't forget to put some protection on the admin.aspx page aswell, incase someone types in the url directly
The default behavior is to redirect to the originally requested resource, so if a user tried to access 'admin.aspx' and isn't authenticated, the user is sent to the login page. After successfully authenticating, the user is sent to the originally requested url (admin.aspx).
user -> "admin.aspx" -> noauth -> login -> "admin.aspx"
So instead of manually trying to send users somewhere, is using this default behavior not going to work for you? The default behavior is actually "robust" (it can be "admin2.aspx", "admin3.aspx" and so on... you can have any number of "protected resources" and the built in process handles all of it....)

Resources