The are times when we need to authenticate users on a server, then access services or resources on a different one.
How do we get the second server to know that the requesting user has successfully been authenticated?
Here is what I am used to do in the exposed scenario.
-
The client posts his login info to the authentication server (A-Server)
-
The A-Server validates login as usual, then informs the service (or resource) server (S-Server) that a user from the specified IP address has been authenticated
-
The S-Server generates a unique ID(GUID), stores it in cache, and returns it to the A-Server
-
The A-Server redirects the client to the S-Server, passing the unique ID as a query string parameter
-
The client contacts the S-Server presenting the unique ID
-
The S-Server validates the unique ID against his cache, and authorizes the client only if the requesting IP address is the expected one
Well, this may seem much complicated.
But it's funny to be coded, and not really so complicated.
Let's go through it step by step.
We will start with our login page, using integrated forms authentication.
web.config file
<authentication mode="Forms">
<forms loginUrl="Login.aspx">
<credentials passwordFormat="Clear">
<user name="authorized" password="user"/>
</credentials>
</forms>
</authentication>
Login.aspx
<asp:TextBox runat="server" ID="UsernameText" /><br />
<asp:TextBox runat="server" ID="PasswordText" TextMode="Password" /><br />
<asp:Button runat="server" Text="Login" OnClick="Login_Click" />
And finally, our login code behind
protected void Login_Click(object sender, EventArgs e)
{
bool success = FormsAuthentication.Authenticate(UsernameText.Text.Trim(), PasswordText.Text.Trim());
if (success)
{
FormsAuthentication.SetAuthCookie(UsernameText.Text.Trim(), false);
AuthorizationService.AuthorizationService authorizationService = new AuthorizationService.AuthorizationService();
Guid guid = authorizationService.Authorize(Request.UserHostAddress);
Response.Redirect("http://S-Server.com/SecuredResource.aspx?guid=" + guid.ToString());
}
}
So, here we have a reference to a WebService proxy.
This is the way our A-Server notifies S-Server about successful authentication of a user from a specified IP address.
On successful login, the page redirects the client to the secured resource on S-Server, passing the obtained unique id as a QueryString parameter.
Let's give a look to the WebService implementation.
[WebMethod]
public Guid Authorize(
string ipAddress) {
Guid guid = Guid.NewGuid();
HttpContext.Current.Cache.Insert(guid.ToString(),
ipAddress,
null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
TimeSpan.FromMinutes(5));
return guid;
}
This peace of code simply adds the IP address to the application cache, using a generated unique id as the key. The user must request the secured service/resource within 5 minutes. Otherwise the cache item will be cleared and the incoming request will no more be authorized.
Finally, here is the code to verify the incoming request to the secured resource.
Assume the secured resource is simply a page containing a secret message to be displayed only to authorized users:
SecuredResource.aspx
<asp:Label runat="server" ID="SecuredMessage" />
Here is the codebehind that validates the user and displays the message:
protected void Page_Load(
object sender, EventArgs e)
{
string guid = Request.QueryString[
"guid"];
string ipAddress = Request.UserHostAddress;
if (guid != null && Cache[guid] != null && Cache[guid].ToString() == ipAddress)
SecuredMessage.Text = "This is a secret message";
else
SecuredMessage.Text = "Unauthorized request";
}
This code will look for a cached item using the unique id passed in as a QueryString parameter.
If a value is found, it is compared to the incoming requesting host IP address.
If they match, the message is displayed.
We could add one more security check to ensure no one invokes the authorization web service but the A-Server.
This can be done in the web service Authorize() method implementation, verifying the requesting IP address: it should match the known S-Server IP.
Or we could add a second parameter as a secret password.
Adding an SSL self-signed certificate to encrypt communication between the two servers will enforce security.
That's all.
Have fun coding!