ExecuteAsync results in 401 Unauthorized if request content type isn't 'text/html'

We are running a service that calls AuthorizeAttribute.ExecuteAsync in a GlobalRequestFilter. When a request is made using the JsonServiceClient with pre-authentication everything works as expected. However, when using Postman (or similar) to make a JSON request with a bearer token, a 401 is immediately returned rather than prompting the service to authenticate with it’s auth provider (IdentityServer4).

Below is a snippet from the AuthenticateAttribute class and it appears the 401 is due to this.DoHtmlRedirectIfConfigured(req, res, true) evaluating to false since the ContentType is set to ‘application/json’ instead of ‘text/html’.

public override async Task ExecuteAsync(IRequest req, IResponse res, object requestDto) {

...

	if (session == null || !authProviders.Any(x => session.IsAuthorized(x.Provider)))
	{
		if (this.DoHtmlRedirectIfConfigured(req, res, true))
			return;
		await AuthProvider.HandleFailedAuth(authProviders[0], session, req, res);
	}
}

Can you suggest the correct way to override this behaviour so that the service authenticates with it’s auth provider regardless of the content type? Many thanks.

You need to provide the Auth Token with the Request.

It’s only failing because the Request isn’t authenticated, only the error response behavior is different where for HTML (i.e. browser) requests it redirects users to the login page, for all other content types it can’t do that so returns a 401.

Thanks for the clarification of the code snippet.

So in Postman I’m setting the Authorization header with the bearer token when making the request. Is this not the same as providing the Auth Token with the Request?

That’s totally dependent on your Auth Provider, i.e. the Postman (and any) request needs to send whatever your Auth Provider expects. I recommend comparing the raw HTTP Request Headers of a successful request and the HTTP Request Headers of a Postman request to see the differences.

So this works:

  1. Use Postman to obtain an access (bearer) token from Identity Server. - 200 OK
  2. Use Postman to make a request to myservice.io/auth/IdentityServer - 200 OK
  3. Use Postman to make a request to myservice.io/mysecrets - 200 OK

Do 2 and 3 have to be separate requests or can they be combined?

Typically as long as you have an access token that’s all you need, but I’m not familiar with the implementation of the Community IdentityServer Auth Provider you’re using to be able to verify at which point you’ve obtained the Access Token or if you’ve just got the Request Token.

Raise an issue on the Community Auth Provider GitHub project if you have any questions on its implementation, or if you know the developer of the project’s username in the forums you can @ them here.