If you are using reference tokens, you need an authentication handler that implements the back-channel validation via the OAuth 2.0 token introspection protocol, e.g. this one:.
services.AddAuthentication("token")
.AddOAuth2Introspection("token", options =>
{
options.Authority = Constants.Authority;
// this maps to the API resource name and secret
options.ClientId = "resource1";
options.ClientSecret = "secret";
});
It is not uncommon to use the same API with both JWTs and reference tokens. In this case you setup two authentication handlers, make one the default handler and provide some forwarding logic, e.g.:
services.AddAuthentication("token")
// JWT tokens
.AddJwtBearer("token", options =>
{
options.Authority = Constants.Authority;
options.Audience = "resource1";
options.TokenValidationParameters.ValidTypes = new[] { "at+jwt" };
// if token does not contain a dot, it is a reference token
options.ForwardDefaultSelector = Selector.ForwardReferenceToken("introspection");
})
// reference tokens
.AddOAuth2Introspection("introspection", options =>
{
options.Authority = Constants.Authority;
options.ClientId = "resource1";
options.ClientSecret = "secret";
});
The logic of the forward selector looks like this:
/// <summary>
/// Provides a forwarding func for JWT vs reference tokens (based on existence of dot in token)
/// </summary>
/// <param name="introspectionScheme">Scheme name of the introspection handler</param>
/// <returns></returns>
public static Func<HttpContext, string> ForwardReferenceToken(string introspectionScheme = "introspection")
{
string Select(HttpContext context)
{
var (scheme, credential) = GetSchemeAndCredential(context);
if (scheme.Equals("Bearer", StringComparison.OrdinalIgnoreCase) &&
!credential.Contains("."))
{
return introspectionScheme;
}
return null;
}
return Select;
}
/// <summary>
/// Extracts scheme and credential from Authorization header (if present)
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public static (string, string) GetSchemeAndCredential(HttpContext context)
{
var header = context.Request.Headers["Authorization"].FirstOrDefault();
if (string.IsNullOrEmpty(header))
{
return ("", "");
}
var parts = header.Split(' ', StringSplitOptions.RemoveEmptyEntries);
if (parts.Length != 2)
{
return ("", "");
}
return (parts[0], parts[1]);
}