Jhipster UAA External Client - java

I use jhipster UAA and Gatewy App in my server running in EC2.
(https://www.jhipster.tech/images/microservices_architecture_detail.002.png)
I have application running external cloud to put data in this server, It use REST endpoint to make request.
I need turn on security in this endpoint, then use access token or client secret.
I turned on the security, and copy my secret token by FrontEnd application. My first request work, but the second request don't work.
My OAuth2RestTemplate.
#Bean
public OAuth2RestTemplate redditRestTemplate(OAuth2ProtectedResourceDetails resourceDetails, OAuth2ClientContext clientContext) {
clientContext.setAccessToken(new DefaultOAuth2AccessToken(applicationProperties.getAccessToken()));
OAuth2RestTemplate template = new OAuth2RestTemplate(resourceDetails, clientContext);
AccessTokenProvider accessTokenProvider = new AccessTokenProviderChain(
Arrays.<AccessTokenProvider>asList(
new ImplicitAccessTokenProvider(),
new ResourceOwnerPasswordAccessTokenProvider(),
new ClientCredentialsAccessTokenProvider())
);
template.setAccessTokenProvider(accessTokenProvider);
return template;
}
My request.
OAuth2RestTemplate b = a.getBean(OAuth2RestTemplate.class);
String c = b.getForEntity("https://cloud.com/api/app/v1/events", String.class).getBody();
First request Ok.
I need save the new accessToken?
Is there any way to do this integration using "client secret" or anything?

After making the authentication request /auth/login you have to save the accessToken received in the response. Subsequent requests must contain this access token in the header Authorization: Bearer <accessToken>

Related

Authentication object loss into ClientRegistrationRepository.findByRegistrationId() method for WebClient with oauth2 filters

In a multi-tenancy Spring boot web app (with spring security 5), I need to make http requests to externals services secured by oauth2.
Each client using the same app can has multiple oauth2 config depending on external services it can access.
There is how I instantiate my singleton WebClient:
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.clientCredentials()
.build();
OAuth2AuthorizedClientRepository oAuth2AuthorizedClientRepository = new HttpSessionOAuth2AuthorizedClientRepository();
// oAuth2ClientRegistrationDAO is my custom bean implements ClientRegistrationRepository defined below
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(oAuth2ClientRegistrationDAO, oAuth2AuthorizedClientRepository);
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
ServletOAuth2AuthorizedClientExchangeFilterFunction filter = new ServletOAuth2AuthorizedClientExchangeFilterFunction(
authorizedClientManager);
// WebClient to be used to communicate thought oauth2 secured webservices
WebClient webClient = WebClient.builder().apply(filter.oauth2Configuration()).build();
This is my custom bean implements ClientRegistrationRepository used to provide right Oauth2 parameters depending on registrationId value:
#Repository
public class OAuth2ClientRegistrationDAO implements ClientRegistrationRepository {
#Override
public ClientRegistration findByRegistrationId(String registrationId) {
// Problem: auth object is NULL!
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println(auth); // print: null
// TODO get ClientRegistration from right database
// (depending logged tenant, fetched from Authentication object) and return them
}
}
The problem is when I do a request like this:
// Here, auth is not null: Ok!
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
webClient.get()
.uri("[External webservice url secured with oauth2]")
.attributes(
ServerOAuth2AuthorizedClientExchangeFilterFunction
.clientRegistrationId("[registrationId]"))
.retrieve()
.bodyToMono(String.class)
.block();
My Authentication object of logged user is null into OAuth2ClientRegistrationDAO because on asynchronous implementation of WebClient, method findByRegistrationId() is called on another thread than this used by Authenticated spring app client.
Edit: I found a potential solution using SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL); Authentication context is still present on other thread with this configuration

Resolve #RegisteredOAuth2AuthorizedClient with the token obtained outside of spring server of Spring Security 5.2.x

I have a controller method that has an argument OAuth2AuthorizedClient with annotation #RegisteredOAuth2AuthorizedClient.
ResponseEntity<String> getFoo(
#RegisteredOAuth2AuthorizedClient("custom") OAuth2AuthorizedClient client) {
OAuth2AccessToken token = client.getAccessToken();
....
}
In order to resolve this, spring look for OAuth token on OAuth2AuthorizedClientService which actually stores the previously authenticated tokens in memory.
Now, I have a scenario where I obtain JWT token from OAuth server outside of this spring resource server, and trying to authenticate this server by passing token using Authorization header.
But when spring trying to resolve OAuth2AuthorizedClient it is looking for token in-memory with the principle of JWT (which will obviously not found since token is not obtained on this server). Hence send new login redirection for new token.
Overall question would be, is it possible to resolve OAuth2AuthorizedClient with the JWT token (obtained ourside of the server) passed in Authorization header?

OAuth2 client credentials flow via Spring Boot Keycloak integration

My application consists of:
backend/resource server
UI webapp
keycloak
The UI is talking with the backend server via RESTful API using the keycloak client with authorization code grant flow. This is working fine.
Now, I need the additional possibility to access resource of the backend using a system/service account (with usually more permissions than the user). How would you implement this requirement? I thought the client credentials flow would be useful here.
Is it possible to use the OAuth2 client credentials flow with the keycloak client for Spring Boot? I found examples that used the Spring Security OAuth2 client features to achieve a client credentials flow but that feels weird because I already use the keycloak client for the OAuth thing.
Edit: Solution
Thanks for your answers which helped me a lot. In my UI webapp, I am now able to communicate with the backend either by using the authenticated user OAuth2 token or by using the token from the client credentials flow of my UI service account. Each way has its own RestTemplate, the first is done via the keycloak integration and second is done by Spring Security OAuth2 as explained here.
Yes, you can use OAuth 2.0 Client Credentials flow and Service Accounts.
Keycloak suggest 3 ways to secure SpringBoot REST services:
with Keycloak Spring Boot Adapter
with keycloak Spring Security Adapter
with OAuth2 / OpenID Connect
Here is a good explanation about this with an example in the OAuth2/OIDC way:
Tutorial by Arun B Chandrasekaran
Code sample by Arun B Chandrasekaran
If you follow this example, keep in mind:
Take care to configure your client as:
Access Type: Confidential
Authorization: Enabled
Service Account (OAuth Client Credentials Flow): Enabled
Take care to configure your target service as:
Access Type: Bearer-only
So, caller should be confidential and target service should be bearer-only.
Create your users, roles, mappers... and assign roles to your users.
Check that you have this dependencies in your spring project:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
</dependency>
Configure authentication to be used in the REST client (application.properties)
e.g.:
security.oauth2.client.client-id=employee-service
security.oauth2.client.client-secret=68977d81-c59b-49aa-aada-58da9a43a850
security.oauth2.client.user-authorization-uri=${rest.security.issuer-uri}/protocol/openid-connect/auth
security.oauth2.client.access-token-uri=${rest.security.issuer-uri}/protocol/openid-connect/token
security.oauth2.client.scope=openid
security.oauth2.client.grant-type=client_credentials
Implement your JwtAccessTokenCustomizer and SecurityConfigurer (ResourceServerConfigurerAdapter) like Arun's sample.
And finally implement your service Controller:
#RestController
#RequestMapping("/api/v1/employees")
public class EmployeeRestController {
#GetMapping(path = "/username")
#PreAuthorize("hasAnyAuthority('ROLE_USER')")
public ResponseEntity<String> getAuthorizedUserName() {
return ResponseEntity.ok(SecurityContextUtils.getUserName());
}
#GetMapping(path = "/roles")
#PreAuthorize("hasAnyAuthority('ROLE_USER')")
public ResponseEntity<Set<String>> getAuthorizedUserRoles() {
return ResponseEntity.ok(SecurityContextUtils.getUserRoles());
}
}
For a complete tutorial, please read the referred Arun's tutorial.
Hope it helps.
Following #dmitri-algazin you to implement the workflow you have basically two options:
If you want to cover other IdMs besides Keycloak which solves somehow the Single Responsibility principle, I would use RestTemplate. Below you can find the variables:
//Constants
#Value("${keycloak.url}")
private String keycloakUrl;
#Value("${keycloak.realm}")
private String keycloakRealm;
#Value("${keycloak.client_id}")
private String keycloakClientId;
RestTemplate restTemplate = new RestTemplate();
private static final String BEARER = "BEARER ";
First you need to generate the access token:
#Override
public AccessTokenResponse login(KeycloakUser user) throws NotAuthorizedException {
try {
String uri = keycloakUrl + "/realms/" + keycloakRealm +
"/protocol/openid-connect/token";
String data = "grant_type=password&username="+
user.getUsername()+"&password="+user.getPassword()+"&client_id="+
keycloakClientId;
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/x-www-form-urlencoded");
HttpEntity<String> entity = new HttpEntity<String>(data, headers);
ResponseEntity<AccessTokenResponse> response = restTemplate.exchange(uri,
HttpMethod.POST, entity, AccessTokenResponse.class);
if (response.getStatusCode().value() != HttpStatus.SC_OK) {
log.error("Unauthorised access to protected resource", response.getStatusCode().value());
throw new NotAuthorizedException("Unauthorised access to protected resource");
}
return response.getBody();
} catch (Exception ex) {
log.error("Unauthorised access to protected resource", ex);
throw new NotAuthorizedException("Unauthorised access to protected resource");
}
}
And then with the token you can retrieve information from the users:
#Override
public String user(String authToken) throws NotAuthorizedException {
if (! authToken.toUpperCase().startsWith(BEARER)) {
throw new NotAuthorizedException("Invalid OAuth Header. Missing Bearer prefix");
}
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", authToken);
HttpEntity<String> entity = new HttpEntity<>(headers);
ResponseEntity<AccessToken> response = restTemplate.exchange(
keycloakUrl + "/realms/" + keycloakRealm + "/protocol/openid-connect/userinfo",
HttpMethod.POST,
entity,
AccessToken.class);
if (response.getStatusCode().value() != HttpStatus.SC_OK) {
log.error("OAuth2 Authentication failure. "
+ "Invalid OAuth Token supplied in Authorization Header on Request. Code {}", response.getStatusCode().value());
throw new NotAuthorizedException("OAuth2 Authentication failure. "
+ "Invalid OAuth Token supplied in Authorization Header on Request.");
}
log.debug("User info: {}", response.getBody().getPreferredUsername());
return response.getBody().getPreferredUsername();
}
You can substitute this URL by the one provided by #dimitri-algazin to retrieve all the users information.
It is possible to use the Keycloak dependencies:
<!-- keycloak -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>3.4.3.Final</version>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-client</artifactId>
<version>3.1.4.Final</version>
</dependency>
And use the classes to generate the token:
Keycloak keycloak = KeycloakBuilder
.builder()
.serverUrl(keycloakUrl)
.realm(keycloakRealm)
.username(user.getUsername())
.password(user.getPassword())
.clientId(keycloakClientId)
.resteasyClient(new ResteasyClientBuilder().connectionPoolSize(10).build())
.build();
return keycloak.tokenManager().getAccessToken();
The examples are extracted from here. We also uploaded the image to Docker Hub to facilitate the interaction with Keycloak. For this reason we started with option 2). Right now we are in the process to cover other IdMs and we went for option 1) in order to avoid including extra dependencies. Conclusion:
I would go for option 2 if you stick to Keycloak because classes include extra functionalities for Keycloak tool.
I would go for option 1 for further coverage and other OAuth 2.0 tools.
We had similar requirement, get user e-mail by user uuid.
Create service user, make sure user has role "realm-management"->"view-users" (might be query-users as well)
Process is simple: do login to keycloak with service user (keep password and/or user name encoded in the properties file), make request to keycloak with accessToken in authorisation header to
GET http://{yourdomainadress}/auth/admin/realms/{yourrealmname}/users/{userId}
A way to login to keycloak using REST API:
POST http://{yourdomainadress}/auth/realms/{yourrealmname}/protocol/openid-connect/token
Headers:
Content-Type: application/x-www-form-urlencoded
Body x-www-form-urlencoded:
client_id: your-client
username: user-you-are-using
password: password-for-user
grant_type: password
client_secret: 11112222-3333-4444-5555-666666666666 (client secret is required if client "Access Type"="confidential")
Shortly: make sure your service user has right role assigned to do operation,
do login, do query keycloak (check docs to get right query url and params, always challenging)

Spring security Oauth client - Request like a postman example

I am trying to do a JUnit Tests that executes an OAuth flow.
My customer built a OAuth provider, when I make a test using postman, the postman show me a screen to fill down the credentials, after that, the postman store the information (access_token, id_token, all JWT informations), it is ok.
See the example:
My code to test is:
#Test
public void getAccessTokenViaSpringSecurityOAuthClient() {
try {
OAuth2ProtectedResourceDetails resourceDetails = googleOAuth2Details();
OAuth2RestTemplate oAuthRestTemplate = new OAuth2RestTemplate(resourceDetails);
org.springframework.http.HttpHeaders headers = new org.springframework.http.HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
OAuth2AccessToken token = oAuthRestTemplate.getAccessToken();
System.out.println(oAuthRestTemplate.getResource());
System.out.println(oAuthRestTemplate.getOAuth2ClientContext());
System.out.println(token);
Assert.assertTrue(token != null);
} catch (Exception e) {
e.printStackTrace();
}
}
public OAuth2ProtectedResourceDetails googleOAuth2Details() {
AuthorizationCodeResourceDetails googleOAuth2Details = new AuthorizationCodeResourceDetails();
googleOAuth2Details.setClientId("xxxxx");
googleOAuth2Details.setUserAuthorizationUri("https://xxx/yyy/oauth2/authorize");
googleOAuth2Details.setAccessTokenUri("https://xxx/yyy/oauth2/token");
googleOAuth2Details.setScope(Arrays.asList("openid"));
googleOAuth2Details.setPreEstablishedRedirectUri("https://www.getpostman.com/oauth2/callback");
googleOAuth2Details.setAuthenticationScheme(AuthenticationScheme.query);
googleOAuth2Details.setClientAuthenticationScheme(AuthenticationScheme.form);
return googleOAuth2Details;
}
When I run the task, this exception happens:
org.springframework.security.oauth2.client.resource.UserRedirectRequiredException: A redirect is required to get the users approval
Is it possible to test this flow?
How can I do it?
The exception explains the problem : A redirect is required to get the users approval.
Postman is hiding the fact that once the authorization process is over, the authorization server redirects your browser to a redirect_uri, or Callback URL as Postman names it. This URL collects the authorization code delivered by the authorization server, and requests a token.
See the authorization code flow for more details :
OAuth 2.0 RFC 6749
a simplfied diagram
This means that you cannot "unit test" the authorization code grant of an authorization server. You need some kind of web server to process the callback of the authorization code flow.
You could probably test your authorization a lot faster by creating a #SpringBootTest with a Spring Boot application using #EnableOAuth2Sso. Spring Boot will auto-configure an OAuth2RestTemplate for you, and you can have it #Autowired in your JUnit test.

How to extend Spring Security authentication to Zuul

I have a Netflix Zuul server receiving all HTTP requests and forwarding them to my microservice.
The underlying microservice authenticates users through Facebook oauth API. I receive the user data from Facebook API and I authenticate the user programmatically:
public Authentication login(String username, String password) {
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
Authentication result = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(result);
return result;
}
With Zuul in front this doesn't work. I think it sets up the session in the microservice environment, but never extends it to Zuul. It works fine when requests go straight to microservice.
What I've done:
I cleared the sensitiveHeaders property in my application.properties:
zuul.routes.[SERVICE_NAME].sensitiveHeaders=, but it's meant to work the other way around I think.
How do I setup a session with Zuul?
Edit:
My Zuul configuration:
# Zuul proxy
zuul.add-host-header=true
zuul.routes.httpbin.path=/**
zuul.routes.httpbin.serviceId=httpbin
zuul.routes.httbin.sensitiveHeaders=
httpbin.ribbon.listOfServers=http://localhost:8090
ribbon.eureka.enabled=false

Categories