How to access JWT in Quarkus when proactive auth is disabled? - quarkus

I need to access a jwt sent in authorization header. I also have to disable the proactive auth check. How can I get the token?
I have this class which used to work when proactive auth was enabled:
#RequestScoped
public class AccessRights {
#Inject
JsonWebToken accessToken;
public boolean validateAccess() {
if(accessToken.getRawToken() == null)
throw new AccessDeniedException("Invalid Access Token");
JsonObject claim = accessToken.getClaim("claim");
// ... do some validation ...
}
}
When I set quarkus.http.auth.proactive=false it stops working. The accessToken is always null. Now that is expected since its documented here https://quarkus.io/guides/security-jwt#proactive-authentication and here https://quarkus.io/guides/security-built-in-authentication#proactive-authentication. I tried to use the alternative way suggested in the docs, but I cannot get it to run.
Problem: I don't know how to get a JsonWebToken from the suggested workaround
Problem: Even when I try to use securityIdentity instead, it is always null. Plus I don't know yet how to actually use it for validation.
#RequestScoped
public class AccessRights {
#Inject
CurrentIdentityAssociation currentIdentityAssociation;
public boolean validateAccess() {
SecurityIdentity identity = identities.getDeferredIdentity().await().indefinitely();
// identity is always null
}
}
EDIT:
To add more context: I have a graphQL API and the initial problem was, that if I receive an expired token, the framework will respond with a 401 unauthorized error. However as is best practice in graphQL, I need to respond with a 200 with json body that describes the error. The proactive auth check prevents the graphql error handler to kick in. That is why I disabled it, effectively making things worse.
Here is how the whole auth process in my application works:
The API receives a request that should contain a bearer token (JWT) in the authorization header. The token is acquired somewhere else. My application needs to validate the issuer and expiration time. This part used to be handled by the pro-active auth check. After that my custom validateAccess() method will extract some roles from the token. If those roles are not present, I will call an external api, providing the raw token, to get back some more detailed access rights.

I believe you missed something important here.
Docs says that :
By default, Quarkus does what we call proactive authentication. This means that if an incoming request has a credential then that request will always be authenticated (even if the target page does not require authentication).
Which implicitly means : if you disable proactive auth, you need to require auth to be done before accessing resource.
That may be part of your problem, as you disabled proactive auth, you'll have to explicitly restrict access to resources to authenticated clients. Otherwise, no auth is performed and thus SecurityIdentity is null.
If you already tried that, please add more code and context. I'll gladly edit my response.
EDIT 1 :
I see 2 distinct problems in informations you added :
You got to validate token (if proactive auth is disabled)
You got to get response code 200 with error details instead of 401
But as you clearly stated, token validation works out of the box with proactive enabled.
So, I would suggest you to let proactive auth do its job. Then, I would add an ExceptionMapper.
This way, you can write custom code for Exception unfolding, and you can respond what you want for every situation.
See ExceptionMappers documentation for more

Related

How to silently renew Id Token using AddMicrosoftIdentityWebAppAuthentication to Call Downstream API

I am trying to implement the BFF-Gateway pattern (no tokens in the browser) to be used with a React SPA. The BFF is using AddMicrosoftIdentityWebAppAuthentication to handle login and issue a cookie to the SPA. And it is using YARP to proxy api requests to a downstream api. I'm using Azure B2C. Everything works perfectly until the BFF id_token expires in 1 hour. At that point, fetching the downstream api access token via GetAccessTokenForUserAsync (which is called in a piece of middleware) fails:
var scope = _configuration["CallApi:ScopeForAccessToken"];
var accessToken = await _tokenAcquisition.GetAccessTokenForUserAsync(new[] { scope });
ctx.Request.Headers.Add("Authorization", "Bearer " + accessToken);
Exception:
IDW10502: An MsalUiRequiredException was thrown due to a challenge for the user. See https://aka.ms/ms-id-web/ca_incremental-consent.
ResponseBody: {"error":"invalid_grant","error_description":"AADB2C90085: The service has encountered an internal error. Please reauthenticate and try again.\r\nCorrelation ID: 622d6bd6-d06e-4142-86f2-b30a7a17b3b5\r\nTimestamp: 2022-11-25 09:31:23Z\r\n"}
This is effectively the same as Call Downstream API Without The Helper Class example and this sample, except that I'm acquiring the access token in middleware, not a controller, so the downstream YARP requests contain the access token. BTW I get the same error if I do this inside a controller per this example. And I see no soluton to this in the sample.
There is a similar question here which references the sample referenced above, but for the B2C sample I see no solution to this problem.
I also found this sample and this explanation. But this uses Microsoft.Owin to configure auth, not AddMicrosoftIdentityWebAppAuthentication. This looks promising, but is a departure from most examples I see that use Microsoft.Identity.Web.
Can you please point to the correct soluton? I need call to be able to call _tokenAcquisition.GetAccessTokenForUserAsync after the id token expires without asking the user to reauthenticate and/or the SPA to having to reload.
At the moment I am handling this issue in the SPA by catching the exception from MSAL and redirecting back to the login endpoint in the BFF which initiates the challenge. This gets me a new id_token and cookie, but this is just a temp workaround as it's very disruptive to user to be redirected away from the SPA.

Add claims to security.oauth2.jwt.Jwt

I have a SpringBoot OAuth2 Client and ResourceServer. My AuthorizationServer is Okta.
Now suppose that in a certain moment I want to add a claim to my token, for Example:
#GetMapping("/addIdUser")
public ResponseEntity<String> addUser(#AuthenticationPrincipal Jwt jwt) {
// here I want to add a claim to my Jwt, the token
// should return to the frontend and when I resend back to my resourceserver
// it should be validated again
return new ResponseEntity<String>(token, HttpStatus.OK);
}
I have read tons of docs of TokenEnhencer and TokenConverter but it not seems to be the solution I looking for.
Can I do something like this?
Seems like you want to authorize based on domain specific claims, and there are two main options here:
Get extra claims at the time of token issuance - Okta needs to either call an API or do a database lookup - this is the preferred option but not always possible
Look up claims in the resource server when an access token is first received, then cache them for subsequent requests with the same access token
Leave the Okta issued token alone in the client, then adopt one of the above two approaches in the API. Out of interest, here is a Spring Boot API of mine that uses a library based approach to OAuth handling and implements both approaches:
Standard Authorizer
Claims Caching Authorizer

In spring social for Facebook api, why does facebook.isAuthorized always return true?

I have an angular2 app that has a Facebook login feature. When the user authenticates themself, I then send this accessToken to the server.
The server program is with Springboot and I make use of spring social.
I want to check to see if this user is authorized. So I call:
facebook.getToken() returns the access token generated on the client side. When I call facebook.isAuthorized() it returns true...As expected, because I am sending real data.
Although if I send bogus data such as:
(The token in this case is fabricated by me) to the same API endpoint
facebook.isAuthorized returns true. This is unexpected because in this case I am fabricating an accessToken.
The spring-social dependency is this:
Why does isAuthorize return true for a real access token, as well as a fake one? How can I check to see if a user of my angular2 app has authenticated themselves through Facebook on the server side?
Implementation of isAuthorized() for FacebookTemplate can be found at https://github.com/spring-projects/spring-social/blob/a6bf2626ee8ac81765c416029ca033affc94fc6c/spring-social-core/src/main/java/org/springframework/social/oauth2/AbstractOAuth2ApiBinding.java#L87.
With this source its quite obvious that isAuthorized() only checks if there is any access token provided.
To validate your token you can run any request which need authorization (e.g. userOperations().getUserPermissions()) and check for InvalidAuthorizationException or you can find some inspirations in how to verify facebook access token?.

SignalR oAuth on self host

I have a webapi running on a self hosted app, security provided via oauth, and I'm setting the authorisation header for calling the API. This works great, we have a user identity on all calls.
Now I can pass the same authorization token by query string or cookie (or header on some connection types) to signalr (self hosted in the same app).
The token gets to the server for signalr, I can can add it into context.cookie, or into the headers.
but..nowhere can I get it to create an authenticated user, I seem to be plagued by 401 errors
I assume I am missing a key piece of code thats supposed to take the token and create an authenticated user for signalr (even though webapi/owin does it itself).
does anybody have any pointers, or examples where signalr works with oauth on self-host?
You are correct that you need to setup a way to authenticate and set the user when wanting to use SignalR with OAuth.
You can create a customer AuthorizeAttribute for SignalR.
In it you can basically get the query string to get hold of the token, once you have the token, you can unprotect it and start validating it and setting the user context in the request.
I have an example at
https://github.com/louislewis2/AngularJSAuthentication/blob/master/AngularJSAuthentication.API/SignalRHelpers/QueryStringBearerAuthorizeAttribute.cs
Once that has been implemented, you simply decorate your hub with [QueryStringBearerAuthorize]
The github link has a full working example for this and might provide some valuable insight.

How to see the redirect status from an STS / IdP

I have searched (probed ,even) for an answer to this but haven't come up with anything useful so far. I'm pretty new to ADFS, STS's in general and WIF so please excuse any obvious ignorance or inappropriate use of terminology. ;)
I'm currently integrating a custom MVC3 app with an external IdP, via ADFS. The ADFS to IdP setup is all done and working.
Some parts of the site are accessible to anon users - in the web.config authentication mode has been set to none. The other parts are protected by having their controllers/action methods decorated by a custom System.Web.Mvc.AuthorizeAttribute.
All the usual modifications to the web.config for using the WsFederationAuthenticationModule have been made and it works 95%; the user can browse to the anon accessible parts of the site. When they try and hit the protected parts, the authorize attribute checks if they have some custom information from our IdP in the IClaimsPrincipals associated with the HttpContext.Current.User and then sets the ActionResult to 401 if not; The WsFederationAuthenticationModule kicks in and redirects them to the IdP's login page. When they enter their details, they're then successfully redirected with some FedAuth cookies and the authorization then passes.
The problem starts when they get to the IdP's login page. This particular IdP has a link to return you directly to our site (to the same page the original request was made to), with this SAML response embedded somewhere (this is according to their documentation)
urn:oasis:names: tc:SAML:2.0:status: AuthnFailed
At this point, they are now "Unauthorized" and all the user will see (at least in dev) is a 401 page. You have to kill the session or otherwise get rid of that cookie to start again.
What I need to do is intercept that redirect request from the IdP, and essentially check for that particular SAML status, because the user should then be redirected to one of the unauthorized areas as if nothing has happened. I've tried something like this in the global.asax:
protected void Application_Start()
{
// mvc stuff here....
// add handler to intercept handling creation of security tokens by WsFederationAuthnticationModule
FederatedAuthentication.ServiceConfigurationCreated += OnServiceConfigurationCreated;
}
void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e)
{
FederatedAuthentication
.WSFederationAuthenticationModule
.SessionSecurityTokenCreated += WSFederationAuthenticationModule_SecuityTokenCreated;
}
public void WSFederationAuthenticationModule_SecuityTokenCreated (Object sender, SessionSecurityTokenCreatedEventArgs args)
{
var token = args.SessionToken;
// do something with the session token here e.g. check for SAML status
}
.. but I cant see anything useful on that token; nothing to indicate a specific response status. The fact that there is a FedAuth cookie at all but no custom info from the Idp is a dead give away that the user has been there but somehow failed to authenticate, but in principle I want to be able to see that status. I might have to deal with timeouts at the IdP as well....
Maybe I'm doing this all wrong, or just plain old don't understand, but can somehow fill me in on how to determine those response statuses?
Phew. Thank you! :D
Ok, so I'm going to answer my own question.
The answer to whether I can get that custom status from my IdP is a no, at the moment. :(
But this is only because ADFS is not setup to capture it and pass it on. Apparently you need to do some custom coding for capturing information from the back channel that is opened between ADFS and the IdP.... well beyond the current scope of work.
As a work around for the moment:
If a request is made to the site and there is NO SAML token, its a new request by a user who has made no auth attempt at the Idp
If there is a SAML token but no ID from the IdP in the token (which is only present when they auth properly), then the user failed Auth for some reason
If there is a SAML token with the ID present, the user auth'd properly
Not great but acceptable. BTW, all credit goes to YMC in this SO post for the following code which lets you check for SAML tokens:
void WSFederationAuthenticationModule_SecurityTokenReceived(object sender, SecurityTokenReceivedEventArgs e)
{
var message = SignInResponseMessage.CreateFromFormPost(Request) as SignInResponseMessage;
var rstr = new WSFederationSerializer()
.CreateResponse(message,
new WSTrustSerializationContext(
SecurityTokenHandlerCollectionManager.CreateDefaultSecurityTokenHandlerCollectionManager()));
}
Pce!

Resources