So today I had the challenge of trying to create a "universal" auto-login function that would allow a 3rd party site to link to any page in an Umbraco site that integrated with an OpenIdConnect provider for logging users in. However, they wanted users to be automatically logged into the site as they would already be logged into the 3rd Party when they had access to these links.
A colleague of mine had already put some groundwork into this by setting up a route for /autologin that would perform the core actions, but it would always return to the home page of the site after trying to log them in.
When I came to review the code, I wasn't happy that they would always end up on the home page and I thought to myself, what if they wanted to be able to link to any page in the site. That presented the following questions:
The answer was, as it happens, more straight forward than I had imagined.
So, I set about researching it and I came across two resources that helped me solve the problem:
As mentioned, there was an existing controller set up to handle the inbound route of /autologin
. The action that this pointed to looked like this:
[HttpGet]
[AllowAnonymous]
public ActionResult Index()
{
if (Umbraco.MemberIsLoggedOn())
{
return Redirect("/");
}
else
{
// Request a redirect to the external login provider
return new ChallengeResult("OpenIdConnect",
Url.SurfaceAction<UmbracoIdentityAccountController>("ExternalLoginCallback", new { ReturnUrl = "/" }).Replace("autologin", ""));
}
}
So the first thing that I had to do was update this action to allow for the redirect URL to be passed into the method, I also wanted to clean up the URL, specifically the query string as after reading the Propellent.io article, I was aware that it could have rogue &'s and ?'s left in it. So a quick update to the action turned it into:
[HttpGet]
[AllowAnonymous]
public ActionResult Index(string redirectUrl)
{
if (Umbraco.MemberIsLoggedOn())
{
return Redirect(redirectUrl.IfNullOrWhiteSpace("/").Trim("&").Trim("?"));
}
else
{
// Request a redirect to the external login provider
return new ChallengeResult("OpenIdConnect",
Url.SurfaceAction<UmbracoIdentityAccountController>("ExternalLoginCallback",
new { ReturnUrl = redirectUrl.IfNullOrWhiteSpace("/").Trim("&").Trim("?") }).Replace("autologin", ""));
}
}
Now for the fun part....
So, what did I need to do? Well I wanted to take a URL that looked like this domain/some/path/on/my/site?with=queryParameters&another=parameter&autologin=true
(well I don't care what autologin is equal to to be honest, just that it was in the query string as a parameter), and redirect it to /autologin?redirectUrl=originalUrlWithQueryStringExcludingAutoLoginParameter
.
The Propellent.io blog post gave the foundations for this, however they were only doing a Rewrite so a small change, and an understanding of the limitations meant that I could convert it to a redirect. This redirect, however, could potentially leave rogue &'s and ?'s on the end as mentioned previously. Not an issue as I could strip that out as shown above at my autologin action.
The other part that was needed was that I wanted to ensure my redirect Url was safely encoded to be part of a URL else I was concerned about potentially strange behaviours so I did a quick google and came across the StackOverflow question above that gave me my answer: UrlEncode
is a function built straight into the IIS Rewrite module! 🎉🎉
By combining the information from both sources I was able to come up with the following IIS Rewrite Rule:
<rule name="RemoveAutoLoginQueryStringParameter" enabled="true" stopProcessing="true">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll">
<add input="{QUERY_STRING}" pattern="(.*)(autologin=(((?!&).)*))(&?)(.*)" />
</conditions>
<action type="Redirect" redirectType="Temporary" url="autologin?redirectUrl={UrlEncode:/{R:1}?{C:1}{C:6}}" appendQueryString="false" />
</rule>
The best part about doing this bit of work was that it worked first time! Yep I was pretty shocked!
Until next time! Have fun and keep on coding!