How to use OAuth2RestTemplate having only tokenValue? - spring

The application starts when other application calls the starting endpoint with the access token as a paremeter. The access token is a type of string.
Then I have to call a few other endpoints where the authentication is based on that token.
Is it possible to create OAuth2RestTemplate to make requests having only tokenValue withouth access token uri?

As far as I know, you can achieve this by implementing the AccessTokenProvider interface and setting it:
https://docs.spring.io/spring-security/oauth/apidocs/org/springframework/security/oauth2/client/token/AccessTokenProvider.html
Although, the OAuth2RestTemplate is exactly to simplify the use of this kind of flow for you. If your use will be straight forward maybe it's better for you to use the default RestTemplate and use the HTTP specification. So, for this, you should use an Authorization header of type Bearer. Like this:
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer " + tokenValue);
And use this header in your requests, like this:
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<BodyClass> response = restTemplate.exchange(url, HttpMethod.GET, request, BodyClass.class);
BodyClass body = response.getBody();
Hope it helps.

Related

How to authorize against an OpenId Connect secured REST-API programmatically?

I have implement a REST-API based on Spring Boot secured by Spring Security 5.2 OpenID Connect resource server. The authorization server is an IdentityServer4. So far so good, the authentication using Bearer Token (the token is determined via a dummy web page) works well.
The challenge now is to call the REST API from a client that does not require user interaction (web page).
I would like to provide the API users with an unsecured endpoint (/authorization) which can be used to receive the Bearer Token for any further secured service. Username and password should be passed as request parameters.
I have search the web and studied the docs from Spring but I did not have found something which addresses my use case.
I implemented a relatively simple solution
#GetMapping
public ResponseEntity<GetTokenResponse> getToken(#RequestBody GetTokenRequest getTokenRequest) {
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
formData.add("client_id", clientId);
formData.add("client_secret", clientSecret);
formData.add("grant_type", "password");
formData.add("scope", scopes);
formData.add("username", getTokenRequest.getUsername());
formData.add("password", getTokenRequest.getPassword());
RestTemplate restTemplate = new RestTemplate();
HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(formData, headers);
ResponseEntity<GetTokenResponse> response = restTemplate.postForEntity( tokenEndPoint, request , GetTokenResponse.class );
String accessToken = response.getBody().getAccessToken();
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
Jwt jwt = decoder.decode(accessToken);
logger.debug("Headers:\n{}", jwt.getHeaders());
logger.debug("Claims:\n{}", jwt.getClaims());
logger.info("User {}, {} '{}' authorised.", jwt.getClaimAsString("given_name"), jwt.getClaimAsString("family_name"), jwt.getClaimAsString("sub"));
return response;
}
The response contains the bearer token and can therefore be used for the API calls.

301 ResponseCode in RestTemplate Client

I am trying to consume a service using RestTemplate in Spring. I tried around and each time getting 301 as response code. However, I am good to get a json response from the same uri while using POSTMAN.
Here is the code to get the statelist:
HttpHeaders headers = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
String baseUri = "http://www.whizapi.com/api/v2/util/ui/in/indian-states-list";
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUri)
.queryParam("project-app-key","my-key");
HttpEntity<String> entity = new HttpEntity<String>("parameters", headers);
ResponseEntity<String> stats = restTemplate.exchange(builder.build().toUri(),
HttpMethod.GET,entity ,String.class);
Please suggest if I am missing anything.
HTTP 301 is a permanent redirect. You are accessing an outmoded url.
Having checked this url http://www.whizapi.com/api/v2/util/ui/in/indian-states-list redirected to https://www.whizapi.com/api/v2/util/ui/in/indian-states-list.ashx.
The same can be seen in Location response header as shown in the below image.
So, you should directly request https://www.whizapi.com/api/v2/util/ui/in/indian-states-list.ashx from your restTemplate object.
More details could be found here
Hope this helps!

Unable to Retrieve cookies when request is called using Spring RestTemplate

I have two web applications. There is a HttpSession at second application and first application knows the sessionid of it.
I am making a request from First application to second application using RestTemplate by adding its session id to headers.
When the request is received at the second application, I am trying to read all its cookies from request. But, I only see the JSESSIONID cookie and don't get all the cookies that I set previously.
Suppose, I access the same url on the browser manually, I see all those cookies retrieved.
Request made in First application:
String sessionId = "6iuvgwy5ceqzwlxh646qo0ms";//SessionId of second application
String url = "http://example.com/data/retrieve";
HttpHeaders requestHeaders = new HttpHeaders();
requestHeaders.add("Cookie", "JSESSIONID=" + sessionId);
HttpEntity<String> requestEntity = new HttpEntity<String>(null, requestHeaders);
ResponseEntity<String> responseEntity = restTemplate.exchange(url,
HttpMethod.GET, requestEntity, String.class);
If this can't be done using Spring RestTemplate, please suggest me if there is any other way of doing it.
There is no session cookie. Cookie is just a header.
You lind of reset the header to have only JSESSIONID.
To fix it change the logic a bit
You somehow get the session id (I guess from some request). Get not only the session but all the rest cookie as well and add them to the requestHeaders when you call the restTemplate.

Spring RestTemplate receives "401 Unauthorized"

I am using the following to retrieve JSON via RestTemplate in Spring 4:
protected DocInfoResponse retrieveData(String urlWithAuth) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Basic " + auth.getSig());
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<DocInfoResponse> response = restTemplate.exchange(urlWithAuth, HttpMethod.GET, request, DocInfoResponse.class);
return response.getBody();
}
I used the same code (with different response class) to successfully get a JSON doc from the same site (with different parameters to get a different doc).
When I execute the above code I receive the following stack trace (in part):
Caused by: org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at
org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91) ~[spring-web-4.3.7.RELEASE.jar:4.3.7.RELEASE]
Can anyone point me to why this might be receiving the exception?
I found that my issue originally posted above was due to double encryption happening on the auth params. I resolved it by using UriComponentsBuilder and explicitly calling encode() on the the exchange().
SyncResponse retrieveData(UriComponentsBuilder builder) {
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
HttpEntity<String> request = new HttpEntity<String>(headers);
ResponseEntity<SyncResponse> response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.GET, request, SyncResponse.class);
return response.getBody();
}
My UriComponentsBuilder was built using:
UriComponentsBuilder buildUrl(String urlString) {
UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(urlString);
return auth.appendAuth(builder);
}
(The auth.appendAuth() adds additional .queryParams() needed by the target service in urlString.)
The call to execute this was retrieveData(buildUrl(urlString));.
After investigating on my own problem, I realized that FireFox RESTClient was successful because I was connected to the target URL. The Basic Auth I thought I was using, was not so basic after all.
Eventually, I read the doc of the app i was trying to connect to and realized they propose a connection token mechanism. Now it works.
After reading your code, I say it looks quite OK, although I'm not sure what is your object auth on which you call getSig.
First things first: try to access your service from any client, like a web browser, a PostMan or RESTClient. Make sure you successfully retrieve your infos WITHOUT being connected to your app!!!
Depending on the result, I say you should, either try to encrypt manually your Authorization token (you'll easilly find posts on this site to show you how to) or try another connection mechanism.
The process of creating the Authorization header is relatively straightforward for Basic Authentication, so it can pretty much be done manually with a few lines of code:
HttpHeaders createHeaders(String username, String password){
return new HttpHeaders() {{
String auth = username + ":" + password;
byte[] encodedAuth = Base64.encodeBase64(
auth.getBytes(Charset.forName("US-ASCII")) );
String authHeader = "Basic " + new String( encodedAuth );
set( "Authorization", authHeader );
}};
}
Then, sending a request becomes just as simple:
RestTemplate restTemplate = new RestTemplate();
restTemplate.exchange
(uri, HttpMethod.POST, new HttpEntity<T>(createHeaders(username, password)), clazz);
https://www.baeldung.com/how-to-use-resttemplate-with-basic-authentication-in-spring#manual_auth

Spring RestTemplate session

I'm trying to use spring rest template to do a post request to login in.
When I receive the response in my first request i store my session id which is received via cookie. I retrieve it in a set-cookie response header which i get via:
//first request
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
LinkedMultiValueMap<String, Object> mvm = new LinkedMultiValueMap<String, Object>();
mvm.add("LoginForm_Login", "login");
mvm.add("LoginForm_Password", "password");
ResponseEntity<String> result = restTemplate.exchange(uriDWLogin, HttpMethod.POST, requestEntity, String.class);
result.getHeaders().get("Set-Cookie").stream().forEach(System.out::println);
then in every subsequent request i set the Cookie request header with the values received in the first request:
//subsequent request
RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders();
headers.set("Cookie",cookies.stream().collect(Collectors.joining(";")));
HttpEntity<String> entity = new HttpEntity<String>(headers);
RestTemplate.exchange("http://url", HttpMethod.POST, entity, String.class);
all goes well for the second request, but I can not keep the session for the others requests
You will need to use some kind of cache to store your access token.
When you'll be accessing downstream service, you take the token from cache. If cache doesn't contain token, you will authenticate and retrieve it and store to cache first.
Caching is always tricky topic, because it has to be thread-safe. I would try to avoid servlet sessions. You are consuming service, not being consumed.
There are various caching options, but as you are already using Spring, spring caching may be good fit. Take a look at this Spring Cache guide to start.

Resources