2 legged OAuth using Scribe - java equivalent of php - java

I'm trying to use scribe to implement 2 legged OAuth in Java with reference to php code.
I believe I'm very close to cracking this. My current error is:
**OAuth - response.getBody: Problem: signature_invalid | Advice: > |
response.getCode(): 200**
I suspect that this has something to do with the form of the token or lack of consumer object while signing the request.
In php, the code is:
$consumer = new OAuthConsumer($consumer_key, $consumer_secret);
//post transaction to pesapal
$iframe_src = OAuthRequest::from_consumer_and_token($consumer, $token, "GET", $iframelink, $params);
$iframe_src->set_parameter("oauth_callback", $callback_url);
$iframe_src->set_parameter("pesapal_request_data", $post_xml);
**$iframe_src->sign_request($signature_method, $consumer, $token);**
From the last line, to sign the request, the consumer is also passed as a parameter.
My code is as follows:
OAuthService service = new ServiceBuilder()
.provider(something.class)
.signatureType(SignatureType.QueryString)
.apiKey(consumer_key)
.apiSecret(consumer_secret)
.callback(callback_url)
.build();
Token token = new Token("", "");
OAuthRequest request = new OAuthRequest(Verb.GET, iframelink);
request.addBodyParameter("pesapal_request_data", post_xml);
request.addOAuthParameter(OAuthConstants.SIGN_METHOD, signature_method);
service.signRequest(token, request);
Response response = request.send();
Can someone please show me where I may have gone wrong ?
I know that I'm close - very close ....

Try to change $iframelink = http://www.pesapal.com/api/PostPesapalDirectOrderV4 to $iframelink = https://www.pesapal.com/api/PostPesapalDirectOrderV4
Had the same issue when live changing from http to https sorted me out.

Related

Getting Invalid grant, malformed auth code while verifying token on server side

We are trying to use Google OAuth in our product. The flow would be to get Client get the auth from the users and send the token to server. On server side I need to verify the token is valid. For now, I am using OAuth2Sample provided by Google as client. when I verify the sent token on server side, I am getting the following exception:
com.google.api.client.auth.oauth2.TokenResponseException: 400 Bad Request
{
"error" : "invalid_grant",
"error_description" : "Malformed auth code."
}
at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
at com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeTokenRequest.execute(GoogleAuthorizationCodeTokenRequest.java:158)
Here is the code on the server side:
GoogleTokenResponse tokenResponse =
new GoogleAuthorizationCodeTokenRequest(
new NetHttpTransport(),
JacksonFactory.getDefaultInstance(),
"https://www.googleapis.com/oauth2/v4/token",
CLIENT_ID,
CLIENT_SECRET,
authToken, //Sent from the client
"") // specify an empty string if you do not have redirect URL
.execute();
Here is how I get the accesstoken on the client side:
private static final List<String> SCOPES = Arrays.asList(
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email");
//...
GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
httpTransport, JSON_FACTORY,
clientSecrets, //Client ID and Client Secret
SCOPES).setDataStoreFactory(
dataStoreFactory).build();
LocalServerReceiver lsr = new LocalServerReceiver();
Credential cr = new AuthorizationCodeInstalledApp(flow, lsr).authorize("user");
return cr.getAccessToken(); //send this to server for verification
The token is not corrupted on the way to server and it is:
ya29.Glx_BUjV_zIiDzq0oYMqoXkxz8VGzt8GCQuBiaaJ3FxN0qaLxbBXvnWXjNKZbpeu4jraoEqw6Mj9D7LpTx_8Ts_8TH0VGT5cbrooGcAOF0wKMc1DDEjm6p5m-MvtFA
If I try to access profile and email from the client side, it works fine. Same token does not work on the server side gets malformed token exception.
I am using Node.js googleapis client library, Here is my case:
The authorization code in the url hash fragment is encoded by encodeURIComponent api, so if you pass this code to request access token. It will throw an error:
{ "error": "invalid_grant", "error_description": "Malformed auth code." }
So I use decodeURIComponent to decode the authorization code.
decodeURIComponent('4%2F_QCXwy-PG5Ub_JTiL7ULaCVb6K-Jsv45c7TPqPsG2-sCPYMTseEtqHWcU_ynqWQJB3Vuw5Ad1etoWqNPBaGvGHY')
After decode, the authorization code is:
"4/_QCXwy-PG5Ub_JTiL7ULaCVb6K-Jsv45c7TPqPsG2-sCPYMTseEtqHWcU_ynqWQJB3Vuw5Ad1etoWqNPBaGvGHY"
In Java, maybe you can use URLDecoder.decode handle the code.
For anyone who might face this in the future. I faced this issue and decodeURIComponent did not work with me. The previous answers work with for different issue.
From the question itself, you can see that the token starts with ya29.
ya29.Glx_BUjV_zIiDzq0oYMqoXkxz8VGzt8GCQuBiaaJ3FxN0qaLxbBXvnWXjNKZbpeu4jraoEqw6Mj9D7LpTx_8Ts_8TH0VGT5cbrooGcAOF0wKMc1DDEjm6p5m-MvtFA
That indicates that the token is an online token. In case of the online login, you can see that the response looks like this
But that will not work with server side login. So, when using some Google client library, please note that there are two variables that you need to check:
access_type: offline
responseType: code
Once you configure Google client library with those fields, you can see that the response of the login will change to something like this
{
"code": "4/0AX4XfWgaJJc3bsUYZugm5-Y5lPu3muSfUqCrpY5KZoGEGAHuw0jrkg_xkD_RX-6bNUq-vA"
}
Then you can send that code to the backend and it will work.
Thanks to slideshowp2's answer, and Subhadeep Banerjee's comment.
I am using server-side web app with HTTP/REST ,also facing the same problem
and yes, the reason is that authorization code return from URL is encoded.
After decode, everything work fine to get access token.
p.s. here is some info about encodedURL
since our Content-Type: application/x-www-form-urlencoded
we do get this error when for some reason the call back url of the auth is called a second time. The first time it works but the second time it errors out.
Not sure how the users are able to do that. Maybe by pressing the back button in the browser.
The error is indeed this:
{"error": "invalid_grant","error_description": "Malformed auth code."}
Encoding the code in the call back URL was not the problem.

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

JSAPI LinkedIn OAuth10a request with Java (based on a PHP version)

I am trying to access data on LinkedIn profile using its API.
At first I followed the LinkedIn JSPAI Doc on https://developer-programs.linkedin.com/documents/exchange-jsapi-tokens-rest-api-oauth-tokens in PHP. So I started translating code from PHP to Java using Scribe.
Then, I have found this example on Github which looks like what I did : https://github.com/fernandezpablo85/TokenExchangeSample/blob/master/src/main/java/com/linkedin/oauth/ExchangeService.java
and I got this string in the end after authorization and cookie exchange :
oauth_token=75--4ff2c506-37e2-4b77-927f-c28c5f511762&oauth_token_secret=c73110b2-0dce-43bd-8537-8c8fb4fd5290&oauth_expires_in=5183975&oauth_authorization_expires_in=5183975
In PHP, the listed code help to get user data as described in the $url :
// go to town, fetch the user's profile
$url = 'http://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline)';
$oauth->fetch($url, array(), OAUTH_HTTP_METHOD_GET, array('x-li-format' => 'json')); // JSON!
$profile = json_decode($oauth->getLastResponse());
print "$profile->firstName $profile->lastName is $profile->headline.";
So the code works and returns data. In the Java version, I am wondering how to use the returned tokens.
I tried used https://api.linkedin.com/v1/people/~:(id,first-name,last-name,headline)?oauth_token=75--7ff2c506-57e2-4b77-927f-c28c5f551762&oauth_token_secret=c73330b2-0dce-48bd-8537-8c8fb4fd5290&oauth_expires_in=5183975&oauth_authorization_expires_in=5183975
But it does not work.
I found the solution : After getting the Oauth10a keys, you should use them in a new Request by specifying the json format.
OAuthService service = new ServiceBuilder()
.apiKey(APIKEY)
.apiSecret(SECRETKEY)
.provider(LinkedInApi.class)
.build();
OAuthRequest oAuthRequestData = new OAuthRequest(Verb.GET, DATAENDPOINT);
oAuthRequestData.addHeader("x-li-format", "json");
Token accessToken = new Token(oauth_token, oauth_token_secret);
service.signRequest(accessToken, oAuthRequestData);
Response oAuthResponse = oAuthRequestData.send();
System.outt.println(oAuthResponse.getBody());

troubleshooting scribe for social api authentication

I just started looking at scribe for authentication with social networks such as twitter/facebook etc. I am using the example for twitter as reference. However, I don't seem to get oauth_verifier from twitter for some reason (even though the callback is registered through the service builder - I am using localhost in the callback as it worked with another social api). Any helpful suggestions would be quite welcome. thanks in advance.
OAuthService service = new ServiceBuilder()
.provider(TwitterApi.class)
.apiKey(consumerKey)
.apiSecret(consumerSecret)
.callback("http://localhost/oauth/twitter")
.build();
//get the token
Token requestToken = service.getRequestToken();
String authUrl = service.getAuthorizationUrl(requestToken);
Logger.info("authurl::" + authUrl); // not getting the oauth_verifier
Debug output from scribe (I changed the token info):
setting oauth_callback to http://localhost/oauth/twitter
generating signature...
base string is: POST&http%3A%2F%2Fapi.twitter.com%2Foauth%2Frequest_token&oauth_callback%3Dhttp%253A%252F%252Flocalhost%252Foauth%252Ftwitter%26oauth_consumer_key%3DAAACCCV6ASDFGHJCgYBCD%26oauth_nonce%3D607134093%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1353965859%26oauth_version%3D1.0
signature is: +mSqKJIC1Q0pMEFs/gIJViF7kbg=
appended additional OAuth parameters: { oauth_callback -> http://localhost/oauth/twitter , oauth_signature -> +mSqKJIC1Q0pMEFs/gIJViF7kbg= , oauth_version -> 1.0 , oauth_nonce -> 607134093 , oauth_signature_method -> HMAC-SHA1 , oauth_consumer_key -> AAACCCV6ASDFGHJCgYBCD , oauth_timestamp -> 1353965859 }
using Http Header signature
sending request...
response status code: 200
response body: oauth_token=itJrpOP3KLeD7Ha6oy0IRr4HysFut5eAOpIlj8OmNE&oauth_token_secret=X8LmhAUpvIkfEd7t7P1lvwwobC3JJIhUabcEs0Rn5w&oauth_callback_confirmed=true
authurl::https://api.twitter.com/oauth/authorize?oauth_token=itJrpOP3KLeD7Ha6oy0IRr4HysFut5eAOpIlj8OmNE
obtaining access token from http://api.twitter.com/oauth/access_token
setting token to: Token[itJrpOP3KLeD7Ha6oy0IRr4HysFut5eAOpIlj8OmNE , X8LmhAUpvIkfEd7t7P1lvwwobC3JJIhUabcEs0Rn5w] and verifier to: org.scribe.model.Verifier#55ac8c3d
generating signature...
Update:
I am able to receive the oauth_verifier now. I will update this post once I am done testing.
pilot error mostly. I was able to get oauth working with twitter using scribe. After getting the service, the request Token from the service & then the authorizationUrl from the service (while passing in the request token), I was able to redirect to the authorization URL. Once there, I was able to authenticate myself against twitter using my twitter ID which redirected me to the callback URL specified when I created the service. Upon authentication, I received the oauth_verifier which I was able to use to create the verifier & then receive the access token from the service using the verifier and the request token. Then the oauth request was made & signed which resulted in the response from twitter with the user details. Worked. Hope it helps.

InvalidAuthenticityToken exception when sending a HTTP POST

We're creating a Java client to interface with a Ruby on Rails server for an inventory system project (for school).
The client that we're using is supposed to do HTTP GET's to request information and HTTP POST's to update or create new information (yes, we know about HTTP PUT...).
Unfortunately, we are running into a InvalidAuthenticityToken error when we try to do a HTTP post. We authenticate through HTTP basic authentication and our controllers look like this:
class UsersController < ApplicationController
before_filter :authenticate
def show
#user = User.find(params[:id])
##user = User.all
render :xml => #user.to_xml
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
render :text => 'Success!'
else
render :text => 'No success!'
end
end
private
def authenticate
logger.info("Entering Authen..")
authenticate_or_request_with_http_basic do |username, password|
logger.info("Time: #{Time.now}, username: #{username}, password: #{password}")
User.authenticate(username, password)
end
end
end
The show action works perfectly and the authentication action is triggered with a valid username and password (e.g., working fine...). Our problem happens when we attempt to POST an update to the update action. When we attempt it, we get an InvalidAuthenticityToken exception. The following is from our development log:
#Successful GET
Processing UsersController#show (for 10.18.2.84 at 2010-11-24 19:02:42) [GET]
Parameters: {"id"=>"2"}
Entering Authen..
Filter chain halted as [:authenticate] rendered_or_redirected.
Completed in 3ms (View: 2, DB: 0) | 401 Unauthorized [http://10.18.2.84/users/show/2]
Processing UsersController#show (for 10.18.2.84 at 2010-11-24 19:02:43) [GET]
Parameters: {"id"=>"2"}
Entering Authen..
Time: Wed Nov 24 19:02:43 -0600 2010, username: admin, password: pass
[4;36;1mUser Load (1.0ms)[0m [0;1mSELECT * FROM "users" WHERE (user_name = 'admin' AND password = 'pass') LIMIT 1[0m
[4;35;1mUser Load (0.0ms)[0m [0mSELECT * FROM "users" WHERE ("users"."id" = 2) [0m
Completed in 18ms (View: 2, DB: 1) | 200 OK [http://10.18.2.84/users/show/2]
#Unsuccessful POST
Processing UsersController#update (for 10.18.2.84 at 2010-11-24 19:03:06) [POST]
Parameters: {"id"=>"2", "first-name"=>"Macky"}
ActionController::InvalidAuthenticityToken
(ActionController::InvalidAuthenticityToken):
Our concern is that it isn't even attempting to authenticate the basic authentication -- it's skipping the filter and the entire controller as far as we can tell. Our code is pretty vanilla in the client (this is edited down for comprehensibility from what it actually is):
DefaultHttpClient httpClient = new DefaultHttpClient();
myCredentials = new UsernamePasswordCredentials( username, password );
//Set Provider
provider = new BasicCredentialsProvider();
provider.setCredentials(scope, myCredentials);
//Set Credentials
httpClient.setCredentialsProvider( provider );
ArrayList<NameValuePair> formparams = new ArrayList<NameValuePair>();
formparams.add(new BasicNameValuePair("first-name", "Macky"));
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(formparams, "UTF-8");
//Set the post
post = new HttpPost( url );
post.setEntity(formEntity);
response = httpClient.execute( post );
So, is it with Rails that we're doing something wrong or is it with the Java Jakarta client? Any help is greatly appreciated.
Are you including the CSRF token in your POST data?
See this link: http://api.rubyonrails.org/classes/ActionController/RequestForgeryProtection/ClassMethods.html
By default protect_from_forgery is enabled which makes Rails require an authenticity token for any non-GET requests. Rails will automatically include the authenticity token in forms created with the form helpers but I'm guessing since you're building your own form to post, you're not including the token.

Categories

Resources