I want to secure a backend API written in Java/SpringBoot and using Auth0. I am following the Auth0 example.
Everything works fine and as expected, however, when I apply this to my application I want to detect the user who made the API call. In various examples I've come across it appears to be possible with Spring Security by injecting the OidcUser.
Some relevant links I've found are:
Inject custom OidcUser wrapper with #AuthenticationPrincipal
Spring Security and OIDC connect
In my application, my controller looks like this:
#CrossOrigin(origins = "*", maxAge = 3600)
#RestController
#RequestMapping("/api/project")
public class ProjectController {
#PostMapping("/add")
#PreAuthorize("hasAuthority('write:project')")
public ResponseEntity<DummySubmitResponseDto> createNewProject(#AuthenticationPrincipal OidcUser oidcUser,
#RequestBody DummyDto dummyDto) {
DummySubmitResponseDto projectSubmitResponseDto = new DummySubmitResponseDto("TheResponse");
return new ResponseEntity<>(projectSubmitResponseDto, HttpStatus.CREATED);
}
}
I'm using a React SPA to get an access-token. I save it manually and use Curl or POSTMAN to call the API. However, oidcUser is always null.
I'm not sure if I'm following the correct approach. Separate to this, I don't understand how the Backend (which is a resource server) is able to get the user information from the access token.
Related
I am new to Spring Boot Security. I am performing validation of licenseKey in every end-point in REST call. It is working fine.
I want to do it in a common way like SecurityConfig extends WebSecurityConfigurerAdapter {} class so that I should not pass extra parameter in methods for validation. It means there should be common validation if the licenseKey exists then the REST calls should be authorized to go otherwise, it should throw error. Currently I am passing HttpServletRequest which contains licenseKey, the methods are working fine. But our requirement is to perform only in one place in SecurityConfig so that all the requests can be validated.
#GetMapping(path="some/path")
public ResponseEntity<> viewDetails(HttpServletRequest httpRequest, MappingVO mappingVO) {
String licenseUser = userDetailsService.getLicenseUser(httpRequest).getUser().getEmailAddress();
....
....
}
#DeleteMapping(path="some/path")
public ResponseEntity<> deletePart(HttpServletRequest httpRequest, Part part) {
String licenseUser = userDetailsService.getLicenseUser(httpRequest).getUser().getEmailAddress();
....
....
}
In class CustomUserDetails, it has been written like this.
public CustomUserDetails getLicenseUser(HttpServletRequest httpRequest) {
String userId = httpRequest.getHeader("licenseKey");
CustomUserDetails ud = (CustomUserDetails) loadUserByUsername(userId);
return ud;
}
You should add a custom filter in the filter chain in your security config that executes before each request.
Just create a Custom Filter implementing OncePerRequestFilter.java and do the license Key validation inside of that filter. The implementation logic inside your Custom filter will run once before each request that is made on your spring boot server.
Refer
https://www.baeldung.com/spring-onceperrequestfilter
https://www.javadevjournal.com/spring-boot/spring-boot-add-filter/
If you are using Spring Security and want to filter requests to verify that it has valid token passed to it (if you're not already doing this), refer to the official documentation of your respective version of spring security to see where should you add your filter inside the filterChain.
Check Filter Ordering in:
https://docs.spring.io/spring-security/site/docs/3.1.4.RELEASE/reference/security-filter-chain.html
The token validation filter should ideally be exeucted before UsernamePasswordAuthenticationFilter.
I'm trying to configure a Spring Boot application with Keycloak to have an endpoint that is both accessible for authenticated and unauthenticated users. For authenticated users, I want to return some extra information. Here is a simple example of what I'm trying to achieve:
#RestController
public class HelloController {
#GetMapping("/")
public String index(Principal principal) {
KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal) principal;
if (keycloakPrincipal != null) {
return "Hello " + keycloakPrincipal.getKeycloakSecurityContext().getToken().getPreferredUsername();
} else {
return "Hello";
}
}
}
application.properties:
keycloak.securityConstraints[0].authRoles[0] = *
keycloak.securityConstraints[0].securityCollections[0].name = Hello
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /*
So far, I only got it to work for one of both cases. If I protect the endpoint using the security constraint above, the endpoint is only accessible to authenticated users. If I remove the security constraint, the endpoint is accessible for everyone, but then the principal will always be null.
Is it possible to achieve the intended behavior?
Have you tried something like Principal principal = SecurityContextHolder.getContext().getAuthentication();?
I believe the Principal as method parameter is only populated on secured endpoints but am unsure if it would exist in the SecurityContext. If not, you need to add a Filter to add it yourself.
I was able to solve the problem by calling the authenticate() method on the HttpServletRequest object. This will trigger the authentication process and will populate the user principal whenever possible. From the docs:
Triggers the same authentication process as would be triggered if the
request is for a resource that is protected by a security constraint.
To avoid triggering an authentication challenge, I pass in a dummy response object to the authenticate() call.
I am making service to service requests using Spring's WebClient that require an OAuth2 bearer token to be added as a header to the request. I Can do this relatively easily by creating an ExchangeFilterFunction that intercepts the request, retrieves an access token, adds it to the header, and continues on. Since this is not a user request, the SecurityContextHolder does not contain an Authentication that would hold an access token for me, so instead of retrieving from that, I would like to get an access token based on my Spring security configuration (currently defined in the spring.security.oauth2.client.registration and provider properties).
The way I'm doing this now is by Autowiring an OAuth2ClientContext and then getting the AccessToken from it. Reducing the code only to what I care about for this question, I have:
#Component
public class OAuth2WebClientFilter implements ExchangeFilterFunction {
#Autowired
private OAuth2ClientContext oAuth2ClientContext;
public Mono<ClientResponse> filter(ClientRequest request, ExchangeFunction next) {
// simple retrieval of the token
String oAuth2Token = oAuth2ClientContext.getAccessToken().getValue();
// adding the token to the header of the request
request = ClientRequest.from(request).header(HttpHeaders.AUTHORIZATION, "Bearer " + oAuth2Token).build();
return next.exchange(request);
}
}
This does exactly what I want it to. However, I have recently upgraded spring-security-oauth2 to 2.5.0.RELEASE, and it is saying the OAuth2ClientContext is deprecated, but I haven't found a simple replacement for this process. So is there still a way to get an access token in a relatively simple fashion like above, and if so, how?
Also note: this concept is used elsewhere in the project and not just for the WebClient, so I'm looking to see how to properly replace an injected OAuth2ClientContext. Thanks!
Spring Security provides an exchange filter function called ServletOAuth2AuthorizedClientExchangeFilterFunction.
The ServletOAuth2AuthorizedClientExchangeFilterFunction provides a
simple mechanism for requesting protected resources by using an
OAuth2AuthorizedClient and including the associated OAuth2AccessToken
as a Bearer Token. It directly uses an OAuth2AuthorizedClientManager
and therefore inherits the following capabilities:
An OAuth2AccessToken will be requested if the client has not yet been
authorized.
authorization_code - triggers the Authorization Request redirect to
initiate the flow
client_credentials - the access token is obtained directly from the
Token Endpoint
password - the access token is obtained directly from the Token
Endpoint
If the OAuth2AccessToken is expired, it will be refreshed (or renewed)
if an OAuth2AuthorizedClientProvider is available to perform the
authorization
See https://docs.spring.io/spring-security/reference/servlet/oauth2/client/authorized-clients.html#oauth2Client-webclient-servlet for details.
This is my application property file:
spring.security.oauth2.client.registration.myclient.client-id=SampleClientId
spring.security.oauth2.client.registration.myclient.client-secret=secret
spring.security.oauth2.client.provider.myclient.authorization-uri=http://localhost:8081/auth/oauth/authorize
spring.security.oauth2.client.provider.myclient.token-uri=http://localhost:8081/auth/oauth/token
spring.security.oauth2.client.provider.myclient.user-info-uri=http://localhost:8081/auth/user/me
spring.security.oauth2.client.registration.myclient.authorization-grant-type=authorization_code
spring.security.oauth2.client.registration.myclient.redirect-uri=http://localhost:8080/hellouser
spring.security.oauth2.client.provider.myclient.userNameAttribute=user
This is my configuration class:
#Configuration class SecurityConfig extends WebSecurityConfigurerAdapter {
#throws[Exception]
override protected def configure(http: HttpSecurity): Unit = {
http.authorizeRequests.anyRequest.authenticated.and.oauth2Login
.redirectionEndpoint()
.baseUri("/")
}
}
and this my controller:
case class Message(val string: String)
#Controller
class SuccessController {
#GetMapping(path = Array("/hellouser"), produces = Array(MediaType.APPLICATION_JSON_VALUE))
def getHelloUser(model: Model, authentication: OAuth2AuthenticationToken ): Message = {
Message("hello user")
}
}
When I do login, I get back ERR_TOO_MANY_REDIRECTS
In network section of developer console I see these three call are infinitely repeated:
http://localhost:8081/auth/oauth/authorize?response_type=code&client_id=SampleClientId&state=xyz&redirect_uri=http://localhost:8080/hellouser
http://localhost:8080/hellouser?code=abc&state=xyz
http://localhost:8080/oauth2/authorization/myclient
What I am doing wrong?
Thank you
Do not set your re-direct URI to an API.
The redirect URI in the authorization code flow is used to pick up the authorization code so it then can be exchanged for tokens.
What is happening in your setup is
Request Starts
User is not logged in
Redirect to Authorization login
Redirect back from IdP to predetermined URI
Request to API /helloworld
Go to step 2.
https://auth0.com/docs/flows/concepts/auth-code has a visual of this, you are not making it to step 4, but short circuiting the flow back to step 1
Your config makes it that the auth code flow never completes. You should just keep the Spring default redirect URI and remove spring.security.oauth2.client.registration.myclient.redirect-uri=http://localhost:8080/hellouser
Nor should you need to set the redirectionEndpoint#baseUri config.
Edit,
Aditional clarification is that with Spring Security the initial request at step 1 is saved, and when the Authorization Code Flow completes at step 4 Spring will automatically replay the saved request.
We have 8 java microservices talking to each other in kubeneters cluster. Each microservice is bundled with auth library which intercepts and validates/renews JWT token for each REST request to controllers.
Scenario:
From Frontend, we get access token for the first time, Authentication gets successful. Lets say
Frontend hit 'Microservice A' with access token - Successful
'Microservice A' internally hits 'Microservice B' via restTemplate.
My 'Microservice B' also needs logged in user details.
Issue: I have to pass same access token from 'A' to 'B' but I am not able to get access token in Controller/Service logic but can get only in filters where token is being validated. I can get token in Rest Controllers by adding following argument in all rest methods in controller:
#RequestHeader (name="Authorization") String token
But I dont want to go with this approach as I have to pass this token to everywhere till end and have to declare this argument in all APIS.
I want to get token from TokenStore by passing authentication object. We are using Oauth2 and I checked the code in library, There are many tokenStore providers.
In DefaultTokenServices.java class, I am calling
Authentication auth = SecurityContextHolder.getContext().getAuthentication() // Passed this auth to tokenStore
String token = tokenStore.getAccessToken(auth).getValue(); // NullPointerException
My code is going through JWTTokenStore provider which is returning null. I checked, there is a provider called InMemoryTokenStore.class which actually extrActs token from store. But my flow is not going into in memory implementation.
Is there any way I can get token afterwards without grabbing it in controller via arguments? or how can I enable/use inMemoryTokenStore?
Also recommend something better for kubernetes intercommunication authentication?
TIA
It looks like you're using Spring (and Spring Security), so I believe the relevant part of the docs is the part on Bearer Token Propagation.
Its recommendation is to use a WebClient (the recommended replacement for RestTemplate as of Spring 5) that uses the provided ServletBearerExchangeFilterFunction to automagically propagate the JWT token from the incoming request into the outgoing request:
#Bean
public WebClient rest() {
return WebClient.builder()
.filter(new ServletBearerExchangeFilterFunction())
.build();
}
On RestTemplate, the docs say:
"There is no dedicated support for RestTemplate at the moment, but you can achieve propagation quite simply with your own interceptor"
and the following example is provided:
#Bean
RestTemplate rest() {
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add((request, body, execution) -> {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null) {
return execution.execute(request, body);
}
if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
return execution.execute(request, body);
}
AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
request.getHeaders().setBearerAuth(token.getTokenValue());
return execution.execute(request, body);
});
return rest;
}
I don't believe you need to be looking at TokenStores if all you're trying to do is propagate the token. Remember everything relevant about a JWT should be inside the token itself. (Which is why the doc for the JwtTokenStore explains that it doesn't actually store anything, but just pulls info out of the token, and will return null for some methods, including the getAccessToken() method you're calling.)