Search code examples
c#asp.net-coreidentityserver4

How to return a user to application after account creation


I have an Identity server using Identity server 4. When a new user registers we send them a conformation email

var callbackUrl = Url.EmailConfirmationLink(user.Id.ToString(), code, Request.Scheme);
            if (_emailSender.SendEmailConfirmation(model.Email, callbackUrl, out var errors))
                return RedirectToAction("CreateUserConfirmation");

The url sent to the user looks like this

http://localhost:5000/Account/ConfirmEmail?userId=21248584&code=CfDJ8Fjc0B4fXChAiNkOxmI4n6Tm8kxBYVt59xoQ7QqEiE8EdPVknVaQaMq7HACmGUGANjSc6LY0OjvWye%2BcZB8J9YMgB84uCsPRTRzno2W%2B1XtHsYD7zn%2FzZSxhCObppyj2ezPbY7O8wa18a0gvfqrHB2jEWjOxX2eauZkvRUvjxYGBIch0dN8A94VgO7qbTssmcsbI3kbSAmAP4a4%2B05iVK2Y%3D&returnUrl=/connect/authorize/callback?client_id=XenaClient&redirect_uri=http%3A%2F%2Flocalhost%3A49000%2Fsignin-oidc&response_mode=form_post&response_type=id_token%20token%20code&scope=openid%20profile%20testapi&state=OpenIdConnect.AuthenticationProperties%3DoJ7JIbKOpJI6pQuFrpY7YmrqyU2x-8TbuBM_d2XOTdZZZAMYW_RXfckkwDI-G88UkChcDlxYKmpba688bTKUt7VMKPz6Js8dQtyE2goLJASrSLmItXbynP0XC4o7DiM9FQQ_HcsB6vtX4tGFNvFfmAascO_ZyUZM63vHg6ntBBsm4CnbulgqDcECrjFCqpafiqw4gw&nonce=636705184638441890.ZjhkOTIz0NjAtNDRmZS00ODgwLWIwOWQtOWY2YWRkNTMwMTBhYmE4NjQ3MjYtMTllNS00ZWFlLWExMTEtYTRkNzQ2Y2JmYjFm

Redirect back to the application

When the application directs to the Identity server for user login it sends with it a returnurl. This url is used to return the user back to the application after they have logged in

returnurl=/connect/authorize/callback?client_id=XenaClient&redirect_uri=http%3A%2F%2Flocalhost%3A49000%2Fsignin-oidc&response_mode=form_post&response_type=id_token%20token%20code&scope=openid%20profile%20testapi&state=OpenIdConnect.AuthenticationProperties%3Duqw4onaEn-5TgYhVg5nQnRpvZYC1C8y12c5VTheRQlVI5y6GzTzgJCsuPTx_NoPVIFXY2ZSZKbhs4VxQ0HjJik4LGF0E2ToFAFDuonTJC3WwNSlFPN5eTrsbqebms-fqZq7dcpUOjhYU-kLpGcKSyaSXe5qr0EOanqIdEyV9H0EZolq38pjvBJWFf0bWC6KPNCZ4Nw&nonce=636705214204045725.YWI1YmVhNDQtYTE4MC00ZDIwLWJmMDQtYjA3YzNmMGYyODg3ZTNhNTNlMmEtNzQ1YS00M2Y4LWI2N2YtODY4MDg5OGNmYzNj

This works fine with existing users.

Redirect new user

When a new user registers their account the email they are sent does not redirect them back to the application. They are left stuck on the Identity server itself.

Add returnurl to confirm email (What I tried)

What I tried to do was tack the returnurl onto the confirm url email. So that when the user confirms their email they are routed back

 public async Task<IActionResult> ConfirmEmail([FromQuery] string userId, [FromQuery] string code,[FromQuery]  string returnUrl = null)
    {
      ............

     return (returnUrl == null)
            ? RedirectToAction("Login")
            : RedirectToLocal(returnUrl);
    }

The huge url sent in the email looks like this

http://localhost:5000/Account/ConfirmEmail?userId=21248584&code=CfDJ8Fjc0B4fXChAiNkOxmI4n6Tm8kxBYVt59xoQ7QqEiE8EdPVknVaQaMq7HACmGUGANjSc6LY0OjvWye%2BcZB8J9YMgB84uCsPRTRzno2W%2B1XtHsYD7zn%2FzZSxhCObppyj2ezPbY7O8wa18a0gvfqrHB2jEWjOxX2eauZkvRUvjxYGBIch0dN8A94VgO7qbTssmcsbI3kbSAmAP4a4%2B05iVK2Y%3D&returnUrl=/connect/authorize/callback?client_id=XenaClient&redirect_uri=http%3A%2F%2Flocalhost%3A49000%2Fsignin-oidc&response_mode=form_post&response_type=id_token%20token%20code&scope=openid%20profile%20testapi&state=OpenIdConnect.AuthenticationProperties%3DoJ7JIbKOpJI6pQuFrpY7YmrqyU2x-8TbuBM_d2XOTdZZZAMYW_RXfckkwDI-G88UkChcDlxYKmpba688bTKUt7VMKPz6Js8dQtyE2goLJASrSLmItXbynP0XC4o7DiM9FQQ_HcsB6vtX4tGFNvFfmAascO_ZyUZM63vHg6ntBBsm4CnbulgqDcECrjFCqpafiqw4gw&nonce=636705184638441890.ZjhkOTIz0NjAtNDRmZS00ODgwLWIwOWQtOWY2YWRkNTMwMTBhYmE4NjQ3MjYtMTllNS00ZWFlLWExMTEtYTRkNzQ2Y2JmYjFm&returnurl=/connect/authorize/callback?client_id=XenaClient&redirect_uri=http%3A%2F%2Flocalhost%3A49000%2Fsignin-oidc&response_mode=form_post&response_type=id_token%20token%20code&scope=openid%20profile%20testapi&state=OpenIdConnect.AuthenticationProperties%3Duqw4onaEn-5TgYhVg5nQnRpvZYC1C8y12c5VTheRQlVI5y6GzTzgJCsuPTx_NoPVIFXY2ZSZKbhs4VxQ0HjJik4LGF0E2ToFAFDuonTJC3WwNSlFPN5eTrsbqebms-fqZq7dcpUOjhYU-kLpGcKSyaSXe5qr0EOanqIdEyV9H0EZolq38pjvBJWFf0bWC6KPNCZ4Nw&nonce=636705214204045725.YWI1YmVhNDQtYTE4MC00ZDIwLWJmMDQtYjA3YzNmMGYyODg3ZTNhNTNlMmEtNzQ1YS00M2Y4LWI2N2YtODY4MDg5OGNmYzNj

URL to long

The issue is when it comes into the method above returnurl is /connect/authorize/callback?client_id=XenaClient which is not the full url I am sending 1457 long which I suspect is too long for the server to parse?

Question

So how do I return the user to the application if the request is too long. Note defaulting it back to a single url wont work as we have several applications that use this identity server and it will not be possible to know which application the user originally came from.


Solution

  • I finally got this working. When the user registers i have the returnUrl, which contains the redirect_uri. For now we are going to assume that the application is on the same domain and port. So i rip out the redirect uri.

    var pat = "redirect_uri=([^&]+)";
    var r = new Regex(pat, RegexOptions.IgnoreCase);
    var m = r.Match(returnurl);
    

    Then I can add this smaller uri to the email confromation link

    public static string EmailConfirmationLink(this IUrlHelper urlHelper, string userId, string code, string scheme, string returnUrl = "")
        {
            return urlHelper.Action(
                action: nameof(AccountController.ConfirmEmail),
                controller: "Account",
                values: new { userId, code, returnUrl },
                protocol: scheme);
        }
    

    When the user clicks on the link I confirm their email and reroutethem back to the domain i ripped from the redirect uri

    return Redirect(returnUrl);
    

    issues

    Now this should work assuming that the redirect uri and the host of the original application are on the same domain. To my knowledge they currently are. A bonus result will be that if they try to use a localhost redirect uri in production its going to cause major issues and not be able to redirect properly. This is probably a good thing as i would rather third party developers not have localhost as a redirect uri in production.

    If anyone has a better solution i would still love to hear it as this feels like a slight hack. But we needed this dealt with ASAP so i have decided to live with the hack until we find a better solution