Spring Security 3.2 Token Authentication - java

I know this has been asked already, but I am not able to get it to work.
Here is what I would like to get accomplished:
I am using Spring Security 3.2 to secure a REST-like service. No server side sessions.
I am not using basic auth, because that would mean that I need to store the user's password in a cookie on client side. Otherwise the user would need to login with each page refresh/ change. Storing a token is I guess the lesser evil.
A web client (browser, mobile app) calls a REST-like URL to login "/login" with username and password
The server authenticates the user and sends a token back to the client
The client stores the token and adds it to the http request header with each api call
The server checks the validity of the token and sends a response accordingly
I did not even look at the token generation part yet. I know it is backwards, but I wanted to get the token validation part implemented first.
I am trying to get this accomplished by using a custom filer (implementation of AbstractAuthenticationProcessingFilter), however I seem to have the wrong idea about it.
Defining it like this:
public TokenAuthenticationFilter() {
super("/");
}
will only trigger the filter for this exact URL.
I am sticking to some sample implementation, where it calls AbstractAuthenticationProcessingFilter#requiresAuthentication which does not accept wildcards.
I can of course alter that behavior, but this somehow makes me think that I am on the wrong path.
I also started implementing a custom AuthenticationProvider. Maybe that is the right thing?
Can someone give me a push into the right direction?

I think pre-auth filter is a better fit for your scenario.
Override AbstractPreAuthenticatedProcessingFilter's getPrincipal and getCredentials methods.
In case the token is not present in the header, return null from getPrincipal.
Flow:
User logs in for the first time, no header passed, so no
authentication object set in securityContext, normal authentication
process follows i.e. ExceptionTranslation filter redirtects the user
to /login page based on form-logon filter or your custom authenticationEntryPoint
After successful authentication, user requests secured url, pre-auth filter gets token from header authentication object set in
securityContext, if user have access he is allowed to access secured
url

Related

Get the OAuth2/OIDC access token with every request in Spring

I'm trying to enable multi-tenancy for a previously single-user system. The application used to run on a local server and had a relatively simple frontend baked in.
Now I want to allow multiple users to simultaneously use it in a cloud environment. I went ahead and implemented Auth2 with OIDC and PKCE to redirect users to an external Auth Provider. What I want now is that for every request, the user sends his Access token with the request in order for me to decide what data to provide with the answer.
I could not figure out how to obtain that data, as it seems that the spring framework (by default) only sends the ID token with the request. I suspect the fact that my software would simultaneously be the client and the resource server has something to do with it.
This is my first question, so I'm very happy to modify or extend my question if I've forgotten anything.
What I've tried to far:
I've used Postman to verify that the three tokens, ID token, refresh token and access token are issued correctly and can be retrieved with my credentials.
I tried getting the access token from the request itself. Any parameters (like #AuthenticationPrincipal OidcUser oidcUser) in the controller that include the token, however, are only showing the ID token and not the access token.
Getting the token via the OAuth2AuthorizedClientService does not work either, same problem, as I can only get the ID token, but not the access token.
Update #1, 13.12.2022/11:40: I am using PingOne by PingIdentity as the authentication provider and the following dependencies are or might be related or helpful to this matter:
spring-boot-starter-web
spring-boot-starter-security
spring-boot-starter-thymeleaf
spring-boot-starter-web-services
spring-boot-starter-oauth2-resource-server
Split your security in two with http.securityMatcher in the first picked SecurityFilterChain bean to restrict the endpoints it applies to (use #Order to control that), and configure WebClient to add access-token as Authorization header to its requests to resource-server (REST endpoints). This is demoed in the tutorial I already linked in my comment to your question. This tutorial matches exactly what you are asking, the fact that it uses Keycloak instead of PingOne as authorization server is a detail (solved by editing a property or two).
Resource-server security filter-chain
As reminder, a resource-server is a REST API secured with OAuth2. All end-points of #RestController and #Controller with #ResponseBody should be secured with a SecurityFilterChain containing http.oauth2ResourceServer(). The easiest way to configure a resource-server with Spring is using spring-boot-starter-oauth2-resource-server (or one of the very thin wrappers I wrote around it which enable to configure it from properties with 0 java conf)
By default with such filter-chains, Spring populates the security-context with a sub-class of AbstractOAuth2TokenAuthenticationToken<?>. You can retrieve the access-token from it. At least 2 options to access this Authentication instance:
SecurityContextHolder.getContext().getAuthentication()
have Spring auto-magically inject AbstractOAuth2TokenAuthenticationToken<?> auth as controller method parameter
You can also have the original authorization header injected as controller method parameter with #RequestHeader(value = HttpHeaders.AUTHORIZATION) String authorizationHeader.
I expose various ways to configure resource-servers security filter-chain in this tutorials.
Client security filter-chain
End-points of #Controller returning a template name and secured with OAuth2 are clients. The easiest way to configure a Spring client is with spring-boot-starter-oauth2-client and http.oauth2Login().
Note that in this configuration, the request between the browser and the Spring client is not OAuth2 (it is most frequently secured with a session cookie, not a Bearer access-token in Authorization header). Only requests sent by the Spring client (on the server) to resource-servers are secured with OAuth2.
With client security filter-chain, security-context is populated with OAuth2AuthenticationToken which, on purpose, exposes ID-token and not access-token. As reminder, ID tokens are holding user identity data and are intended to be used by clients when access-tokens audience is resource-servers and is designed for access-control. Clients should consider access-tokens as black box and use it only to authorize their requests to resource-servers (set Bearer Authorization header).
You can get the access-token string from OAuth2AuthorizedClient: authorizedClient.getAccessToken().getTokenValue(), which is itself retrieved from the OAuth2AuthorizedClientService you can auto-wire in your controllers: authorizedClientService.loadAuthorizedClient("your-client-id", auth.getName()) (auth being the OAuth2AuthenticationToken instance retrieved from security-context via SecurityContextHolder or controller method parameter injection)
If you need to authorize a WebClient request from client to resource-server, you can do simpler than retrieve access token and position authorization header: do as in the UiController of the tutorial already linked in my comment to your question:
final var authorizedClient = authorizedClientService.loadAuthorizedClient("spring-addons-public", auth.getName());
final var response = webClient.get().uri("http://localhost:8080/api/greet")
.attributes(ServerOAuth2AuthorizedClientExchangeFilterFunction.oauth2AuthorizedClient(authorizedClient))
...
With WebClient configured as follow:
#Bean
WebClient webClient(ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientService authorizedClientService) {
var oauth = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
new AuthorizedClientServiceOAuth2AuthorizedClientManager(clientRegistrationRepository,
authorizedClientService));
oauth.setDefaultClientRegistrationId("spring-addons-public");
return WebClient.builder().apply(oauth.oauth2Configuration()).build();
}
Thanks to those who tried to help me, but eventually I figured it out myself.
I extended my Controllers by two attributes: OAuth2AuthenticationToken authentication and HttpServletRequest request.
Also, I #Autowired in the OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository.
This then allows the following call returning the value of the accessToken:
(oAuth2AuthorizedClientRepository.loadAuthorizedClient(myClientRegistrationId, authentication, request)).client.getAccessToken().getTokenValue();.
After that, it's just parsing the token and retrieving the values using JWTParser.parse() and provided methods from the JWT-result.
Personal note: Don't do the parsing and retrieving value parts in your controller to keep any more complex logic out of it.
I hope this helps somebody!

How to allow unauthorized request for the first time and then require authorization?

So, we are implementing this special kind of authorization, where after logging in, user is presented with basic dashboard. When trying to get to another location, he is asked to authorize with password.
The thing is that in order to present the basic dashboard, some requests are sent and need to come with 200 response, while the rest just returns error message and redirects to authorization screen.
To summarize, we're gonna have 3 kinds of endpoints:
- blocked, until user authorizes
- allowed to return proper data for the first time while each consecutive request will require authorization
- no authorization required
I cannot find a way to overcome the 2nd type. Is there a way to record the number of requests sent per specific endpoint? Or is there any way to actually allow first unauthorized use and then required authorization?
One way would be to authenticate user without any role the first time that users access to yours controllers.
Then when he try again to access you could sent them to login process if user is logged without role.
To securize this methods could use the annotation below:
#Secure("IsAuthenticated()==false || (IsAuthenticated() && hasRole("WRITE"))")
To securize manually a user should use the SecurityContextHolder class

Spring Security returning the same token each time

We're using spring security (Authorisation and Resource server ) in our project.
client sends a token request (/oauth/token) with the oauth2 parameters.
spring security app creates a token for the user and respond to the client with the access_token, refresh_token, custom user object (name, organisation, email etc) and authorities (Roles).
Client adds additional roles (say ROLE_CLIENT, ROLE_USER).
spring application will store the above roles for the given user.
Next time when client sends a token request, spring security returns the previously created token (not expired yet) along with the user and authority information. This authority information is not having the latest roles (added in step4).
Here spring security always using the existing token (as it is not expired) and returning the valid token. Is this the expected behaviour even though the user object is being modified?
It sounds like you need to revoke the access token when the users roles change if you want the next request to get a new access token with the new roles and not return an existing token with existing roles if it's still valid.
At the point where you update the users roles you'd likely want to revoke the token.
I haven't personally tested this but I found a guide for it here https://www.baeldung.com/spring-security-oauth-revoke-tokens so your milage may vary.
I want to add that this does not sound like the normal OAuth2 process and you may be breaking a few conventions here which might bite you later. That said, you don't have to follow a standard if you're confident in your proposed solution.
Edit: To clarify the users roles and access is normally part of a resource and not part of the token exchange. For example you have a normal OAuth2 request which generates a token which you can exchange for an access token, as you've laid out in steps 1 and 2. Then you'd normally take that access token and request user access information from a resource such as "userinfo" service or something similar.
Your security service can also be a resource server but the two steps should be seen as different. Then when you want to modify the users roles you do this again through a resource. This means the next time you invoke the resource it'll have the up to date information without needing to authenticate the user again.

How does the spring internally validate the csrf token with _csrf parameter or X-CSRF-TOKEN header?

I am using spring and enabled csrf with HttpSessionCsrfTokenRepository, I clearly know if the client is sending the csrf token either as _csrf parameter or X-CSRF-TOKEN header for the request spring picks up the token and validates with the token which was generated using generateToken(HttpServletRequest request)
But my question is how does spring does this internally.
My reason for this question being:
1.I have a Rest POST call which takes credentials and validates the
identity of the user. But Since I want to add a csrf token to the
rest call as a layer of security I wanted to add it in the post body
to prevent csrf token leak.
So if I know how spring security filters these tokens internally it would be helpful. I revised the spring documentation but it is mostly how we can use CSRF token in a form with hidden field or meta tags and Ajax call with a header.
And I also would like to hear any comments on my design if it is good to have the token in body( I am convinced because it would not be a simple url parameter to leak the token ) or should I have it in the header. I just dont want to lean to use header just because its simple. Looking for the best solution.
Please shed some light.
There're multiple implementations for CsrfTokenRepository in spring if you want to look into into it. for eg:
https://github.com/rwinch/spring-security/blob/master/web/src/main/java/org/springframework/security/web/csrf/HttpSessionCsrfTokenRepository.java
https://github.com/rwinch/spring-security/blob/master/web/src/main/java/org/springframework/security/web/csrf/CsrfFilter.java
https://github.com/rwinch/spring-security/tree/master/web/src/main/java/org/springframework/security/web/csrf
https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/apidocs/org/springframework/security/web/csrf/CookieCsrfTokenRepository.html
https://docs.spring.io/spring-security/site/docs/4.2.7.RELEASE/apidocs/org/springframework/security/web/csrf/CookieCsrfTokenRepository.html
IMO its good (safer - may be?) to keep Tokens on the header because of few reasons that i can think of..
You cannot set token on a body for your GET request. You want to be consistent of all your endpoints (you may not need it today but things change really fast)
Tomorrow if you want to change your Auth model, you dont want to to change your request body. when request body changes you break the contract with clients
If you change your auth model to a authorization server, you can add proxy server (like ngnix?) before your service and lets call it auth-proxy. You can leave all security related things to this auth-proxy and it will inspect the header and do the validations for you. You don't want the proxy to look into your request body and you can focus on your business implementation
Request body is completely related to your business so you can focus on it vs dealing with security related things in your body.
Everytime you create a new Request for a new endpoint you don't want to keep adding tokens in all the requests
It's just my opinion based on my experience.

Custom authentication in Java REST API

I'm trying to come up with the best way to do my own authentication in our Java REST API using the Jersey framework (v2.5.1) running on Tomcat 7.
The API will be accessed through our iOS application. In the iOS application we use Facebook authentication (using the Facebook SDK), and then we use the access token in every call to the REST API.
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthenticationFilter implements ContainerRequestFilter
{
#Override
public void filter(ContainerRequestContext requestContext) throws IOException
{
// Extract the access token from the HTTP header
// Look up in the database to see if we have a user with that token
// If there is a user found, proceed
// If we can't find a user, we are going to send the token to Facebook to get the user details. If the token is invalid, we throw an exception. If it is valid, we look up if we can match the Facebook details with an existing user. When we can't match, we create a new user.
}
}
This filter will be executed in every API request.
My questions:
Is this a correct workflow?
Should we contact Facebook every time to validate the token? This will cause a lot of overhead.
This filter is executed for every request. How can we exclude certain urls (some resources won't require authentication)? I was thinking of holding a set of urls in the filter class and see if the requested url matches one of the defined public urls (if so, don't do the authentication).
Thanks!
I think it might be better if you can provide this option to your user:
login / login using facebook account.
So you dont have to contact Facebook unless user choose to login using their FB account.
also, the authentication should be session based. session information can includes user, session key, valid time range, maybe source IP too. once the user has successfully logged in, a session is generated. then for every request you only have to check if the session key is still valid.

Categories

Resources