ldap & JWT authentication without spring security - java

I'm trying to create a security module that will check against LDAP for user credentials (on login) and on successful login generate a JWT for further requests to the server.
currently my module works like this:
i have 3 rest API endpoints to provide authentication (login, validate JWT, logout) that are not protected as anyone must be able to access those endpoints,
and also 1 userUpdate endpoint protected with spring security via a JWTAuthenticationProvider
all the stuff pertaining the JWT is ready, now I just need to create a method to check in LDAP if the user and password are correct. but i am having some trouble understanding how am i supposed to do so
i already have the master user and pass to conect to ldap, but most of the examples i find about ldap authentication are with spring security and i dont think thats the way to do it in this case as i need to verify the matching us/pass only on login (and not protect my endpoints with security).
can anyone tell me how im supposed to do that verification? any stuff i am not being clear on? please ask and comment and answer.
thanks
oh one edit:
#Override
public AuthenticationResponse login(AuthenticationRequest authenticationRequest) {
checkNotNull(authenticationRequest, "The authenticationRequest is a required argument!");
AuthenticationResponse authenticationResponse = AuthenticationResponse.builder().build();
//currently a pseudo authentication, here is where i should authenticate against LDAP
Optional<Usuario> optionalUsuario = service.findByNombreUsuario(authenticationRequest);
if (optionalUsuario.isPresent()) {
Usuario usuario = optionalUsuario.get();
String token = JwtTokenUtil.generateToken(authenticationRequest);
authenticationResponse.setAuthenticationToken(token);
repository.saveToken(UserToken.builder()
.nombreUsuario(usuario.getNombreUsuario())
.roles(usuario.getRoles())
.build(), token);
as you can see i intent to make the authentication against ldap only at login, and only to check if the user and pass are correct, i will manage the roles and authorities using other DB
another edit: i have some basic ldap structure for ldap auth using spring security, but i always get bad credentials
edit again: i managed to make it work with spring security, but (as expected) was told by my team that we need to implement that authentication without spring security to integrate with our custom role loader and token creation

use http://docs.spring.io/spring-security/site/docs/current/apidocs/org/springframework/security/ldap/authentication/LdapAuthenticationProvider.html to authenticate and get roles from LDAP, it should be done using spring security, I probably missed smth but could you explain why you don't want use it as far it is security standart

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!

Spring boot LDAP auth in rest API without spring security

I have an API that is consumed by a mobile application that goes something like this:
#PostMapping("/myAPI")
public String getAgentProductsList(#RequestParam String username, #RequestParam String password, #RequestParam String productType) {
/*
here I need to verify the username and password in LDAP and retrieve the user's related data
*/
/*
some more code
*/
return "some products related to the user authenticated previously";
}
I need to do the authentication without using spring security or any redirection (that's what I've found in almost all the basic tutorial out there). So authenticating the username/password must happen inside the my endpoint.
That's my first time with LDAP so any helpful links or explinations is highly appreciated.

How do I get the #AuthenticatedPrincipal from a web socket simpUser?

I'm trying to get logged in users' details who are connected via websocket on Spring Boot 2. To do this, I'm currently using SimpUserRegistry to find a list of connected SimpUsers.
Since Spring Boot 2.4, I noticed the SimpUser class has a getPrincipal() method that returns a generic Principal object. As each user is supposed to login via Spring Security's mechanisms, I thought I was able to cast it to Spring Security's UserDetails to get the logged in user , but I realize it wasn't the case.
Does anyone know how I can make use of getPrincipal or other ways to get logged in userDetails?
First of all, let's make it clear that, to use getPrincipal() with websocket, you have to implement websocket authentication and authorization through Interceptor (as far as I know SpringSecurity doesn't do this automatically).
After doing the above correctly, you can now use the getPrincipal () method. It will return The identity of the principal being authenticated (maybe Username, email,...)
You can use code that looks like this:
#MessageMapping("/test")
public void doSomething(#Payload AppMessage appMessage, Principal principal) {
String username = principal.getName();
// find userDetail with username here
}

Storing a JWT token

I am using JWT for authentication. Now i want to store this token which is being generated in one class, so that any other class can use it until the session expires. What is the best way to do it. My application is in spring boot.
Adding more. I am making a client which hits a rest webservice with the credentials to get the token. Now i need to store this token somewhere so that further rest requests can use it.
Is it fine to store the token in httpSession and retrieve it further.
Usually is not a good idea to store a JWT token, since it should contain all the information to identify and authorize a service user without hit the DB/persistence layer.
But maybe there are situations that require to persist it among user data. In this case you can store it in a table/collection and retrieve it while authenticating the user.
If you are using Spring + Spring Security, you can then populate a token field in a custom User implementation.
You can retrieve user data this way:
CustomUser userDetails = (CustomUser)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
It is not preferred to store a JWT token in order to protect it from CSRF.
But if you want to persist or use it anyways, one way in spring boot is you can just include the #RequestHeader parameter in any rest request with the value as "Authorization" and then you can just fetch out the jwt token from it and can use it as per your functionality :
#GetMapping("/abc")
public ResponseEntity<String> getToken(
#RequestHeader(value="Authorization") String authorizationHeader){
String jwt = authorizationHeader.substring(7);
//your functionality
return ResponseEntity.ok("JWT Token successfully retrieved");
}

Can't link DB user with social networks with Oauth2

I use SpringBoot Security and Spring Security Oauth2.
And i use OAuth2ClientAuthenticationProcessingFilter for login with social networks.
In this filter i use custom UserInfoTokenServices, with overrirded loadAuthentication(String accessToken)
In this method before return OAuth2Authentication i write social user id to DB user or create it, if does not exist (with extra logic i give him some password).
It works fine and later user can login with his email and password, as DB user or with his social network profile and it will be the same profile.
But now i need to link new social, when user already logged.
I tried to use same endpoint /socialName/login, but now super.loadAuthentication(accessToken) of UserInfoTokenServices throw:
Getting user info from: https://www.googleapis.com/oauth2/v2/userinfo
Could not fetch user details: class org.springframework.web.client.HttpClientErrorException, 401 Unauthorized
How to fix it? Or what should i do in this case?

Categories

Resources