Dealing with Java CookieManager "Invalid cookie" errors - java

I have defined a CookieStore as follows:
CookieManager cookieManager = new CookieManager();
CookieHandler.setDefault(cookieManager );
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
Whenever I complete a request using HttpURLConnection:
URL url = new URL(MY_URL);
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
I get this in my output:
java.net.CookieManager put SEVERE: Invalid cookie for https://...: ; HttpOnly
How should I deal with this message?

You need to first identify what kind of exception is being thrown. From looking at the documentation for CookieManger: http://www.docjar.com/html/api/java/net/CookieManager.java.html
283 try {
284 cookies = HttpCookie.parse(headerValue);
285 } catch (IllegalArgumentException e) {
286 // Bogus header, make an empty list and log the error
287 cookies = java.util.Collections.EMPTY_LIST;
288 if (logger.isLoggable(PlatformLogger.SEVERE)) {
289 logger.severe("Invalid cookie for " + uri + ": " + headerValue);
290 }
291 }
It seems that the issue is that your headers for your request is incorrect. Might want to look into that and here is a link of example code.
http://www.programcreek.com/java-api-examples/index.php?api=java.net.CookieManager
Also you probably want to use the chrome debugger to see the actual request being sent out and usually it will give you more information on why the request failed. The request could be incorrect, the url you are trying to send it to could be invalid, the service that your sending the request to might expect certain parameters.
From the code, it seems to look for the headers in the response. However, the response itself either contains no headers or there is something wrong with it and as a result HttpCookie.parse will throw a error.
If you look at HttpCookies.parse it throws an exception if:
Throws:
IllegalArgumentException - if header string violates the cookie specification's syntax, or the cookie name contains llegal characters, or the cookie name is one of the tokens reserved for use by the cookie protocol
NullPointerException - if the header string is null
So you need to look at the response and see if the data they put in the header is correct.

Related

java HttpURLConnection.addRequestProperty "Authorization" doesn't work

I'm building a java 8 console program to get data from a remote web service.
That service needs an authorization token in the form:
"Authorization: Bearer <my token>".
My connection code:
final URL url = new URL("https://<service url>");
final HttpURLConnection cnn = (HttpURLConnection) url.openConnection();
cnn.setRequestMethod("GET");
cnn.addRequestProperty("Authorization", "Bearer "+token);
Map<String, List<String>> props = cnn.getRequestProperties();
int size = props.size(); // size is always 0
cnn.connect();
int status = cnn.getResponseCode(); // status is always 404
I found "Authorization" is a disallowed request property so I run this code with JVM property
-Dsun.net.http.allowRestrictedHeaders=true.
Running my code I can find cnn.getRequestProperties() is always empty as my property was discarded and not used and, maybe a consequence of that, connection status is 404 (Forbidden).
I cannot ask to service-supplier to change the way everybody can authorize himself to its service.
Anybody know the way to get "Authorization" property accepted ?
Thanks,
Marco

HttpsURLConnection authenticating twice?

I establish a secure http connection and attempt to get the InputStream from it afterwards. The connection occurs, and I am able to get the data, but I am actually sending two authorization requests to the server?? Here is my code that is getting the connection and getting the input stream established:
someConnection = (HttpsURLConnection) url.openConnection();
String userPass = username + ":" + password;
String basicAuth = "Basic" + new String(new Base64().encode(userPass.getBytes()));
someConnection.setRequestProperty("Authorization", basicAuth);
if (header != null) someConnection.setRequestProperty(header, headerValue);
InputStream is = someConnection.getInputStream();
There is no traffic until the .getInputStream() method is called. Then I see two requests for authorization:
Any ideas why it is doing that? the first request appears to be failing for some reason.
The value of your header Authorization doesn't match with the expected format, it should be "Basic " followed by ${username}:${password} encoded with RFC2045-MIME variant of Base 64 (more details about Basic access authentication).
Here you forgot to add the trailing space after Basic such that authentication is never done properly which leads to this unexpected behavior.
There should be space between "Basic" and the base64 encoded data.
Without this the Authorization header is wrong. I would guess that you receive 401 on the first request and send the next with other credentials possibly obtained from different source (JAAS?).

OAuth2 requesting token returns 401

I'm trying to authenticate to a site that uses OAuth2 and store the token in my session object. My web app initially checks to see if there's a token already there, and if there isn't it redirects the user to the login page on the external site, where the user logs in and gets redirected back to my app. So far, so good, this works. My app directs me to the external site (Mendeley), I log in there, and then it redirects me back to the url in my app that I expect it to.
When it redirects back to my app, I expect a code and a state parameter on the request, and I do see these, so I assume I'm on the right track (stop me if I'm wrong). So then, if I understand correctly, I'm supposed to post the code back to the Mendeley service to get my authorization token, and that's where it all blows up.
URL url = new URL("https://api-oauth2.mendeley.com/oauth/token");
HttpsURLConnection connection = (HttpsURLConnection) url
.openConnection();
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type",
"application/x-www-form-urlencoded");
String authString = getClientId() + ":" + "[MY CLIENT SECRET]";
System.out.println("auth string: " + authString);
byte[] authEncBytes = Base64.getUrlEncoder().encode(
authString.getBytes());
String authStringEnc = new String(authEncBytes);
System.out.println("Base64 encoded auth string: " + authStringEnc);
connection.addRequestProperty("Authorization", "Basic "
+ authStringEnc);
connection.setDoOutput(true);
OutputStream os = connection.getOutputStream();
OutputStreamWriter writer = new OutputStreamWriter(os);
writer.write("scope=all&grant_type=authorization_code");
writer.write("&client_id=");
writer.write(getClientId());
writer.write("&code=");
writer.write(code);
writer.write("&redirect_uri=");
writer.write(getMendeleyRedirectUrl(request));
writer.write("&client_secret=");
writer.write("[MY CLIENT SECRET]");
writer.flush();
writer.close();
int responseCode = connection.getResponseCode();
BufferedReader reader = new BufferedReader(new InputStreamReader(
connection.getInputStream()));
The response code I get is 401. On that last line where it tries to get the inputStream from the connection it throws an exception, and that makes sense to me sense it returned a 401 and doesn't have one.
Yes, the redirect_uri is encoded. (I don't think the initial redirect to the login would work otherwise.)
My Spidey Sense tells me I'm overlooking something that should be obvious to me, but I've tried everything I could think of. Any help would be greatly appreciated.
Edit: changed how auth header is added, now getting response code 400.
You should check if you are creating the correct basic auth header. It should be something like this:
String user = "your app id";
String password = "your app secret";
String authValue = user + ":" + password;
Base64.Encoder encoder = Base64.getEncoder();
Bytes[] btyes = authValue.getBytes(StandardCharsets.UTF_8);
String authValueEncoded = encoder.encodeToString(bytes);
connection.addRequestProperty("Authorization",
"Basic "+authValueEncoded);
This values for user and password are specific for Mendeley. See step 4 of http://dev.mendeley.com/reference/topics/authorization_auth_code.html
Regarding the error 400, you might want to check the grant_type, code or redirect_uri. Remember that the code can only be used once.
from the docs:
Errors due to incorrect or missing values for grant_type, code and
redirect_uri result in a HTTP bad request response with a status of
400 Bad Request and a JSON format error code and message:
HTTP/1.1 400 Bad Request Content-Type: application/json
Content-Length: 82
{"error":"invalid_grant","error_description":"Invalid access code"}
Missing values generate a response with an invalid_request error code.
Invalid values (including previously used codes) generate a response
with an invalid_grant error code. Specifying a value other than
authorization_code (or refresh_token) generate a response with an
unsupported_grant_type error code.
So you might wan to look inside the response body to see what's wrong.

How to get access token using gmail api

I got the authorization code following this document. But when I tried to get access token, I always got errors. Can anyone help me ?
public String AccessToken()
{
String accessToken = "";
StringBuilder strBuild = new StringBuilder();
String authURL = "https://accounts.google.com/o/oauth2/token?";
String code = "4/SVisuz_x*********************";
String client_id = "******************e.apps.googleusercontent.com";
String client_secret = "*******************";
String redirect_uri = "urn:ietf:wg:oauth:2.0:oob";
String grant_type="authorization_code";
strBuild.append("code=").append(code)
.append("&client_id=").append(client_id)
.append("&client_secret=").append(client_secret)
.append("&redirect_uri=").append(redirect_uri)
.append("&grant_type=").append(grant_type);
System.out.println(strBuild.toString());
try{
URL obj = new URL(authURL);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setDoOutput(true);
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.setRequestProperty("Host", "www.googleapis.com");
//BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(con.getOutputStream()));
//bw.write(strBuild.toString());
//bw.close();
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(strBuild.toString());
wr.flush();
wr.close();
//OutputStreamWriter out = new OutputStreamWriter(con.getOutputStream());
System.out.println(con.getResponseCode());
System.out.println(con.getResponseMessage());
} catch (Exception e)
{
System.out.println("Error.");
}
return "";
}
when I ran this code, the output is:
400
Bad Request
How to get access token using gmail api?
Ans: As per your following tutorial, you are using OAuth 2.0. So there is a basic pattern for accessing a Google API using OAuth 2.0. It follows 4 steps:
Obtain OAuth 2.0 credentials from the Google Developers Console.
Obtain an access token from the Google Authorization Server.
Send the access token to an API.
Refresh the access token, if necessary.
For details, you can follow the tutorial - Using OAuth 2.0 to Access Google APIs
You have to visit the Google Developers Console to obtain OAuth 2.0 credentials such as a client ID and client secret that are known to both Google and your application
Root Cause Analysis:
Issue-1:
After studying your code, some lacking are found. If your code runs smoothly, then the code always give an empty string. Because your AccessToken() method always return return "";
Issue-2:
catch (Exception e)
{
System.out.println("Error.");
}
Your try catch block is going exception block. Because, it seems that you have not completed your code properly. You have missed encoding as well as using JSONObject which prepares the access token. So it is giving output as
Error.
Solution:
I got that your code is similar with this tutorial
As your code needs more changes to solve your issue. So I offer you to use LinkedHashMap or ArrayList. Those will provide easier way to make solution. So I give you 2 sample code to make your life easier. You can choose any of them. You need to change refresh_token, client id, client secret and grant type as yours.
private String getAccessToken()
{
try
{
Map<String,Object> params = new LinkedHashMap<>();
params.put("grant_type","refresh_token");
params.put("client_id",[YOUR CLIENT ID]);
params.put("client_secret",[YOUR CLIENT SECRET]);
params.put("refresh_token",[YOUR REFRESH TOKEN]);
StringBuilder postData = new StringBuilder();
for(Map.Entry<String,Object> param : params.entrySet())
{
if(postData.length() != 0)
{
postData.append('&');
}
postData.append(URLEncoder.encode(param.getKey(),"UTF-8"));
postData.append('=');
postData.append(URLEncoder.encode(String.valueOf(param.getValue()),"UTF-8"));
}
byte[] postDataBytes = postData.toString().getBytes("UTF-8");
URL url = new URL("https://accounts.google.com/o/oauth2/token");
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setDoOutput(true);
con.setUseCaches(false);
con.setRequestMethod("POST");
con.getOutputStream().write(postDataBytes);
BufferedReader reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
StringBuffer buffer = new StringBuffer();
for (String line = reader.readLine(); line != null; line = reader.readLine())
{
buffer.append(line);
}
JSONObject json = new JSONObject(buffer.toString());
String accessToken = json.getString("access_token");
return accessToken;
}
catch (Exception ex)
{
ex.printStackTrace();
}
return null;
}
For accessing google play android developer api, you need to pass the
previous refresh token to get access token
private String getAccessToken(String refreshToken){
HttpClient client = new DefaultHttpClient();
HttpPost post = new HttpPost("https://accounts.google.com/o/oauth2/token");
try
{
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(4);
nameValuePairs.add(new BasicNameValuePair("grant_type", "refresh_token"));
nameValuePairs.add(new BasicNameValuePair("client_id", GOOGLE_CLIENT_ID));
nameValuePairs.add(new BasicNameValuePair("client_secret", GOOGLE_CLIENT_SECRET));
nameValuePairs.add(new BasicNameValuePair("refresh_token", refreshToken));
post.setEntity(new UrlEncodedFormEntity(nameValuePairs));
org.apache.http.HttpResponse response = client.execute(post);
BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
StringBuffer buffer = new StringBuffer();
for (String line = reader.readLine(); line != null; line = reader.readLine())
{
buffer.append(line);
}
JSONObject json = new JSONObject(buffer.toString());
String accessToken = json.getString("access_token");
return accessToken;
}
catch (IOException e) { e.printStackTrace(); }
return null;
}
Resource Link:
Unable to get the subscription information from Google Play Android Developer API
Using java.net.URLConnection to fire and handle HTTP requests
How to send HTTP request GET/POST in Java
Hope that, this samples and resource link will help you to solve your issue and get access of access token.
What is 400 bad request?
Ans: It indicates that the query was invalid. Parent ID was missing or the combination of dimensions or metrics requested was not valid.
Recommended Action: You need to make changes to the API query in order for it to work.
For HTTP/1.1 400 Bad Request error, you can go through my another
answer. It will help you to make sense about which host you
need to use and which conditions you need to apply.
Why token expires? What is the limit of token?
A token might stop working for one of these reasons:
The user has revoked access.
The token has not been used for six months.
The user changed passwords and the token contains Gmail, Calendar,
Contacts, or Hangouts scopes.
The user account has exceeded a certain number of token requests.
There is currently a limit of 25 refresh tokens per user account per client. If the limit is reached, creating a new token automatically invalidates the oldest token without warning. This limit does not apply to service accounts.
Which precautions should be followed?
Precautions - 1:
Some requests require an authentication step where the user logs in
with their Google account. After logging in, the user is asked whether
they are willing to grant the permissions that your application is
requesting. This process is called user consent.
If the user grants the permission, the Google Authorization Server
sends your application an access token (or an authorization code that
your application can use to obtain an access token). If the user does
not grant the permission, the server returns an error.
Precautions - 2:
If an access token is issued for the Google+ API, it does not grant
access to the Google Contacts API. You can, however, send that access
token to the Google+ API multiple times for similar operations.
Precautions - 3:
An access token typically has an expiration date of 1 hour, after
which you will get an error if you try to use it. Google Credential
takes care of automatically "refreshing" the token, which simply means
getting a new access token.
Save refresh tokens in secure long-term storage and continue to use
them as long as they remain valid. Limits apply to the number of
refresh tokens that are issued per client-user combination, and per
user across all clients, and these limits are different. If your
application requests enough refresh tokens to go over one of the
limits, older refresh tokens stop working.
You are not using the right endpoint. Try to change the authURL to https://www.googleapis.com/oauth2/v4/token
From the documentation:
To make this token request, send an HTTP POST request to the /oauth2/v4/token endpoint
The actual request might look like the following:
POST /oauth2/v4/token HTTP/1.1
Host: www.googleapis.com
Content-Type: application/x-www-form-urlencoded
code=4/v6xr77ewYqhvHSyW6UJ1w7jKwAzu&
client_id=8819981768.apps.googleusercontent.com&
client_secret=your_client_secret&
redirect_uri=https://oauth2-login-demo.appspot.com/code&
grant_type=authorization_code
Reference https://developers.google.com/identity/protocols/OAuth2InstalledApp#handlingtheresponse
For me your request is fine, I tried it using Curl, I also get a 'HTTP/1.1 400 Bad Request' with the reason why it failed 'invalid_grant' :
curl -X POST https://www.googleapis.com/oauth2/v4/token -d 'code=4/SVisuz_x*********************&client_id=*******************7vet.apps.googleusercontent.com&client_secret=***************&redirect_uri=https://oauth2-login-demo.appspot.com/code&grant_type=authorization_code'
I receive (HTTP/1.1 400 Bad Request) :
{
"error": "invalid_grant",
"error_description": "Code was already redeemed."
}
Now using HttpClient from Apache :
URL obj = new URL(authURL);
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(authURL);
post.addHeader("Content-Type", "application/x-www-form-urlencoded");
post.addHeader("Host", "www.googleapis.com");
post.setEntity(new StringEntity(strBuild.toString()));
HttpResponse resp = client.execute(post);
System.out.println(resp.getStatusLine());
System.out.println(EntityUtils.toString(resp.getEntity()));
I see in my console :
HTTP/1.1 400 Bad Request
{
"error": "invalid_grant",
"error_description": "Code was already redeemed."
}
Are you sure the code you are using is still valid ? Can you try with a new one ?
Firstly, you must look this page :
https://developers.google.com/gmail/api/auth/web-server#create_a_client_id_and_client_secret
The value you see in the query parameter code is a string you have to post to google in order to get the access token.
After the web server receives the authorization code, it may exchange the authorization code for an access token and a refresh token. This request is an HTTPS POST to the URL https://www.googleapis.com/oauth2/v3/token
POST /oauth2/v3/token HTTP/1.1
content-type: application/x-www-form-urlencoded
code=4/v4-CqVXkhiTkn9uapv6V0iqUmelHNnbLRr1EbErzkQw#&redirect_uri=&client_id=&scope=&client_secret=************&grant_type=authorization_code
https://developers.google.com/identity/protocols/OAuth2WebServer
I think I understand what's wrong:
as #newhouse said, you should POST to https://www.googleapis.com/oauth2/v4/token and not https://accounts.google.com/o/oauth2/token (#newhouse I gave you a +1 :) )
(https://www.googleapis.com/oauth2/v4/token is for getting the authorization_code and https://accounts.google.com/o/oauth2/token is for getting the code).
You can't use the same code more than once.
Everything else seems in order so, if you keep getting 400, you are probably trying to use the code you got more than one time (then you'll get 400 every time, again and again).
* You should also lose the con.setRequestProperty("Host", "www.googleapis.com");
Refer : https://developers.google.com/android-publisher/authorization
You already have authorization code that is called "refresh token". Please keep it in safe place. You can use "refresh token" to generate "access token".
To get "access token", please make a post request to following URL
https://accounts.google.com/o/oauth2/token
Parameters:
grant_type
client_id
client_secret
refresh_token
where "grant_type" should be "refresh_token"
We are using PHP to do same, here is PHP's code for your reference
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_URL => 'https://accounts.google.com/o/oauth2/token',
CURLOPT_USERAGENT => 'Pocket Experts Services',
CURLOPT_POST => 1,
CURLOPT_POSTFIELDS => array(
"grant_type" => "refresh_token",
"client_id" => $GOOGLE_CLIENT_ID,
"client_secret" => $GOOGLE_CLIENT_SECRET,
"refresh_token" => $GOOGLE_REFRESH_TOKEN,
)));
// Send the request & save response to $resp
$resp = curl_exec($curl);
Hope it will help you.
the low security methode was temporary and i couldn't use it in production but I found an article that made it easier using node here
with an example code and it works perfect

Java http call returning response code: 501

I am having an issue with this error:
**Server returned HTTP response code: 501 for URL: http://dev1:8080/data/xml/01423_01.xml**
See this code:
private static Map sendRequest(String hostName, String serviceName) throws Exception {
Map assets = null;
HttpURLConnection connection = null;
Authenticator.setDefault(new Authenticator());
URL serviceURL = new URL(hostName + "/" + serviceName);
connection = (HttpURLConnection)serviceURL.openConnection();
connection.setRequestMethod("GET");
ClientHttpRequest postRequest = new ClientHttpRequest(connection);
InputStream input = null;
/*
At line input = postRequest.post(); I get the following error
Server returned HTTP response code: 501 for URL: http://dev1:8080/data/xml/01423_01.xml
Yet if I enter that url in my browser it opens up fine.
Is this a common problem? Is there some type of content type I need to set?
*/
input = postRequest.post();
connection.disconnect();
return assets;
}
A 501 response means "not implemented", and is usually taken to mean that the server didn't understand the HTTP method that you used (e.g. get, post, etc).
I don't recognise ClientHttpRequest , but you have a line that says
connection.setRequestMethod("GET");
and then a line that says
input = postRequest.post();
I'm not sure what post() actually does, but does that mean send a POST request? If so, then that contradicts the GET specified in the first line.
Either way, the server is saying that it doesn't under the GET or the POST method, whichever one your code is actually sending. You need to find out what method the server does support for that URL, and use that.
Perhaps you should check your port settings:
new URL(hostName + "/" + serviceName);
Looks like the port number ":8080" is missing.
Some server expect additional information from the client in the request like a user agent or some form data. Even cookies could be expected by the application running on the server. You should also check the complete response and not only the response code.
I would recommend you to use a library like httpclient that is more convenient:
https://hc.apache.org/httpcomponents-client-ga/index.html
Here is simple usage example:
https://github.com/apache/httpcomponents-client/blob/master/httpclient5/src/test/java/org/apache/hc/client5/http/examples/ClientWithResponseHandler.java

Categories

Resources