4

Related questions

Problem

I have a service running under a specific path on a domain, e.g. https://www.example.com/myservice. The myservice path is dedicated to my service and other services have other paths at the same domain. It is setup like this in startup configure:

app.Map("/myservice", builder =>
{
    builder.UseStaticFiles();
    builder.UseMvcWithDefaultRoute();
});

I am using a library that implements a custom RemoteAuthenticationHandler. By default, the callback path routes to /x-callback which results in the browser trying to access https://www.example.com/x-callback.

Since my service does not process url's without the /myservice prefix I get a 404. Changing the URL in the browser to /myservice/x-callback manually loads the callback and all is fine.

I can set the callback path for the handler in startup options as expected in startup configure services.

services.AddSomething(options =>
{
    options.AddThingX((o) =>
    {
        o.CallbackPath = new PathString($"/myservice{o.CallbackPath}");
    });
});

When I set the callback path like that the browser tries to load /myservice/x-callback. But, this URL now returns a 404. It seems the handler for the callback also has its URL changed. Changing the URL in the browser to /myservice/myservice/x-callback loads the callback as expected.

The RemoteAuthenticationHandler

This is the code in the handler that handles the challenge and uses the callback path. It sets the callback path as a query parameter to the login url.

protected override Task HandleChallengeAsync(AuthenticationProperties properties)
{
    // Add options etc
    // ...
    // ...

    // This defines the login url, with a query parameter for the CallbackPath
    var loginUrl = GetLoginUrl(loginOptions);
    Response.Redirect(loginUrl);

    return Task.CompletedTask;
}

private string GetLoginUrl(MyServiceLoginOptions loginOptions)
{
    // This is where the return url is set. The return url
    // is used after login credentials are verified.
    return $"{Options.LoginPath}" +
            $"?returnUrl={UrlEncoder.Encode(Options.CallbackPath)}" +
            $"&loginOptions={UrlEncoder.Encode(_loginOptionsProtector.Protect(loginOptions))}";
}

The login controller

This is where the user can provide the credentials and have them verified. After verification, the user is redirected to the callback path.

private async Task<ActionResult> ChallengeComplete(LoginStatusRequest request, ChallengeResponse challengeResponse)
{
    // auth logic
    // ...
    // All is fine, the users credentials have been verified. Now
    // we can redirect to the CallbackPath.
    return Ok(Response.Finished(returnUri));
}

Note I could do a URL rewrite but if possible, I would like to use the "correct" /myservice path to avoid confusion and perhaps causing issues for other services (though very unlikely).

Question

How can I prefix the callback path with /myservice so my application can process it without also adding the duplicate prefix?

span
  • 5,405
  • 9
  • 57
  • 115
  • Where's your call to `UseAuthentication`? It's not shown inside `Map`, so is it on the root `IApplicationBuilder`? – Kirk Larkin Oct 08 '19 at 11:51
  • @KirkLarkin I'm using IdentityServer and it is inside `Configure` as you assume: `app.UseIdentityServer();`. – span Oct 08 '19 at 18:45
  • Disregarding the question and going right for a solution: would `o.CallbackPath = new PathString(o.CallBackPath);` work? – LosManos Oct 08 '19 at 19:42
  • @LosManos Nice try! Unfortunately no work :P. It redirects to `/x-callback` and not `/myservice/x-callback`. – span Oct 08 '19 at 19:56
  • Is it `new PathString` that fools you then? – LosManos Oct 08 '19 at 19:59
  • Can't see how. PathString only makes sure the string starts with `/` and does not end with `/`. The issue is that MyService only processes requests in the pipeline under the `/myservice` path due to the mapping. When I add that prefix to the callback path, the pipeline processes the request but now the `RemoteAuthenticationHandler` is no longer listening at the "app root" but rather under `/myservice/identity`, which is under the mapped path in the pipeline. It then results in the /myservice/myservice/x-callback. Perhaps there is some event I could listen to, to intercept the request. – span Oct 09 '19 at 06:19
  • Can you share the code in `RemoteAuthenticationHandler`? – Kahbazi Oct 09 '19 at 17:55
  • @Kahbazi Probably parts of it. Is there anything in particular you are after? – span Oct 09 '19 at 19:21
  • I wanna see the part that is using the `CallbackPath` – Kahbazi Oct 09 '19 at 19:25
  • @Kahbazi Great! I added the relevant code I hope :) – span Oct 09 '19 at 19:57

1 Answers1

5

MapMiddleware is adding the matched path to the Request.PathBase, so you can use it when creating the return url

string returnUrl = Context.Request.PathBase + Options.CallbackPath;
Kahbazi
  • 14,331
  • 3
  • 45
  • 76