span
span

Reputation: 5624

Using RemoteAuthenticationHandler CallbackPath with IApplicationBuilder path match

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?

Upvotes: 4

Views: 3221

Answers (1)

Kahbazi
Kahbazi

Reputation: 15005

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;

Upvotes: 5

Related Questions