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.
Related
Trying to read data of specific envelopes from DocuSign using completely backend java process ... and after some trial and error I've obtained AccessToken with JWT grant but still getting authorization error when asking for actual data :(
Defined new integration key 9xxx7e
User Application: Authorization Code Grant
No secrets added
Service Integration - uploaded public RSA key
Added one Redirect URI (regardless I don't need any)
Manual confirmation of corresponding link : https://account-d.docusign.com/oauth/auth?response_type=code&scope=signature%20impersonation&client_id=9xxx7e&state=123&redirect_uri=https://my.redirect.net/DocuSign ... assuming it is just one-time action
Successfully requested Access Token using java code (using com.docusign:docusign-esign-java:3.10.1)
ApiClient = new ApiClient(ApiClient.DEMO_REST_BASEPATH);
OAuthToken token = apiClient.requestJWTApplicationToken(integrationKeyJwt, scopes, privateKeyFileContent, 3600);
Trying to get envelope data using simple HttpGet
HttpGet request = new HttpGet("https://demo.docusign.net/restapi/v2.1/accounts/6xxx1e/envelopes");
request.addHeader("Content-Type", "application/json");
request.addHeader("Authorization", "Bearer " + token.getAccessToken());
but still got 401 response with content:
{"errorCode":"AUTHORIZATION_INVALID_TOKEN","message":"The access token provided is expired, revoked or malformed. Authentication for System Application failed."}
Please any idea what is wrong? How to obtain correct Access Token?
P.S.: I also tried to get Authorization Code Grant without JWT or implicit grant but no luck without browser tough :(
I would recommend that you print the accessToken you're creating in a file and use in Postman. This will at least help you narrow it down to either the Token generation step or sending the request portion.
Let us know what you find.
Problem was/is with use of apiClient.requestJWTApplicationToken but apiClient.requestJWTUserToken is the way to go
I am integrating my web app with AppDirect,
for this I created a java rs API using jersey.
When I subscribe to an event, I get a map containing the oauth values (key and secret) to sign my request and an event url to which I issue a sign fetch to.
I am getting those values (oauth and eventurl) as expected.
Now when I try to issue a signed fetch using the library signpost, I use the following code:
OAuthConsumer consumer = new DefaultOAuthConsumer(consumer_key, secret);
// create an HTTP request to a protected resource
URL url = new URL(eventUrl);
HttpURLConnection request = (HttpURLConnection) url.openConnection();
// sign the request
consumer.sign(request);
// send the request
request.connect();
I get this error message:
getResponseMessage: Unauthorized
getresponsecode: 401
I also tried with the following test values:
url = "https://www.appdirect.com/api/integration/v1/events/dummyOrder";
dummyKey = "Dummy";
dummySecret = "secret";
But I got the same result.
Please how can I fix it?
I also tried and adding this:
request.setRequestMethod("GET");
request.setRequestProperty("Authorization", "OAuth");
request.setRequestProperty("Host", "...");
request.setRequestProperty("Content-Type", "application/xml");
request.setRequestProperty("oauth_nonce", oauth_nonce);
request.setRequestProperty("oauth_signature", oauth_signature);
request.setRequestProperty("oauth_signature_method", oauth_signature_method);
request.setRequestProperty("oauth_timestamp", oauth_timestamp);
request.setRequestProperty("oauth_version", oauth_version);
also tried with key:secret in the Authorization property
Here is a behavior of this service when testing via Postman Chrome extension.
If you are using OAuth provider, so you need to get valid api-key for AppDirect and secret.
BTW second screenshot shows you don't need to send an OAuth token to appdirect to https://www.appdirect.com/api/integration/v1/events/dummyOrder, because it authorizes any url.
So, according to your notes, you have to add proper(secret and key) and then AppDirect OAuth server will return you a valid token which you will use when addressing AppDirect's repositories. Or you may send key-secret with each request.
I am trying to retrieve user photo using outlook REST API(https://msdn.microsoft.com/en-us/office/office365/api/photo-rest-operations#UserphotooperationsGetphoto)
I got the access token following (https://msdn.microsoft.com/en-us/library/azure/dn645543.aspx)
but getting this error : any help?
HTTP/1.1 401 Unauthorized [Content-Length: 0, Server: Microsoft-IIS/8.0, request-id: 6925fcab-9021-4059-af4b-4cbf130faea7, X-CalculatedBETarget: CY1PR0401MB1388.namprd04.prod.outlook.com, X-BackEndHttpStatus: 401, Set-Cookie: exchangecookie=87cb2447eae9401c80a96c497dff06a9; expires=Sat, 22-Apr-2017 07:56:53 GMT; path=/; HttpOnly, x-ms-diagnostics: 2000001;reason="The access token is acquired using an authentication method that is too weak to allow access for this application. Presented auth strength was 1, required is 2.";error_category="invalid_token",
code looks something like this:
HttpClient httpclient = HttpClients.createDefault();
final String bearerToken = getBearerToken();
HttpGet request = new HttpGet("https://outlook.office.com/api/v2.0/me/photo/$value");
request.setHeader(javax.ws.rs.core.HttpHeaders.AUTHORIZATION, "Bearer " + bearerToken);
request.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
HttpResponse response = httpclient.execute(request);
return IOUtils.toByteArray(response.getEntity().getContent());
According to the error message. Instead of a client_secret in your request body, you need a client_assertion.
For more details, you can reference the blog Building Daemon or Service Apps with Office 365 Mail, Calendar, and Contacts APIs (OAuth2 client credential flow)
According to the API you call "https://outlook.office.com/api/v2.0/me/photo/$value". It seems that you only want to get the photo for the current login user; if so, you can use Authorization Code Grant Flow to get the token which will not require the client certificates.
UPDATE#1:
Can this be done programmatically/API way
As far as I know, the consent need the user's or admin's interactivity.
https://login.windows.net/common/oauth2/authorize?response_type=code&client_id={0}&resource={1}&redirect_uri={2}&prompt={3}
If you are developing a ASP.NET web application, you can reference the sample project O365-WebApp-MultiTenant.
BTW, when calling the API with app-token, you need to specify the user name.
e.g.
https://outlook.office.com/api/v2.0/users('user1#customdomain.onmicrosoft.com')/messages
UPDATE#2:
The 403 code when updating the photo using the app token is expected result.
As we can see from the figure above, updating the user photo requires the delegated permission "User.Read.Write". The app token does not have permission to update user's photo.
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.
I've searched and searched but can't seem to find the answer to what seems like a straightforward authentication scenario.
We have an existing Java web application that uses form-based authorization provided by Spring. We are attempting to access this application via our portal site without challenging the user to enter their credentials (SSO).
The portal has a credential vault and we can successfully access the secrets for the remote web application on the server side. We are using Apache's HTTP Components utility to post the login request to the j_spring_security_check and are successfully authenticating. The response to this post sends back a 302 redirect to the application home page and sets a cookie with a session id.
Now we have to somehow send this authenticated session back to the browser and this is where we are having trouble. Simply redirecting the browser to the home page doesn't work - it redirects us to the login page. Forwarding all of the response headers back to the browser exactly as received on the server-side doesn't work either - still returned to the login page.
So, how do we authenticate server-side and still be able to load the target page client-side?
I am relatively new to this so I apologize if this is a silly question. Any help or advice regarding an alternative approach is appreciated.
Notes:
HttpComponent Client code:
DefaultHttpClient httpclient = new DefaultHttpClient();
try {
// try to get the home page
HttpGet httpget = new HttpGet("http://<host>/<root>/home.action");
HttpResponse httpClientResponse = httpclient.execute(httpget);
HttpEntity entity = httpClientResponse.getEntity();
// check status and close entity stream
System.out.println("Login form get: " + httpClientResponse.getStatusLine());
EntityUtils.consume(entity);
// check cookies
System.out.println("Initial set of cookies:");
List<Cookie> cookies = httpclient.getCookieStore().getCookies();
printCookies(cookies);
/*** Login ***/
HttpPost httppost = new HttpPost("http://<host>/<root>/j_spring_security_check");
// Prepare post parameters
List <NameValuePair> nvps = new ArrayList <NameValuePair>();
nvps.add(new BasicNameValuePair("j_username", getUserFromVault()));
nvps.add(new BasicNameValuePair("j_password", getPasswordFromVault()));
httppost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
httpClientResponse = httpclient.execute(httppost);
// copy response headers and determine redirect location
Header[] allHeaders = httpClientResponse.getAllHeaders();
System.out.println("Headers: ");
String location = "";
for (Header header : allHeaders) {
System.out.println(header);
if("location".equalsIgnoreCase(header.getName())) location = header.getValue();
response.addHeader(header.getName(), header.getValue());
}
// check response body
entity = httpClientResponse.getEntity();
System.out.println("Response content: " + httpClientResponse.getStatusLine());
System.out.println(EntityUtils.toString(entity)); // always empty
EntityUtils.consume(entity);
// check cookies
System.out.println("Post logon cookies:");
cookies = httpclient.getCookieStore().getCookies();
printCookies(cookies);
// populate redirect information in response
System.out.println("Redirecting to: " + locationHeaderValue);
response.setStatus(httpClientResponse.getStatusLine().getStatusCode()); // 302
// test if server-side get works for home page at this point (it does)
httpget = new HttpGet(location);
httpClientResponse = httpclient.execute(httpget);
entity = httpClientResponse.getEntity();
// print response body (all home content is loaded)
System.out.println("home get: " + httpClientResponse.getStatusLine());
System.out.println("Response content: " + httpClientResponse.getStatusLine());
System.out.println(EntityUtils.toString(entity));
EntityUtils.consume(entity);
} finally {
httpclient.getConnectionManager().shutdown();
}
Headers returned from the successful login on the server side:
HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Set-Cookie: JSESSIONID=6F98B0B9A65BA6AFA0472714A4C816E5; Path=<root>
Location: http://<host>/<root>/home.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive
Headers from the client side request and response:
Request:
GET /<root>/home.action HTTP/1.1
Host: <host>
Connection: keep-alive
Referer: http://localhost:10039/SCMViewer/TestLoginServlet?launchScm=Launch+SCM+servlet
Accept:application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.98 Safari/534.13
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=FC8E823AB1A1545BE8518DB4D097E665
Response (redirect to login):
HTTP/1.1 302 Found
Date: Wed, 23 Feb 2011 22:09:03 GMT
Server: Apache/2.2.3 (CentOS)
Location: http://<host>/<root>/security/login.action
Content-Type: text/plain; charset=UTF-8
Content-Length: 0
Via: 1.1 PPWebFilter.<host>:80 (IronPort-WSA/7.0.0-825)
Connection: keep-alive
As a test, we wrote a bit of a hack that seems to work, but is too insecure to be viable:
Embedded a form on the jsp which will post the login credentials directly to the remote site's j_spring_security_check.
Wrote a servlet method to retrieve the credentials from the vault.
Filled the credentials on the client side into hidden form fields and submitted the form via javascript.
It is a bit hard to understand what your application is trying to do, but my best guess is that your 'portal' sits between the user's browser and the application, and you are trying to use the some stored credentials for the application to authenticate on behalf of the users.
There are two things you need to watch for / deal with.
The responses from the application will contain SetCookie headers of some sort. The cookies need to be handled carefully. Depending on the security model you are using:
They could be saved in the portal and used for future requests to the application.
They could be relayed to the user's browser. The portal would also need to pass the cookies through in future requests to the application. (This approach needs to be handled carefully to deal with possible issues with session token leakage.)
Also, be aware that SpringSecurity changes the session cookie when login succeeds. If you don't capture the new session cookie and use them in follow on requests to the application, those requests won't be authenticated.
The application's login mechanism is clearly trying to redirect you (the portal) to the "default" place after logging in, and this is inappropriate. There are two simple fixes for this:
Have the portal detect the final redirect and treat it as an indication that you've successfully logged in. Then have the portal repeat the request for the page you were originally requesting from the application using the new cookie (see above).
IIRC, there's an extra parameter you can add to a j_spring_security_check request that tells the application where to return on successful login. I can't recall the details ...
I thought that forwarding the setCookie response header from the RA into the portal's response to the browser would be all that is needed to transfer the cookie/session id to the user's new browser window. Is that not correct?
That will cause the browser to set the RA's cookie for the portal context. That won't work unless the RA and portal are in the cookie's "scope" (for the want of a better word).
Question is, how do I display this on/through the portal? Do I just have to copy all the content over and map all the relative links accordingly? And, as you state, continue to proxy all requests to the app through the portal, passing the cookie each time? Is there any way to avoid copying/modifying the markup?
You do need to massage the markup. But exactly what massaging is required is not entirely clear. I think you'll need to map the relative links so that when the user's browser sees them they point to the portal. Then, arrange that the portal relays requests to the RA with the appropriate cookies.
One tool that you can use to deal with relative links is the HTML <base> element. In fact, this potentially easier to deal with than absolute links ... if you map everything via the portal.
But beware that there are all sorts of things that can cause grief in this process. For example, you've got to beware of the "same source" restriction, and with javascript with embedded URLs for the RA.
In case anyone is interested, here's how everything turned out.
Once we realized the issue with setting foreign cookies, we decided we had a few options:
Proxy - Tunnel through the portal to the
remote application, using the portal
as a proxy. This option is the most
straightforward logically, but it
has complications as mentioned above
(i.e. you have to modify each
request and each response - adding
cookies and markup as necessary).
This method turned out to be a pain
point for us, not unrelated to our
use of IBM WebSphere Portal 7.
3rd party SSO solution - Use CAS or Tivoli or some other enterprise solution. This is our
ideal final solution, but it is
still being researched to determine
compatibility with our environment.
Cookie Monster - Our interim solution, in order to
get IBM portal out of the way as the
middle man, was to deploy a small
new remote application on the same
server as our target app that simply
accepts a cookie in JSON format and
spits it back to the browser in a
302 redirect response.
The cookie monster solution works as follows: when the
user clicks on the link in the
portal, our portlet will internally
lookup the user's credentials,
authenticate to the remote
application, and return the
authentication cookie/token. We
convert that (as well as the
destination URL) to JSON and return
it to the browser. The browser then
posts this JSON to the remote cookie
application in a new window. The
cookie is reconstituted and placed
in the response along with the 302
and the target location. Voila, the
page redirects to the application
homepage and the user is logged in. Yay!
Some notes for anyone using IBM WebSphere Portal:
We handled the authentication via
resource-serving portlet.
Make sure the response from the resource-serving portlet is not cached (we made the cache expire immediately as we could not return no-cache)
Make sure you ping the portal before making the ajax call as the session may be expired.
I'm sure there are other, more elegant solutions, but this is working for us until we get CAS/Tivoli up and running.