Why isn't OAuth2 client refreshing expired access_token? - spring-security-oauth2

I've got a client app configured with #EnableOAuth2Sso and #EnableZuulProxy, and a resource server (separate app) configured with #EnableOAuth2Resource. I can see that the client correctly authenticates to the resource server with Authorization: Bearer {access_token here}, but when once the access token expires, the proxied resource server request fails permanently.
[Edited]
I've modified my resource server by providing a custom RemoteTokenServices bean that uses OpenAM's /tokeninfo endpoint to decide whether an access_token remains valid. (The Spring-provided RemoteTokenServices bean attempts to POST, which gets a 405 from OpenAM). When I detect the access_token is invalid, I throw InvalidTokenException from my.spring.oauth2.OpenAMRemoteTokenServices#loadAuthentication. Now, my resource server is (I think correctly) sending HTTP 401 on the response to the client, in the case where the access_token has expired.
Still, the client is not attempting to refresh the token.
Maybe my mental model is wrong. I expect the client, in the case of expired access_token, to automatically use the refresh_token to obtain a new one. I don't know whether I think it should proactively refresh an access_token (within some epsilon before expiry time), or wait for a downstream request to fail and try refreshing then. But my client appears to be doing neither, and I can't tell why not.

As stated in this git issue: https://github.com/spring-guides/tut-spring-security-and-angular-js/issues/140, the problem might be related to the fact that with versions 1.4 and above of spring boot the Zuul filter that handles the downstream of access tokens to services (org.springframework.cloud.security.oauth2.proxy.OAuth2TokenRelayFilter) is missing a bean of type OAuth2RestTemplate, which is used by the filter itself to automatically handle the refresh_token grant when access tokens expire.
I had the same issue and I solved it by adding in a configuration class the following bean:
#Configuration
public class ZuulConfiguration {
#Bean
protected OAuth2RestTemplate oauth2RestTemplate(OAuth2ProtectedResourceDetails resource,
OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
}

Related

When an API is doing HTTP Token Authentication using Introspection, what should it return if the Introspection server is down or returns a 500?

When an API is doing HTTP Token Authentication using Introspection, what should it return if the Introspection server is down or returns a 500?
I am writing an API and want to adhere to Http standards. If the API fails to validate the token (due to the custom Introspection server being down or returning a 500) should the API return a 401, 422, or a 500?
Also, if this token introspection endpoint is not an Oauth endpoint should it return the same Http code?
I have done some research and found:
https://tools.ietf.org/id/draft-ietf-oauth-v2-bearer-11.xml#authn-header
This says: "If the protected resource request included an access token and failed authentication, the resource server SHOULD include the error attribute to provide the client with the reason why the access request was declined. The parameter value is described in Section 3.1. In addition, the resource server MAY include the error_description attribute to provide developers a human-readable explanation that is not meant to be displayed to end users."
https://tools.ietf.org/id/draft-ietf-oauth-v2-bearer-11.xml#resource-error-codes
This says: "The access token provided is expired, revoked, malformed, or invalid for other reasons. The resource SHOULD respond with the HTTP 401 (Unauthorized) status code."
Now the above make me think that for sure when using Oauth Introspection a 401 is the correct approach in the given case and not a 500. The reason being since the authentication was not a success = failure. Does this sound correct? Also, should this be the same for non-Oauth Introspection?
I reviewed Microsoft's code for their .net core API Introspection package. It shows they return a 401 in this case. The code is at: https://github.com/aspnet-contrib/AspNet.Security.OAuth.Extensions/blob/dev/src/AspNet.Security.OAuth.Introspection/OAuthIntrospectionHandler.cs
The relevant part is:
// Return a failed authentication result if the introspection
// request failed or if the "active" claim was false.
var payload = await GetIntrospectionPayloadAsync(token);
if (payload == null || !payload.Value<bool>(OAuthIntrospectionConstants.Claims.Active))
{
Context.Features.Set(new OAuthIntrospectionFeature
{
Error = new OAuthIntrospectionError
{
Error = OAuthIntrospectionConstants.Errors.InvalidToken,
ErrorDescription = "The access token is not valid."
}
});
return AuthenticateResult.Fail("Authentication failed because the authorization " +
"server rejected the access token.");
}
Finally I came accross this:
https://www.rfc-editor.org/rfc/rfc6750#section-2.1
Which says: "If the protected resource request does not include authentication
credentials or does not contain an access token that enables access
to the protected resource, the resource server MUST include the HTTP
"WWW-Authenticate" response header field; it MAY include it in
response to other conditions as well."
You definitely don't want a 4xx error, because it suggests that it might be solvable by the client or at least a client-side error. This is clearly an outage so your error code should at least start with 5.
If you are accessing a external service, that service is down or malfunctioning and the service is critical (e.g.: there's nothing you can do in terms of a fallback) the most appropriate status code might be 503 Service Unavailable.

How do I get log output from JwtSecurityTokenHandler?

I have an ASP.NET Core 2.1 Web Application project that uses JWT tokens for authenticating the Web API that's built-in to the project. It works fine when I run it locally on my machine, but when I deploy it to Azure (with identical environment and app-settings) it simply returns empty HTTP 401 responses to requests from my authenticated clients and I need to find out why so I can fix it.
I enabled logging of every detail in ASP.NET Core, however I never received any useful output.
First, I added Serilog.AspNetCore and the Console sink to the project through NuGet, then configured logging at Verbose level in Program.cs:
public class Program
{
public static void Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.MinimumLevel.Override("Microsoft", LogEventLevel.Verbose)
.MinimumLevel.Override("System", LogEventLevel.Verbose)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Verbose)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
.CreateLogger();
CreateWebHostBuilder( args ).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(String[] args)
{
return WebHost.CreateDefaultBuilder( args )
.ConfigureLogging( (ctx, cfg ) =>
{
cfg.ClearProviders();
} )
.UseStartup<Startup>()
.UseSerilog();
}
}
But when I run my web-application on Azure (with console stdout logging to file) I got this output:
[04:13:10 Verbose] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker
Authorization Filter: Before executing OnAuthorizationAsync on filter
Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter.
[04:13:10 Verbose]
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler
HandleAuthenticateAsync called
[04:13:10 Debug]
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler
AuthenticationScheme: Bearer was not authenticated.
[04:13:10 Information]
Microsoft.AspNetCore.Authorization.DefaultAuthorizationService
Authorization failed.
[04:13:10 Verbose] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker
Authorization Filter: After executing OnAuthorizationAsync on filter
Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter.
[04:13:10 Information]
Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker
Authorization failed for the request at filter 'Microsoft.AspNetCore.Mvc.Authorization.AuthorizeFilter'.
[04:13:10 Verbose] Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker
Before executing action result Microsoft.AspNetCore.Mvc.ChallengeResult.
[04:13:10 Information] Microsoft.AspNetCore.Mvc.ChallengeResult
Executing ChallengeResult with authentication schemes (["Bearer"]).
[04:13:10 Verbose]
IdentityServer4.AccessTokenValidation.IdentityServerAuthenticationHandler
Forwarding challenge to scheme: BearerIdentityServerAuthenticationJwt
Note how despite verbose logging, the error messages (repeated below) don't give me any explanation:
AuthenticationScheme: Bearer was not authenticated.
Authorization failed.
I dug around the ASP.NET Core Security source-code to see that JwtBearerHandler.HandleAuthenticateAsync doesn't do much logging of its own, but it does call into the not-open-sourced System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler which does normally do a lot of logging, including detailed reasons (e.g. (with IDX10209-type error codes in strings), but I don't know why it isn't outputting anything I can capture.
How do I log messages from JwtSecurityTokenHandler?
I found the problem:
My HttpClient (that was sending the HTTP Authorization header Bearer token) was unintentionally sending it to a http:// URI that immediately received a 301 redirect to a https:// URI. The redirect was performed by IIS without the ASP.NET Core pipeline getting involved.
The HttpClient class does not re-send the Authorization header following a redirect (this is by-design).
I never noticed this because my HttpClient's received HttpResponseMessage had a reference to the original request which had the Authorization header, not the post-redirect request that lacked the header. I had to use Fiddler with the HTTPS proxy to see the second request was lacking the Authorization header.
When IdentityServerAuthenticationHandler or ASP.NET Core's own JwtBearerHandler receives a request with no Authorization header it does not call into JwtSecurityTokenHandler at all. To see this, open the JwtBearerHandler.cs file in the ASP.NET Core Security Git repo and look at HandleAuthenticateAsync: It has this logic:
if (string.IsNullOrEmpty(token))
{
string authorization = Request.Headers["Authorization"];
// If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization))
{
return AuthenticateResult.NoResult();
}
So in my case, it never actually called JwtSecurityTokenHandler at all, hence the lack of output messages about JWT validation.
However the output messages I did receive did not help. They're both misleading:
"AuthenticationScheme: Bearer was not authenticated." should have been something like "AuthenticationScheme: No Bearer token was present in the request." instead.
And "Authorization failed." should have been "Authorization skipped because no token was present in the request."
So in the end, the fix was to change the original request URI's scheme from http:// to https://.

Using Identity Server 3, ClaimsPrinciple null even after successful bearer token authentication

I have a test console app which I'm pointing at a local instance of Identity Server 3 to request an access token. The following code does this and returns my token fine (passing a single scope "scope.test.client").
static TokenResponse GetClientToken(string clientId, string clientSecret, string[] scopes)
{
var uri = new Uri(string.Concat(ID_BASE_URI, ID_URL_TOKEN));
var client = new TokenClient(
uri.AbsoluteUri,
clientId,
clientSecret);
return client.RequestClientCredentialsAsync(string.Join(" ", scopes)).Result;
I then use this token to call an API also running locally. This takes the TokenResponse obtained above and passed it to this method:
static void CallApi(string url, TokenResponse response)
{
try
{
using (var client = new HttpClient())
{
client.SetBearerToken(response.AccessToken);
Console.WriteLine(client.GetStringAsync(url).Result);
}
}
catch (Exception x)
{
Console.WriteLine(string.Format("Exception: {0}", x.Message));
}
}
The API (an ASP.NET WebApi project) uses an Owin Startup class to enforce bearer token authentication for all requests:
appBuilder.Map(baseApiUrl, inner =>
{
inner.UseWebApi(GlobalConfiguration.Configuration);
// Enforce bearer token authentication for all API requests
inner.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = "https://identityserver/core",
ValidationMode = ValidationMode.ValidationEndpoint,
RequiredScopes = new[] { "scope.test.client" }
});
});
It also ensures all API requests are handled by a custom authorize attribute:
GlobalConfiguration.Configuration.Filters.Add(new DefaultApiAuthorizeAttribute());
Debugging this API, the first line in my overridden OnAuthorize method (in DefaultApiAuthorizeAttribute) is this:
var caller = actionContext.RequestContext.Principal as System.Security.Claims.ClaimsPrincipal;
If I break on this line I can see that actionContext.RequestContext.Principal is always null. However, I can see that ((System.Web.Http.Owin.OwinHttpRequestContext)actionContext.RequestContext).Request.Headers contains an Authorization header with the bearer token passed from my console app.
So it would seem that the API project is not authenticating the bearer token. Certainly the Identity Server logs suggest it isn't being hit at all after issuing the initial access token. So I'd appreciate your expert advice about why this might not be happening, or at least some pointers about where to look.
I suspect it might have something to do with SSL. Both sites are hosted locally under self-signed SSL certs, although Identity Server is configured to not require SSL and uses the idsrv3test.pfx development certificate for signing. I do have another test MVC web app which delegates authentication to the same IS3 instance which works fine locally, so I believe my IS3 instance is configured correctly.
You need to call UseIdentityServerBearerTokenAuthentication before you call UseWebApi. When you set up an OWIN Middleware Pipeline, the order is important.
In your case, Web API will be handling your requests before they get sent onto Identity Server (if they get sent on at all).
I imagine a range of possible issues could have the impact I described, but in my case I was able to find the cause by adding a diagnostics log to my consuming API. This led me to discover that the problem was an assembly conflict. The Owin middleware was looking for a Newtonsoft.JSON assembly with version 8.0.0.0 but my consuming API (actually running on top of a CMS intance) was using 7.0.0.0.
For anyone else who wants to find the answer fast, rather than spend hours tweaking configurations, here's the documentation that describes how to add this logging: https://identityserver.github.io/Documentation/docsv2/consuming/diagnostics.html

HttpServletResponse: Keep, Save or Persist the response for later use...?

I am using Spring to create my webapp. I have a scenario like:
An ExternalService sends a GET request to my Controller that has a mapping /DoOperation with some user info as param. I get the param check the user if he is logged into my system or not if NOT i send him to OpenId verification with returnUrl, which is in same webapp say /Authenticated.
The ExternalService does not provide a returnUrl (Not related to OpenId returnUrl) and forces to respond the same request it made at /DoOperation.
Now, How could I keep the very HttpServletResponse (if there is a way, apparently there is not) so that I can write a response once I have gone somewhere else e.g. at OpenID page to verify user and then /Authenticate in this case... so that ExternalService could read it.
Could Servlet Filter help here?

Cache database results in WebAPI authentication

I am creating a RESTful web service using ASP.NET WebApi. I am requiring that all incoming requests go over SSL because I will be using Basic HTTP Authentication. The client is responsible for including the credentials in the request header and the web service will authenticate the client on each request.
The actual authentication requires making a database call. Is there a way to cache the database results (the username and password) so that I don't have to make a database call for every incoming request that occurs in a short period of time?
So when a request comes in the web service will look for the username/password combo in the cache. If it is found it will process the request. If it isn't found, it will make the database call to authenticate and then add the username/password to the cache.
The easiest cache that I can think of to use would be using System.Web.Caching. Below is an extremely simplified version of adding a value to the current cache and then retrieving it for processing. I would not recommend using this code as is.
// GET api/values/5
[HttpGet]
public string GetValue(int id)
{
HttpContext.Current.Cache.Insert("2", "test1");
var value = Convert.ToString(HttpContext.Current.Cache.Get(id.ToString()));
return !string.IsNullOrEmpty(value) ? value : "nothing found";
}

Resources