Spring Boot Webclient with NTLM Authentication - java

I am using Spring WebClient to hit a Rest Service which requires NTLM Authentication. It works in Postman like below:
Hit the URL - http://example.com:83/api/auth/token with authentication as NTLM authentication and provide the user name and password. When hitting this service, it returns a token.
This token has to be passed in header as bearer token for the actual post service -
http://example.com:89/api/v1/employee
But when I tried the same using Spring WebClient, I am facing 401 - Unauthorized error. Below the code snippet I am using.
BasicCredentialsProvider tokenProvider = new BasicCredentialsProvider();
tokenProvider.setCredentials(
new AuthScope("http", "example.com", 83, "/api/auth/token", StandardAuthScheme.NTLM),
new NTCredentials("testuser", "pwd".toCharArray(), null, null)
);
webClient = WebClient.builder()
.clientConnector(new HttpComponentsClientHttpConnector
(HttpAsyncClients
.custom()
.setDefaultCredentialsProvider(tokenProvider)
.setTargetAuthenticationStrategy(DefaultAuthenticationStrategy.INSTANCE)
.setDefaultRequestConfig(
RequestConfig.custom()
.setAuthenticationEnabled(true)
.setTargetPreferredAuthSchemes(Collections.singletonList(StandardAuthScheme.NTLM))
.setExpectContinueEnabled(true)
.build())
.build()))
.build();
ParameterizedTypeReference<LinkedHashMap<String, Object>> result =
new ParameterizedTypeReference<LinkedHashMap<String, Object>>() {};
Map<String, Object> body = new HashMap<>();
body.put("test-key", "value");
webClient.post().uri("http://example.com:89/api/v1/employee").contentType(MediaType.APPLICATION_JSON).accept(MediaType.ALL).bodyValue(body).retrieve().bodyToMono(result).block();
Is this right approach?

Related

Post request for an api works with restTemplate but throws proxy error with webClient

I'm trying to call an api with WebClient and it throws 404 with the below error message
{"fault":{"faultstring":"Unable to identify proxy for host: localhost:9943 and url: \/url\/post\/msg","detail":{"errorcode":"messaging.adaptors.http.flow.ApplicationNotFound"}}}
Below is the snippet where I'm trying to call the api
HttpClient httpClient = HttpClient.create()
.tcpConfiguration(client -> client
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
.doOnConnected(conn -> conn.addHandlerLast(new ReadTimeoutHandler(10000))
.addHandlerLast(new WriteTimeoutHandler(10000))));
ClientHttpConnector connector = new ReactorClientHttpConnector(httpClient);
WebClient webClient = WebClient.builder().baseUrl("https://example.com").clientConnector(connector)
.exchangeStrategies(ExchangeStrategies.builder()
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(16 * 1024 * 1024)).build())
.build();
return webClient.post().uri(uriBuilder -> {
URI url = uriBuilder.path(/postMsg).build();
return url;
}).headers(t -> {
t.addAll(headers);
}).body(Mono.just(request), Request.class)
.exchangeToMono(clientResponse -> handleResponse(clientResponse));
Tried with and without proxy and it doesn't work.
But the same api works fine from postman and with restTemplate. Below is the snippet which works fine with restTemplate
HttpHeaders header = new HttpHeaders();
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
HttpEntity<Request> entity = new HttpEntity<Request>(request, headers);
String response = restTemplate
.exchange("https://example.com/postMsg", HttpMethod.POST, entity, String.class)
.getBody();
request and headers are the same for both
webClient and restTemplate implementation.
Any pointers on what I'm doing wrong is appreciated

ADMIN_NO_SRP_AUTH login failing without static credentials

I have the following flow where I try to log in:
Map<String, String> authParams = new HashMap<>();
authParams.put("USERNAME", email);
authParams.put("PASSWORD", oldPassword);
AWSCognitoIdentityProvider provider = AWSCognitoIdentityProviderClientBuilder.standard()
.withRegion(Regions.US_WEST_1)
.build();
AdminInitiateAuthRequest adminInitiateAuthRequest = new AdminInitiateAuthRequest()
.withClientId(APP_CLIENT_ID)
.withUserPoolId(POOL_ID)
.withAuthFlow(AuthFlowType.ADMIN_NO_SRP_AUTH).withAuthParameters(authParams);
AdminInitiateAuthResult result = provider.adminInitiateAuth(adminInitiateAuthRequest);
But when I run this I get:
com.amazonaws.services.cognitoidp.model.InvalidParameterException: Missing required parameter auth parameters. (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: InvalidParameterException; Request ID: fddb5a6d-fb9f-4456-a8fa-faf75a2bd94e; Proxy: null)
The flow succeeds if I do (locally):
AWSCredentials credentials = new BasicAWSCredentials(
"accessKey",
"secretKey");
and do:
AWSCognitoIdentityProvider provider = AWSCognitoIdentityProviderClientBuilder.standard()
.withCredentials(new AWSStaticCredentialsProvider(credentials))
.withRegion(Regions.US_WEST_1)
.build();
Is there something that I need to enable to use ADMIN_NO_SRP_AUTH? As you can see I have it enabled.
The issue was that I was passing null inadvertently as the oldPassword. That made the API give the 'Missing Required Parameter'

OAuth 2.0 Access Tokens and Client Certificate

So I'm currently developing a Spring boot MS that needs to connect to an external API which has OAuth 2.0 implemented.
The API Store uses a custom version of a grant type called a Client Certificate.
This grant type uses a combination of Mutual SSL and Application level credentials.
It requires two identity factors:
Identity Factor 1 – Mutual SSL: Certificate created by me signed by the API store owner
Identity Factor 2 – Application Level Credentials: {consumerKey:consumerSecret}
The curl command for obtaining this token is:
curl -k -d "grant_type=client_cert" --basic -u "{consumer key}:{consumer secret}" -H "Content-Type: application/x-www-form-urlencoded" --cert {Certificate Pem} https://api.examplestore.com/token
How can I translate this to my Spring boot application?
I've currently written this piece of code, but I think I'm far off.
public void TokenRequest() {
ResponseEntity<String> response = null;
RestTemplate restTemplate = new RestTemplate();
String credentials = String.format("%s:%s", consumerKey, consumerSecret);
String encodedCredentials = new String(Base64.getEncoder().encodeToString(credentials.getBytes()));
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
//headers.setCertificate??
headers.add("Authorization", "Basic " + encodedCredentials);
HttpEntity<String> request = new HttpEntity<String>(headers);
response = restTemplate.exchange(tokenUrl, HttpMethod.POST, request, String.class);
}
Any help is welcome. Thank you :)
I think you are not that far off.
You defenitely need to include the body:
HttpEntity<String> request = new HttpEntity<String>("grant_type=client_cert", headers);
Also you need to include the certificate, maybe like this:
SSLContext sslContext = SSLContextBuilder.create()
.loadTrustMaterial(new URL("/path/to/your/cert"), "certpassword".toCharArray())
.setProtocol("yourProtocol")
.build();
final HttpClient httpClient = HttpClientBuilder.create()
.setSSLContext(sslContext)
.build();
final ClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory(httpClient);
RestTemplate restTemplate = new RestTemplate(requestFactory);
...

Why is my Session gone when using RestTemplate to access Spring MVC?

I am trying to access a Spring MVC app. That uses a CSRF Token. I do an initial GET to receive the Token. Then add it to my POST with my JSESSIONID. However, during debug the Server app doesn't find my JSESSIONID. And therefore, doesn't authenticate my token, and gives me 403.
I can't tell but it looks like my GET JSESSIONID doesn't get saved in the server HTTP Session repository.
Is there a way, to validate:
The session is in the server context?
Am I sending the correct header data?
Here's my code:
public String testLogin() {
ResponseEntity<String> response =
restTemplate.getForEntity(LOGIN_RESOURCE_URL, String.class);
List<String> cookies = new ArrayList<String>();
cookies = response.getHeaders().get("Set-Cookie");
String[] firstString = cookies.get(0).split("=|;");
String jsessionPart = firstString[1];
String[] secondString = cookies.get(1).split("=|;");
String tokenPart = secondString[1];
BasicCookieStore cookieStore = new BasicCookieStore();
BasicClientCookie cookie = new BasicClientCookie("JSESSIONID",
jsessionPart);
cookie.setDomain(".mydomain.com");
cookie.setPath("/");
cookieStore.addCookie(cookie);
BasicClientCookie cookie2 = new BasicClientCookie("X-XSRF-TOKEN",
tokenPart);
cookie2.setDomain(".mydomain.com");
cookie2.setPath("/");
cookieStore.addCookie(cookie2);
HttpClient client = HttpClientBuilder
.create()
.setDefaultCookieStore(cookieStore)
.disableRedirectHandling()
.build();
HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory();
factory.setHttpClient(client);
RestTemplate postTemplate = new RestTemplate(factory);
HttpEntity<?> requestEntity = new HttpEntity<Object>(body, headers);
ResponseEntity<String> response = postTemplate.exchange(loginUserUrl,
HttpMethod.POST, requestEntity,String.class);
To your code sample I added user name and password plus changed the content type. The 403 still happens whether i sent content type or not:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// if you need to pass form parameters in request with headers.
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
try {
map.add( URLEncoder.encode("username", "UTF-8"),
URLEncoder.encode("userdev", "UTF-8") );
map.add(URLEncoder.encode("password", "UTF-8"),
URLEncoder.encode("devpwd","UTF-8") );
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>
(map, headers);
ResponseEntity<String> response =
this.restTemplate(builder).exchange(RESOURCE_URL, HttpMethod.POST,
requestEntity, String.class);
Instead of messing around with cookies yourself let the framework, Apache HttpClient, handle this for you. Configure the RestTemplate to work with a properly configured HttpClient.
Something like this should do the trick
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.requestFactory(this::requestFactory)
.build();
}
#Bean
public HttpComponentsClientHttpRequestFactory requestFactory() {
RequestConfig defaultRequestConfig = RequestConfig.custom()
.setCookieSpec(CookieSpecs.DEFAULT)
.setExpectContinueEnabled(true)
.build();
CloseableHttpClient httpClient = HttpClientBuilder.create()
.setDefaultCookieStore(new BasicCookieStore())
.setDefaultRequestConfig(defaultRequestConfig).build();
HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
requestFactory.setHttpClient(httpClient);
return requestFactory;
}
This will configure the RestTemplate to use a HttpClient that stores cookies in a CookieStore in between requests. Reuse the configured RestTemplate and don't create a new one because you might need it.

HttpClientErrorException: 401 Unauthorized basic authentication

Im trying to get the url with basic authentication. I set the user/password as given below. The same credential is working in postman.
String RELATIVE_IDENTITY_URL = "http://my_url/api/core/v3/people/email/abc#example.com";
RestTemplate restTemplate;
Credentials credentials;
//1. Set credentials
credentials = new UsernamePasswordCredentials("admin", "admin");
CredentialsProvider credsProvider = new BasicCredentialsProvider();
credsProvider.setCredentials( AuthScope.ANY, credentials);
//2. Bind credentialsProvider to httpClient
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
httpClientBuilder.setDefaultCredentialsProvider(credsProvider);
CloseableHttpClient httpClient = httpClientBuilder.build();
HttpComponentsClientHttpRequestFactory factory = new
HttpComponentsClientHttpRequestFactory(httpClient);
//3. create restTemplate
restTemplate = new RestTemplate();
restTemplate.setRequestFactory(factory);
//4. restTemplate execute
String url = RELATIVE_IDENTITY_URL;
String xml = restTemplate.getForObject(url,String.class);
System.out.println("Done");
I think the credentials are not set correctly. What is wrong here.?
Error:
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 401 Unauthorized
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:667)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:620)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:580)
at org.springframework.web.client.RestTemplate.getForObject(RestTemplate.java:287)
at com.src.AuthRestService.main(AuthRestService.java:85)
You are missing the auth header and setting the credentials in your rest template execution.

Categories