Alex Hope O'Connor Alex Hope O'Connor - 1 year ago 64
ASP.NET (C#) Question

ASP.NET WebAPI 2.2 SPA with social login and no MVC dependencies

I have been designing an application which is just a statically served client page designed to use bearer tokens to authenticate with the backing API, however recently I have been trying to add social login options to the back-end but have found it very difficult to find any examples not using MVC dependencies which I would like to avoid if possible.

This question was a great help to get started: ASP.NET Web API social authentication for Web and Mobile

However I have been struggling to get my project to work in the same manor, basically in the question I referenced he has configured a

like this:

OAuthOptions = new OAuthAuthorizationServerOptions
TokenEndpointPath = new PathString("/token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/account/externallogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
//AllowInsecureHttp = false

Also in his backing api account controller he has the following action:

[Route("ExternalLogin", Name = "ExternalLogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider, string error = null)

In this example I have not been able to figure out what the first parameter of the
(template) is actually referencing in the project, if anything, could someone maybe explain what it is doing in this context?

Now when running the sample project provided in the question sending a GET request to 'api/Account/ExternalLogin' the request will be handled on the action in his API account controller and I assume it has something to do with
but am getting a little out of my depth here and struggling to find strong examples of other usages of this attribute.

However I am fairly certain I have configured my WebAPI project correctly in the way he has described, however when sending a GET request to my
it is not handled on my API account controller but instead by my implementation of
which returns a 'invalid_request' error.

Can anyone think of something that I might be overlooking which is causing my API account controller action to be ignored?

I also had a read through this article but it seems to have been written in an older version of WebAPI:

Thanks for any help,

Answer Source

Without actually seeing your GET requests that are being made, I can only assume that they do not meet expectations by the OAuth provider.

The provider first validates the request being made, THEN it hands control over to the endpoint's controller. Your code is most likely correct, it's just that the request is malformed.

I made a new project and was able to replicate the issue you describe by making a get request to the AuthorizeEndpointPath. Unfortunately, there's not much to go off of as to why, however if you decompile source, or manage to find the source, you can see what's going on here.

Decompiling the calling code of ApplicationOAuthProvider.ValidateClientRedirectUri I get:

  await this.Options.Provider.ValidateClientRedirectUri(clientContext);
  if (!clientContext.IsValidated)
    LoggerExtensions.WriteVerbose(this._logger, "Unable to validate client information");
    flag = await this.SendErrorRedirectAsync(clientContext, (BaseValidatingContext<OAuthAuthorizationServerOptions>) clientContext);
    OAuthValidateAuthorizeRequestContext validatingContext = new OAuthValidateAuthorizeRequestContext(this.Context, this.Options, authorizeRequest, clientContext);
    if (string.IsNullOrEmpty(authorizeRequest.ResponseType))
      LoggerExtensions.WriteVerbose(this._logger, "Authorize endpoint request missing required response_type parameter");
    else if (!authorizeRequest.IsAuthorizationCodeGrantType && !authorizeRequest.IsImplicitGrantType)
      LoggerExtensions.WriteVerbose(this._logger, "Authorize endpoint request contains unsupported response_type parameter");
      await this.Options.Provider.ValidateAuthorizeRequest(validatingContext);
    if (!validatingContext.IsValidated)
      flag = await this.SendErrorRedirectAsync(clientContext, (BaseValidatingContext<OAuthAuthorizationServerOptions>) validatingContext);
      this._clientContext = clientContext;
      this._authorizeEndpointRequest = authorizeRequest;
      OAuthAuthorizeEndpointContext authorizeEndpointContext = new OAuthAuthorizeEndpointContext(this.Context, this.Options, authorizeRequest);
      await this.Options.Provider.AuthorizeEndpoint(authorizeEndpointContext);
      flag = authorizeEndpointContext.IsRequestCompleted;

In this code, you can see that if the request has been validated and the request's specified ResponseType is not provided, it set's the context's error to "invalid_request".

I was able to get the request to go through to the ExternalLogin controller method successfully using the following request URI:


P.S. As far as the route attribute on the controller, the "template" field specifies the string that will be used as a template to match incoming request URIs against to determine where the request should be routed.

P.P.S. Actual source code for the decompiled snippet can be found here