Auth0 Home Blog Docs

apostrophe at the end of the URL redirect after successful login

auth0
login
aspnet-core
redirect
aspnet-mvc

#1

After a successful login from my ASP .Net Core 1.1 MVC web app, Auth0 is redirecting me to:

http://localhost:60856/

As you can see, there is an apostrophe at the end of the URL, and I cannot for the life of me figure out why. Where should I be checking for this? This is my controller:

public class AccountController : Controller
{
    public IActionResult Login(string returnUrl = "/")
    {
        return new ChallengeResult("Auth0", new AuthenticationProperties() { RedirectUri = returnUrl });
    }
}

and my appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Auth0": {
    "Domain": "codexcreations.eu.auth0.com",
    "TokenEndpoint": "https://codexcreations.eu.auth0.com/oauth/token/",
    "ClientId": "hidden for this post",
    "ClientSecret": "hidden for this post",
    "CallbackUrl": "http://localhost:65095/signin-auth0",
    "ApiIdentifier": "http://inspections.propworx.co.za/api/"
  }
}

and my unfortunately long Startup.cs (sorry…)

public class Startup
{
    public IConfigurationRoot Configuration { get; }
    public IHostingEnvironment HostingEnvironment { get; }
    
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(env.ContentRootPath)
            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);
    
            if (env.IsDevelopment())
            {
                builder.AddUserSecrets<Startup>();
            }
    
            builder.AddEnvironmentVariables();
            Configuration = builder.Build();
    
            HostingEnvironment = env;
        }
    
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication(
                options => options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
                
            services.AddMvc();
    
            services.AddOptions();
    
            services.Configure<Auth0Settings>(Configuration.GetSection("Auth0"));
        }
    
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<Auth0Settings> auth0Settings)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
    
            app.UseStaticFiles();
    
            app.UseJwtBearerAuthentication(new JwtBearerOptions
            {
                Audience = auth0Settings.Value.ApiIdentifier,
                Authority = $"https://{auth0Settings.Value.Domain}"
            });
    
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                    AutomaticAuthenticate = true,
                    AutomaticChallenge = true,
    
                    Events = new CookieAuthenticationEvents()
                    {
                        OnRedirectToLogin = ctx =>
                        {
                            if (!(IsAjaxRequest(ctx.Request) || IsApiRequest(ctx.Request)))
                            {
                                ctx.Response.Redirect(ctx.RedirectUri);
                                return Task.CompletedTask;
                            }
                            ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
                            return ctx.Response.WriteAsync("Unauthorized");
                        }
                    }
                });
    
                var options = new OpenIdConnectOptions("Auth0")
                {
                    Authority = $"https://{auth0Settings.Value.Domain}",
                    ClientId = auth0Settings.Value.ClientId,
                    ClientSecret = auth0Settings.Value.ClientSecret,

                    AutomaticAuthenticate = false,
                    AutomaticChallenge = false,
    
                    ResponseType = "code",
    
                    CallbackPath = new PathString("/signin-auth0"),
    
                    ClaimsIssuer = "Auth0",
    
                    SaveTokens = true,
    
                    Events = new OpenIdConnectEvents
                    {
                        OnRedirectToIdentityProviderForSignOut = (context) =>
                        {
                            var logoutUri = $"https://{auth0Settings.Value.Domain}/v2/logout?client_id={auth0Settings.Value.ClientId}";
    
                            var postLogoutUri = context.Properties.RedirectUri;
                            if (!string.IsNullOrEmpty(postLogoutUri))
                            {
                                if (postLogoutUri.StartsWith("/"))
                                {
                                    // transform to absolute
                                    var request = context.Request;
                                    postLogoutUri = request.Scheme + "://" + request.Host + request.PathBase + postLogoutUri;
                                }
                                logoutUri += $"&returnTo={ Uri.EscapeDataString(postLogoutUri)}";
                            }
    
                            context.Response.Redirect(logoutUri);
                            context.HandleResponse();
    
                            return Task.CompletedTask;
                        }
                    }
                };
                options.Scope.Clear();
                options.Scope.Add("openid");
                app.UseOpenIdConnectAuthentication(options);
    
                app.UseMvc(routes =>
                {
                    routes.MapRoute(
                        name: "default",
                        template: "{controller=Home}/{action=Index}/{id?}");
                });
            }
    
            private static bool IsAjaxRequest(HttpRequest request)
            {
                var query = request.Query;
                if ((query != null) && (query"X-Requested-With"] == "XMLHttpRequest"))
                {
                    return true;
                }
                IHeaderDictionary headers = request.Headers;
                return ((headers != null) && (headers"X-Requested-With"] == "XMLHttpRequest"));
            }
    
            private static bool IsApiRequest(HttpRequest request)
            {
    
                return request.Path.StartsWithSegments(new PathString("/api"));
            }
        }
    }

#2

Technically it can’t be the Auth0 service redirecting to that URL directly because the service will only redirect to an URL that exists in the Allowed Callback URLs list and I’m assuming you did not add a callback URL with an apostrophe to that list.

Based on your configuration, Auth0 should be redirecting to /signin-auth0 and it’s then additional logic within your application that may be causing the final redirect which includes the apostrophe.

The first thing you need to find out is which request precedes the one with the apostrophe. In your browser network tools you can find this information. Knowing which request is causing the redirect may point you directly to the root cause, but if not try to cut down your application to the minimal code that still reproduces the issue. For example, you have redirect logic in your cookie authentication configuration check if the issue still occurs without it.


#3

Thanks for the reply jmangelo.You have a good point - since none of my “Allowed Callback URLs” have anything with an apostrophe in it.

I did run it on Chrome with the developer tools open on Network. As soon as I click OK on the Autg0 login screen, it redirects to http://localhost:60856/’ and if I look at the activity, I see two items:

signin-auth0 (Status: 203, Type: x-www-form-urlencoded, Initiator: codexcreations.eu.auth0.com/login/callback:1)
' (Status: 404, Type: document, initiator: signin-auth0)

Trying to figure it out… will let you know if I do. Thanks again…


#4