0

I have a Asp.Net Web API 2 using Token based authentication (OAuth2).

I have implemented Web API versioning using aspnet-api-versioning.

So now I have three different versions of my API. It's really great, I can now change V3 without affecting the current API.

But the /token endpoint is not versioned because it is not in my controller. It's in the Providers.

I searched but couldn't find anything helpful.

Kishan Vaishnav
  • 2,273
  • 1
  • 17
  • 45

2 Answers2

0

We can register more than one token endpoint in the Startup.Auth.cs

So here's what I did:

        OAuthOptions = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/Token"),
            Provider = new ApplicationOAuthProvider(PublicClientId),
            AuthorizeEndpointPath = new PathString("/Account/ExternalLogin"),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
            AllowInsecureHttp = true, //Allow HTTP to send username password.
        };
        app.UseOAuthBearerTokens(OAuthOptions);

        OAuthOptionsV3 = new OAuthAuthorizationServerOptions
        {
            TokenEndpointPath = new PathString("/api/V3/Accounts/Token"),
            Provider = new ApplicationOAuthProvider2(PublicClientId),
            AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
            AllowInsecureHttp = true, //Allow HTTP to send username password.
        };
        // Enable the application to use bearer tokens to authenticate users
        app.UseOAuthBearerTokens(OAuthOptionsV3);

So now I have different token endpoint for each version.

Kishan Vaishnav
  • 2,273
  • 1
  • 17
  • 45
0

I'm not familiar with this particular setup, but this looks like middleware. There isn't quite enough information here to provide you with a specific answer, but your goals should be achievable in one of a few ways:

Option 1 - Use the Conventions API

If you authorization endpoint is actually a controller (though I think it is not), you can use the Conventions API like so:

services.AddApiVersioning(options =>
{
    options.Conventions.Controller<OAuthController>().IsApiVersionNeutral();
}

Conventions was specifically meant to deal with a scenario where a controller might be externally defined and you don't have any control over the source code.

Option 2 - Use a Custom Convention

Middleware could create actions dynamically. As long as actions are actually produced, then you can use a custom IControllerConvention. You would be passed the ControllerModel which contains the actions you need to version. Assuming this is the correct behavior, you'd be looking for matching actions in the source model and then you can apply it to the controller conventions with something like:

public class MyConventions : IControllerConvention
{
  public bool Apply(IControllerConventionBuilder controller, ControllerModel controllerModel)
  {
    var method = // TODO: resolve the target method from controllerModel

    if (method == null)
    {
      return false;
    }

    controller.Action(method).IsApiVersionNeutral();
    return false;
  }
}

Option 3 - In Middleware

If this is pure middleware, API versioning isn't directly supported there. You can, however, support versioning on your own if the pipeline is composed properly. Specifically, API Versioning must come before other parts of middleware that need it. This usually happens automatically, but if you need to control registration, you need to change your setup to handle it manually like this:

services.AddApiVersioning(options => options.RegisterMiddleware = false);

// ... inside application setup
services.UseApiVersioning();

The API Versioning middleware doesn't really do much of anything special. It merely adds a pipeline feature. As long as that's before your other middleware, it will be available downstream like this:

var feature = context.Features.Get<IApiVersioningFeature>();

// the raw, unparsed API version, if any
var rawApiVersion = feature.RawApiVersion;

// the parse API version; will be null if no version is specified
// or the value cannot be parsed
var apiVersion = feature.ApiVersion;

// TODO: enforce versioning policies within the middleware

Option 4 - Use the API Explorer

If none of the previous approaches will work for you, you can leverage the API Explorer extensions for API Versioning to build your configuration (as above) from discovered APIs. This would have the advantage of not being hardcoded or require changes every time you release a new version.

Your application startup configuration would change to something like this:

public void Configure(IApplicationBuilder app, IApiVersionDescriptionProvider provider)
{
  foreach (var description in provider.ApiVersionDescriptions)
  {
    var options = new OAuthAuthorizationServerOptions()
    {
      TokenEndpointPath = new PathString($"/api/{description.GroupName}/Accounts/Token"),
      Provider = new ApplicationOAuthProvider2(PublicClientId),
      AccessTokenExpireTimeSpan = TimeSpan.FromDays(TokenExpirationInDays),
      AllowInsecureHttp = true,
    };

    app.UseOAuthBearerTokens(options);
  }
}
Chris Martinez
  • 3,185
  • 12
  • 28
  • The first three options are versioning methods which are not appropriate as my endpoint is not a controller method. The last option is appropriate for the token endpoint but I wanted different implementation for each version. – Kishan Vaishnav Feb 25 '20 at 08:42
  • Option 4 would align to what you mentioned (and I assumed) worked for you, but as a loop over all API versions. If you have specific customization or other policies per API version, you may need a mapping that you intersect with the defined API versions. For example, you might have a factory which returns the **OAuthAuthorizationServerOptions** for a particular API version. Then you simply need to call `app.UseOAuthBearerTokens(options)` as shown. – Chris Martinez Mar 04 '20 at 19:13