I implemented the fantastic code linked at the bottom of the ServiceStack Auth documentation for implementing an Auth provider that supports OpenID Connect (http://estynedwards.com/blog/2016/01/30/ServiceStack-IdentityServer-Angular/).
The code works great when it is supposed to, handling the token, checking the group membership, etc. However, if I send a bad token, the server code errors out and no HttpError gets returned to the client (confirmed via Postman as well). Instead we just get “No Response.” When I run it in the debugger, the code internally is failing in the correct place and throwing the correct HttpError exception (e.g. token expired), but for some reason it doesn’t make it back to the client. I’m including my code here, though it is pretty much line for line the code in the article (the only 2 changes being the suggestion made in the comments there on PreAuthenticate to avoid the loop and added storing of the roles).
Why isn’t the HttpError making it back to the client?
public override object Authenticate(IServiceBase authService, IAuthSession session, Authenticate request)
{
var header = request.oauth_token;
// if no auth header, 401
if (string.IsNullOrEmpty(header))
{ throw HttpError.Unauthorized(MissingAuthHeader); }
var headerData = header.Split(' ');
// if header is missing bearer portion, 401
if (string.Compare(headerData[0], "BEARER", StringComparison.OrdinalIgnoreCase) != 0)
{ throw HttpError.Unauthorized(InvalidAuthHeader); }
try
{ // set current principal to the validated token principal
ClaimsPrincipal cPrincipal = JsonWebToken.ValidateToken(headerData[1], Certificate, Audience, Issuer);
Thread.CurrentPrincipal = cPrincipal;
if (HttpContext.Current != null)
{
// set the current request's user the the decoded principal
HttpContext.Current.User = Thread.CurrentPrincipal;
}
// set the session's username to the logged in user
session.UserName = Thread.CurrentPrincipal.Identity.Name;
//set the sessions roles to the roles from the token so that authorization works
session.Roles = (cPrincipal.Claims.Where(c => c.Type == ClaimTypes.Role).Select(c => c.Value)).ToList();
return OnAuthenticated(authService, session, new AuthTokens(), new Dictionary<string, string>());
}
catch (Exception ex)
{
throw new HttpError(HttpStatusCode.Unauthorized, ex);
}
}
public override bool IsAuthorized(IAuthSession session, IAuthTokens tokens, Authenticate request = null)
{
return HttpContext.Current.User.Identity.IsAuthenticated && session.IsAuthenticated && string.Equals(session.UserName, HttpContext.Current.User.Identity.Name, StringComparison.OrdinalIgnoreCase);
}
public void PreAuthenticate(IRequest request, IResponse response)
{
var header = request.Headers["Authorization"];
using (var authService = HostContext.ResolveService<AuthenticateService>(request))
{
authService.Post(new Authenticate
{
provider = Name,
oauth_token = header
});
}
}
}