Im having an issue where I have a good oauth token with bearer type but whenever I try to make an api call it throws me a 403 Forbidden message. The call is to just get the posts from a sub reddit page(i.e. https://www.reddit.com/r/gaming.json) but with the oauth base url. Not sure if I'm missing header I need or something. Someone please help.
ServiceGenerator:
public class ServiceGenerator {
private static final String OAUTH_BASE_URL = "https://oauth.reddit.com/";
private static final String CLIENT_ID = "XXXXXXX";
private static OkHttpClient.Builder httpClient;
private static Retrofit.Builder builder;
public static <S> S createService(Class<S> serviceClass, final Context context) {
httpClient = new OkHttpClient.Builder();
builder = new Retrofit.Builder()
.baseUrl(OAUTH_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
final String token_type = PreferenceManager.getDefaultSharedPreferences(context).getString("token_type", "");
final String refresh_token = PreferenceManager.getDefaultSharedPreferences(context).getString("refresh_token", "");
final String access_token = PreferenceManager.getDefaultSharedPreferences(context).getString("access_token", "");
if(!access_token.equals("")) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
Log.d(TAG, "Intercepting request: " + request.url());
if(response.header("No-Authentication") == null){
response = response.newBuilder()
.addHeader("User-Agent", "my user agent")
.addHeader("Authorization", token_type + " " + access_token)
.build();
}
return response;
}
});
httpClient.authenticator(new Authenticator() {
#Override
public Request authenticate(Route route, Response response) throws IOException {
if(responseCount(response) >= 2) {
// If both the original call and the call with refreshed token failed,
// it will probably keep failing, so don't try again.
return null;
}
Log.d(TAG, "Refreshing Token");
String credentials = CLIENT_ID + ":";
String encodedCredentials = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
// We need a new client, since we don't want to make another call using our client with access token
RedditService tokenClient = createService(RedditService.class);
Call<AccessToken> call = tokenClient.refreshToken("Basic " + encodedCredentials, "refresh_token", refresh_token);
try {
retrofit2.Response<AccessToken> tokenResponse = call.execute();
if(tokenResponse.code() == 200) {
AccessToken newToken = tokenResponse.body();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putBoolean("logged_in", true).apply();
prefs.edit().putString("token_type", newToken.token_type).apply();
prefs.edit().putString("access_token", newToken.access_token).apply();
prefs.edit().putString("refresh_token", newToken.refresh_token).apply();
return response.request().newBuilder()
.header("Authorization", newToken.token_type + " " + newToken.access_token)
.build();
} else {
return null;
}
} catch(IOException e) {
return null;
}
}
});
}
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
private static int responseCount(Response response) {
int result = 1;
while ((response = response.priorResponse()) != null) {
result++;
}
return result;
}
}
Client (tried with and without .json):
public interface RedditService {
#FormUrlEncoded
#POST("api/v1/access_token")
#Headers("No-Authentication: true")
Call<AccessToken> getLoginToken(#Header("Authorization") String authorization,
#Field("grant_type") String grant_type,
#Field("code") String code,
#Field("redirect_uri") String redirect_uri);
#FormUrlEncoded
#POST("api/v1/access_token")
#Headers("No-Authentication: true")
Call<AccessToken> refreshToken(#Header("Authorization") String authorization,
#Field("grant_type") String grant_type,
#Field("refresh_token") String redirect_uri);
#GET("/r/{subreddit}/{sort}")
Call<Page> getSubredditPage(#Path("subreddit") String subreddit,
#Path("sort") String sort,
#Query("after") String after);
}
EDIT:
Here's the headers that are being sent along with the request
Content-Type: text/html
Cache-Control: no-cache
Transfer-Encoding: chunked
Accept-Ranges: bytes
Date: Fri, 25 Nov 2016 20:58:55 GMT
Via: 1.1 varnish
Connection: keep-alive
X-Served-By: cache-ord1733-ORD
X-Cache: MISS
X-Cache-Hits: 0
X-Timer: S1480107535.759884,VS0,VE19
Set-Cookie: loid=omeUS2oZLIDWukuAYK; Domain=reddit.com; Max-Age=63071999; Path=/; secure
Set-Cookie: loidcreated=1480107535000; Domain=reddit.com; Max-Age=63071999; Path=/; secure
Server: snooserv
Authorization: bearer XXXXXXXXXXXXXXX
User-Agent: my-user-agent
Related
I'm trying to make a request to the Third-party API, but I'm running into some issues using OkHTTP.
I'm using AWS4Signer to sign the request. I'm able to generate the credentials for the same.
Request<Void> requestAws = new DefaultRequest<Void>("sts");
requestAws.setHttpMethod(HttpMethodName.POST);
requestAws.setEndpoint(URI.create("third pary api call which uses https"));
requestAws.addHeader("x-amz-security-token", sessionCredentials.getSessionToken());
requestAws.addHeader("Content-Type", "application/json");
//sign the request
AWS4Signer signer = new AWS4Signer();
signer.setServiceName(Constant.SERVICE_NAME);
signer.setRegionName(Constant.AWS_REGION);
signer.sign(requestAws, new AWSCredentials() {
#Override
public String getAWSSecretKey() {
return sessionCredentials.getAccessKeyId();
}
#Override
public String getAWSAccessKeyId() {
return sessionCredentials.getSecretAccessKey();
}
});
Map<String, String> headers = requestAws.getHeaders();
String x_date = null;
String x_token = null;
String authorization = null;
String x_content = null;
//get and assign values
for (Map.Entry<String, String> entry : headers.entrySet()) {
if (entry.getKey().equals("x-amz-security-token")) {
x_token = entry.getValue();
}
if (entry.getKey().equals("X-Amz-Date")) {
x_date = entry.getValue();
}
if (entry.getKey().equals("Authorization")) {
authorization = entry.getValue();
}
}
logger.info("Headers body response: " + JsonUtils.jsonize(headers));
String json = objectMapper.writeValueAsString(emailRequestBody);
postHandler.post(x_date, x_token, authorization, json);
Below is the request code of okHTTP
String post(String x_date, String x_token, String authorization, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url("https url is here")
.addHeader("Content-Type", "application/json")
.addHeader("X-Amz-Date", x_date)
.addHeader("x-amz-security-token", x_token)
.addHeader("Authorization", authorization)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Below is how the request looks like with headers:
Request{method=POST, url=https://cbc.com/api/send/email, headers=[Content-Type:application/json, X-Amz-Date:20220125T111056Z, x-amz-security-token:FwoGZXIvYXdzEHUaDF6/kQ0g7Mog7W1f7CK0ATG5xhFIXP34wRjziAkJKhw9vE5cbADBOpji7uqtLp5GLGLay+e9O2deFRB4eSpUMOOThDCEQg1tum43iX4a+8Kikuc3fv5gDjbMrdLJYAK3piYVbOAET8BAXdDdkPZVG+nNu31cEWZe9HC60svIj0m95YZ9Xx5rBIDm0AVWtj4JRCmonNm1ymCNRB4GTjhEzgnxlkqEYfdUivFdlORq/IlIssUzzV04fkr0kiqDiE9GrmU51ijAtb+PBjIt9MWbM8+x4z+y+IV4JFjuK4zrVW3Iaw4xUG/C+mpcCrZrunh+8fWgVTR6In1r, Authorization:AWS4-HMAC-SHA256 Credential=medS2y7xvISbOf7ke3IWthyCMV5koeTDD5r3gkxJ/20220125/us-west-2/execute-api/aws4_request, SignedHeaders=content-type;host;x-amz-date;x-amz-security-token, Signature=d862c9ed8175770244e17fd3cb216c2a92138183ad427ed67fc5f284a1a75266]}
Below is the response:
Response{protocol=h2, code=403, message=, url=https://cbc.com/api/send/email}
Why the response is returning 403? Can someone help me what I missed? Thank you for your time.
im new with rest assured and i make make one test to extract access token and some other params and i want to use these access token in the request headers for all other tests
is there away to set global header for all test methods i have, or make function to run once per all test and inject the required request header
#Test
public void getAccessToken (){
Response body =
given()
.params("username", "test#example.com","password","pass!","grant_type","password").post("https://example.test.com/Token")
.then()
.log().body()
.statusCode(200)
.extract().response();
String access_token = body.path("access_token").toString();
String token_type = body.path("token_type").toString();
String refresh_token = body.path("refresh_token").toString();
String Authorization = "bearer " + access_token; }
}
Update
i have added the following part below, but now getting 400 status code instead of 200 seems im missing something, down below i added to sample one of them works, and other one using RequestSpecification doesn't work
Worked as expected
public class PermissionTests {
Response body =
given()
.params("username", "user#example.com","password","pass!","grant_type","password").post("https://test.example.com/Token")
.then()
.log().body()
.statusCode(200)
.extract().response();
String access_token = body.path("access_token").toString();
String token_type = body.path("token_type").toString();
String refresh_token = body.path("refresh_token").toString();
String Authorization = "bearer " + access_token;
#Test
public void addNewGraph(){
given()
.header("officeId",1)
.header("organizationId",1)
.header("refreshToken",refresh_token)
.header("Authorization",Authorization)
.when()
.get("https://test.example.com/api/cases/recent")
.then()
.log().body()
.statusCode(200);
}
})
this sample doesnt work it returns 400 knowing that im using TestNG not JUnit
public class PermissionTests {
private static RequestSpecification requestSpec;
#BeforeClass
public static void AuthSetup() {
Response body =
given()
.params("username", "user#example.com","password","pass!","grant_type","password").post("https://test.example.com/Token")
.then()
.log().body()
.statusCode(200)
.extract().response();
String access_token = body.path("access_token").toString();
String token_type = body.path("token_type").toString();
String refresh_token = body.path("refresh_token").toString();
String Authorization = "bearer " + access_token;
HashMap<String, String> defaultHeader = new HashMap<>();
defaultHeader.put("officeId","1");
defaultHeader.put("organizationId","1");
defaultHeader.put("refresh_token",refresh_token);
defaultHeader.put("Authorization", Authorization);
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.addHeader("officeId", "1");
builder.addHeader("organizationId", "1");
builder.addHeader("refresh_token", refresh_token);
builder.addHeader("Authorization", Authorization);
requestSpec = builder.build();
// specification = new RequestSpecBuilder()
//// .addHeaders(defaultHeader)
// .addHeader("officeId","1")
// .addHeader("organizationId","1")
// .addHeader("refresh_token",refresh_token)
// .addHeader("Authorization",Authorization)
// .build();
}
#Test
public void addNewGraph(){
given()
.spec(requestSpec)
.log().all()
.when()
.get("https://test.example.com/api/cases/recent")
.then()
.log().body()
.statusCode(200);
}
})
here is the log from last method
{
"access_token": "eyJhbGciOiJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNobWFjLXNoYTI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjE2MSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJha2FtZWxAdHJhY2tlcnByb2R1Y3RzLmNvbSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYWNjZXNzY29udHJvbHNlcnZpY2UvMjAxMC8wNy9jbGFpbXMvaWRlbnRpdHlwcm92aWRlciI6IkFTUC5ORVQgSWRlbnRpdHkiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6ImJmODQ1MTEwLTk0ZDEtNGE0Yi05YzkxLThlNWQ1NDI2YTYxMyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2FkbWluIjoiVHJ1ZSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6IjE2MSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6ImFrYW1lbEB0cmFja2VycHJvZHVjdHMuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvb3JnYW5pemF0aW9uSWQiOiIxIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvb2ZmaWNlSWQiOiIxIiwicmVxdWlyZU1mYSI6IkZhbHNlIiwibmJmIjoxNTg4MTQ3MzMwLCJleHAiOjE1ODgxOTA1MzAsImlzcyI6Imh0dHBzOi8vdHJhY2tlcnByb2R1Y3RzLmNvbSIsImF1ZCI6ImM3MzJhY2U4MzRjZDQ4NTE5MGEzZTNhMjM2YTZhYzFkIn0.6pbDhYmyAXX9z46By4HxrCg_4HKRCSGq42FdhFoyA6s",
"token_type": "bearer",
"expires_in": 43199,
"refresh_token": "d64dde50sd4be16ef209dcc5ss",
"userName": "user#example.com",
"userId": "sds",
"deviceId": "eesdsde20d93e",
"maxStringFieldLength": "10000",
"opfs": "null",
".issued": "Wed, 29 Apr 2020 08:02:10 GMT",
".expires": "Wed, 29 Apr 2020 20:02:10 GMT"
}
Request method: GET
Request URI: https://example.test.com/api/cases/recent
Proxy: <none>
Request params: <none>
Query params: <none>
Form params: <none>
Path params: <none>
Headers: officeId=1
organizationId=1
refresh_token=d64dde50sd4be16ef209dcc5ss
Authorization=bearer eyA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjE2MSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzsdvY2xhaW1zL25hbWUiOiJha2FtZWxAdHJhY2tlcnByb2R1Y3RzLmNvbSIsImh0dHA6Ly9zY2hlbWFzLm1pY3Jvc29mdC5jb20vYWNjZXNzY29udHJvbHNlcnZpY2UvMjAxMC8wNy9jbGFpbXMvaWRlbnRpdHlwcm92aWRlciI6IkFTUC5ORVQgSWRlbnRpdHkiLCJBc3BOZXQuSWRlbnRpdHkuU2VjdXJpdHlTdGFtcCI6ImJmODQ1MTEwLTk0ZDEtNGE0Yi05YzkxLThlNWQ1NDI2YTYxMyIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2FkbWluIjoiVHJ1ZSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL3NpZCI6IjE2MSIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL2VtYWlsYWRkcmVzcyI6ImFrYW1lbEB0cmFja2VycHJvZHVjdHMuY29tIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvb3JnYW5pemF0aW9uSWQiOiIxIiwiaHR0cDovL3NjaGVtYXMueG1sc29hcC5vcmcvd3MvMjAwNS8wNS9pZGVudGl0eS9jbGFpbXMvb2ZmaWNlSWQiOiIxIiwicmVxdWlyZU1mYSI6IkZhbHNlIiwibmJmIjoxNTg4MTQ3MzMwLCJleHAiOjE1ODgxOTA1MzAsImlzcyI6Imh0dHBzOi8vdHJhY2tlcnByb2RdZCI6ImM3MzJhY2U4MzRjZDQ4NTE5MGEzZTNhMjM2YTZhYzFkIn0.6pbDhYmyAXX9z46By4HxrCg_4HKRCSGq42FdhFoyA6s
accept=application/json, text/plain, */*
Cookies: <none>
Multiparts: <none>
Body: <none>
{
"message": "GENERAL.ERROR",
"errorId": "637237441331863542"
}
original request header from the browser
You can use the Specification Re Use of Rest Assured, Particularly the RequestSpecBuilder() since you need to re-use request data in different tests
public class PermissionTests {
private static RequestSpecification requestSpec;
#BeforeClass
public static void AuthSetup() {
Response body = given().log().all()
.params("username", "user#example.com", "password", "pass!", "grant_type", "password")
.post("https://test.example.com/Token").then().log().body().statusCode(200).extract().response();
String access_token = body.path("access_token").toString();
String token_type = body.path("token_type").toString();
String refresh_token = body.path("refresh_token").toString();
String Authorization = "bearer " + access_token;
RequestSpecBuilder builder = new RequestSpecBuilder();
builder.addHeader("officeId", "1");
builder.addHeader("organizationId", "1");
builder.addHeader("refresh_token", refresh_token);
builder.addHeader("Authorization", Authorization);
requestSpec = builder.build();
}
#Test
public void addNewGraph() {
given().spec(requestSpec).log().all().when().get("https://test.example.com/api/cases/recent").then().log()
.body().statusCode(200);
}
}
You could use RestAssured.requestSpecification to set a default request specification that will be sent with each request, e.g.
RestAssured.requestSpecification = new RequestSpecBuilder()
.build().header("Authorization", "Bearer " + token);
So i have below scenario to implement using Spring boot rest template to consume a REST-API (involves token authentication mechanism). To perform test i've created simple mock REST API in spring boot. Here's the process,
From my API consumer app,
sends a request using rest-template to consume a protected API, this API requires Authorization: Bearer <token> header to be present in request.
if something is wrong with this token (missing header, invalid token), protected API returns HTTP-Unauthorized (401).
when this happens, consumer API should send another request to another protected API that returns a valid access token, this protected API requires Authorization: Basic <token> header to be present. New access token will be stored in a static field and it will be used in all other requests to authenticate.
This can be achieved by simply catching 401-HttpClientErrorException in RestTemplate consumer methods (postForObject), but the idea was to decouple it from REST-API consumer classes. To achieve it, i tried to use ClientHttpRequestInterceptor
Here's the code, that i tried so far.
Interceptor class
public class AuthRequestInterceptor implements ClientHttpRequestInterceptor {
private static final Logger LOGGER = LoggerFactory.getLogger(AuthRequestInterceptor.class);
private static final String BASIC_AUTH_HEADER_PREFIX = "Basic ";
private static final String BEARER_AUTH_HEADER_PREFIX = "Bearer ";
//stores access token
private static String accessToken = null;
#Value("${app.mife.apiKey}")
private String apiKey;
#Autowired
private GenericResourceIntegration resourceIntegration; // contains methods of rest template
#Override
public ClientHttpResponse intercept(
HttpRequest request,
byte[] body,
ClientHttpRequestExecution execution
) throws IOException {
LOGGER.info("ReqOn|URI:[{}]{}, Headers|{}, Body|{}", request.getMethod(), request.getURI(), request.getHeaders(), new String(body));
request.getHeaders().add(ACCEPT, APPLICATION_JSON_VALUE);
request.getHeaders().add(CONTENT_TYPE, APPLICATION_JSON_VALUE);
try {
//URI is a token generate URI, request
if (isBasicUri(request)) {
request.getHeaders().remove(AUTHORIZATION);
//sets BASIC auth header
request.getHeaders().add(AUTHORIZATION, (BASIC_AUTH_HEADER_PREFIX + apiKey));
ClientHttpResponse res = execution.execute(request, body);
LOGGER.info("ClientResponse:[{}], status|{}", "BASIC", res.getStatusCode());
return res;
}
//BEARER URI, protected API access
ClientHttpResponse response = null;
request.getHeaders().add(AUTHORIZATION, BEARER_AUTH_HEADER_PREFIX + getAccessToken());
response = execution.execute(request, body);
LOGGER.info("ClientResponse:[{}], status|{}", "BEARER", response.getStatusCode());
if (unauthorized(response)) {
LOGGER.info("GetToken Res|{}", response.getStatusCode());
String newAccessToken = generateNewAccessCode();
request.getHeaders().remove(AUTHORIZATION);
request.getHeaders().add(AUTHORIZATION, (BEARER_AUTH_HEADER_PREFIX + newAccessToken));
LOGGER.info("NewToken|{}", newAccessToken);
return execution.execute(request, body);
}
if (isClientError(response) || isServerError(response)) {
LOGGER.error("Error[Client]|statusCode|{}, body|{}", response.getStatusCode(), CommonUtills.streamToString(response.getBody()));
throw new AccessException(response.getStatusText(),
ServiceMessage.error().code(90).payload(response.getRawStatusCode() + ":" + response.getStatusText()).build());
}
return response;
} catch (IOException exception) {
LOGGER.error("AccessError", exception);
throw new AccessException("Internal service call error",
ServiceMessage.error().code(90).payload("Internal service call error", exception.getMessage()).build()
);
} finally {
LOGGER.info("ReqCompletedOn|{}", request.getURI());
}
}
private String generateNewAccessCode() {
Optional<String> accessToken = resourceIntegration.getAccessToken();
setAccessToken(accessToken.get());
return getAccessToken();
}
private static void setAccessToken(String token) {
accessToken = token;
}
private static String getAccessToken() {
return accessToken;
}
private boolean isClientError(ClientHttpResponse response) throws IOException {
return (response.getRawStatusCode() / 100 == 4);
}
private boolean isServerError(ClientHttpResponse response) throws IOException {
return (response.getRawStatusCode() / 100 == 5);
}
private boolean unauthorized(ClientHttpResponse response) throws IOException {
return (response.getStatusCode().value() == HttpStatus.UNAUTHORIZED.value());
}
private boolean isBasicUri(HttpRequest request) {
return Objects.equals(request.getURI().getRawPath(), "/apicall/token");
}
private boolean isMifeRequest(HttpRequest request) {
return request.getURI().toString().startsWith("https://api.examplexx.com/");
}
}
Token generate method- In resourceIntegration
public Optional<String> getAccessToken() {
ResponseEntity<AccessTokenResponse> res = getRestTemplate().exchange(
getAccessTokenGenUrl(),
HttpMethod.POST,
null,
AccessTokenResponse.class
);
if (res.hasBody()) {
LOGGER.info(res.getBody().toString());
return Optional.of(res.getBody().getAccess_token());
} else {
return Optional.empty();
}
}
Another sample protected API call method
public Optional<String> getMobileNumberState(String msisdn) {
try {
String jsonString = getRestTemplate().getForObject(
getQueryMobileSimImeiDetailsUrl(),
String.class,
msisdn
);
ObjectNode node = new ObjectMapper().readValue(jsonString, ObjectNode.class);
if (node.has("PRE_POST")) {
return Optional.of(node.get("PRE_POST").asText());
}
LOGGER.debug(jsonString);
} catch (IOException ex) {
java.util.logging.Logger.getLogger(RestApiConsumerService.class.getName()).log(Level.SEVERE, null, ex);
}
return Optional.empty();
}
Problem
Here's the log of mock API,
//first time no Bearer token, this returns 401 for API /simulate/unauthorized
accept:text/plain, application/json, application/*+json, */*
authorization:Bearer null
/simulate/unauthorized
//then it sends Basic request to get a token, this is the log
accept:application/json, application/*+json
authorization:Basic M3ZLYmZQbE1ERGhJZWRHVFNiTEd2Vlh3RThnYTp4NjJIa0QzakZUcmFkRkVOSEhpWHNkTFhsZllh
Generated Token:: 57f21374-1188-4c59-b5a7-370eac0a0aed
/apicall/token
//finally consumer API sends the previous request to access protected API and it contains newly generated token in bearer header
accept:text/plain, application/json, application/*+json, */*
authorization:Bearer 57f21374-1188-4c59-b5a7-370eac0a0aed
/simulate/unauthorized
The problem is even-though mock API log had the correct flow, consumer API does not get any response for third call, here's the log of it (unnecessary logs are omitted).
RequestInterceptor.intercept() - ReqOn|URI:[GET]http://localhost:8080/simulate/unauthorized?x=GlobGlob, Headers|{Accept=[text/plain, application/json, application/*+json, */*], Content-Length=[0]}, Body|
RequestInterceptor.intercept() - ClientResponse:[BEARER], status|401 UNAUTHORIZED
RequestInterceptor.intercept() - GetToken Res|401 UNAUTHORIZED
RequestInterceptor.intercept() - ReqOn|URI:[POST]http://localhost:8080/apicall/token?grant_type=client_credentials, Headers|{Accept=[application/json, application/*+json], Content-Length=[0]}, Body|
RequestInterceptor.intercept() - ClientResponse:[BASIC], status|200 OK
RequestInterceptor.intercept() - ReqCompletedOn|http://localhost:8080/apicall/token?grant_type=client_credentials
RestApiConsumerService.getAccessToken() - |access_token2163b0d4-8d00-4eba-92d0-7e0bb609b982,scopeam_application_scope default,token_typeBearer,expires_in34234|
RequestInterceptor.intercept() - NewToken|2163b0d4-8d00-4eba-92d0-7e0bb609b982
RequestInterceptor.intercept() - ReqCompletedOn|http://localhost:8080/simulate/unauthorized?x=GlobGlob
http://localhost:8080/simulate/unauthorized third time does not return any response, but mock API log says it hit the request. What did i do wrong ?, is it possible to achieve this task using this techniques ? or is there any other alternative way to do this ? any help is highly appreciated.
I have tried this:
Add an interceptor ClientHttpRequestInterceptor
import java.io.IOException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpRequest;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.util.StringUtils;
public class RequestResponseHandlerInterceptor implements ClientHttpRequestInterceptor {
#Autowired
private TokenService tokenService;
#Autowired
private RedisTemplate<String, String> redisTemplate;
private static final String AUTHORIZATION = "Authorization";
/**
* This method will intercept every request and response and based on response status code if its 401 then will retry
* once
*/
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
ClientHttpResponse response = execution.execute(request, body);
if (HttpStatus.UNAUTHORIZED == response.getStatusCode()) {
String accessToken = tokenService.getAccessToken();
if (!StringUtils.isEmpty(accessToken)) {
request.getHeaders().remove(AUTHORIZATION);
request.getHeaders().add(AUTHORIZATION, accessToken);
//retry
response = execution.execute(request, body);
}
}
return response;
}
}
Apart from this you need to override RestTemplate initialization as well.
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setInterceptors(Collections.singletonList(new RequestResponseHandlerInterceptor()));
return restTemplate;
}
I am using Postman currently to generate Bearer Token, which I am using in my automated tests. Now I would like to automate also the Bearer Token generation process too using REST Assured in Java. Please help me. Thanks.
Response response =
given()
.auth()
.oauth(
"n0pCrq5SPgraZ3CyY0Nd",
"xvvx-LVd5dszLi9OO_1qjbU4eUQ4dXwLrDZN7oioSITr_EXrgsyyOvPvZmv85Ew2",
"",
"",
"HMAC-SHA256")
.when()
.get(url)
.then()
.contentType(ContentType.JSON)
.extract()
.response();
This is working. Thanks #wilfred clement.
public static String getOauthToken(String consumerKey, String consumerSecret, String endpoint ) {
log.info("GET ACCESS TOKEN=" + endpoint);
URI uri = null;
try {
uri = new URI(endpoint);
} catch (URISyntaxException e) {
log.error("Not proper oauth url=" + endpoint);
throw new RuntimeException(e);
}
ValidatableResponse res = given()
.header("Content-Type", "application/json")
.auth().oauth(consumerKey,
consumerSecret,
"",
"")
.body("{\"grantType\": \"client_credentials\"}").when().post(uri).then();
int responseCode = res.extract().statusCode();
if (HttpURLConnection.HTTP_OK == responseCode) {
String token = res.extract().jsonPath().get("accessToken").toString();
log.info("Auth token=" + token);
return token;
} else {
String msg = "Access token retrieve failed. Http response code=" + responseCode;
log.error(msg);
throw new RuntimeException(msg);
}
}
I'm trying to implemente a request method, but I can't figure out how to sent the X-XSRF-TOKEN to my webservice.
In the webservice, the token is configured to be X-XSRF-TOKEN
<beans:bean id="csrfTokenRepository"
class="org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository">
<beans:property name="headerName" value="X-XSRF-TOKEN" />
</beans:bean>
I have it in my android App
public class WSConfig {
private static String urlBase = "http://192.168.25.226:8080/webapi/";
private static HttpHeaders httpHeaders;
private static RestTemplate restTemplate = new RestTemplate();
private static HttpEntity<String> httpEntity = new HttpEntity(getXSRF());
private static ResponseEntity<String> response;
public static HttpHeaders getXSRF() {
try {
HttpEntity<String> responseEntity = restTemplate.exchange(urlBase, HttpMethod.GET, null, String.class);
CookieManager cookieManager = new CookieManager();
List<String> cookieHeader = responseEntity.getHeaders().get("Set-Cookie");
httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
if (cookieHeader != null) {
for (String cookie : cookieHeader) {
String[] tokens = TextUtils.split(cookie, "=");
if (tokens[0].equals("XSRF-TOKEN")) {
String[] tokenValue = TextUtils.split(tokens[1],";");
httpHeaders.add("X-XSRF-TOKEN", tokenValue[0]);
}
if (tokens[0].equals("JSESSIONID")) {
String[] tokenValue = TextUtils.split(tokens[1],";");
httpHeaders.add("Cookie", "JSSESSIONID="+tokenValue[0]);
}
}
}
} finally {
return httpHeaders;
}
}
public static HttpEntity<String> makeRequest(String uri, HttpMethod method) {
try {
restTemplate.setErrorHandler(new DefaultResponseErrorHandler(){
protected boolean hasError(HttpStatus statusCode) {
return false;
}});
System.out.println(httpEntity.getHeaders());
response = restTemplate.exchange(urlBase + "registrar", HttpMethod.POST, null, String.class);
System.out.println(response.getHeaders());
System.out.println(response.getBody());
} catch (HttpStatusCodeException e) {
e.printStackTrace();
}
return null;
}
}
In my LogCat, I got these results from the System.outs
System.out.println(httpEntity.getHeaders());
{Accept=[application/json], Cookie=[JSSESSIONID=D0D537D4C38D2D69B01BF4F98B540763], X-XSRF-TOKEN=[8c21c671-bba4-4624-ada1-ff1e9e8f2e22]}
System.out.println(response.getHeaders());
{Server=[Apache-Coyote/1.1], Cache-Control=[no-cache, no-store, max-age=0, must-revalidate], Pragma=[no-cache], Expires=[0], X-XSS-Protection=[1; mode=block], X-Frame-Options=[DENY], X-Content-Type-Options=[nosniff], Set-Cookie=[JSESSIONID=7DBA84F6218BC9A8328A97587FC6293A; Path=/webapi/; HttpOnly], Content-Type=[text/html;charset=utf-8], Content-Language=[en], Content-Length=[1073], Date=[Thu, 20 Aug 2015 00:53:46 GMT], X-Android-Sent-Millis=[1440032027763], X-Android-Received-Millis=[1440032027805], X-Android-Response-Source=[NETWORK 403]}
And, the error
System.out.println(response.getBody());
HTTP Status 403 - Expected CSRF token not found. Has your session expired?
I can't figure what I have to do, I'm sending the header correctly, but can't make the post.
UPDATED
I'm think that this error have relation with JSESSIONID, instead of XSRF-TOKEN, someway after my first GET ( to get the XSRF ) the session is getting expired.
SOLUTION
As I said, this error is relationed with JSESSIONID.
When I split the JSESSIONID cookie it is losing something that need to make the cookie alive (the path, maybe?)
So, instead of add the cookie like this
httpHeaders.add("Cookie", "JSSESSIONID="+tokenValue[0]);
I've attached it this way
httpHeaders.add("Cookie", cookie);
Making it, I make sure that all content be attached to the new header.
The final method.
public static HttpHeaders getXSRF() {
try {
HttpEntity<String> responseEntity = restTemplate.exchange(urlBase, HttpMethod.GET, null, String.class);
CookieManager cookieManager = new CookieManager();
List<String> cookieHeader = responseEntity.getHeaders().get("Set-Cookie");
httpHeaders = new HttpHeaders();
httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
if (cookieHeader != null) {
for (String cookie : cookieHeader) {
String[] tokens = TextUtils.split(cookie, "=");
if (tokens[0].equals("XSRF-TOKEN")) {
String[] tokenValue = TextUtils.split(tokens[1],";");
httpHeaders.add("X-XSRF-TOKEN", tokenValue[0]);
}
if (tokens[0].equals("JSESSIONID"))
httpHeaders.add("Cookie", cookie);
}
}
} finally {
return httpHeaders;
}
}