Can't obtain oauth request token from redmine with redmine_oauth_provider - java

I'm trying to use oauth in a redmine 2.4.2 with the redmine_oauth_provider plugin (https://github.com/a-ono/redmine_oauth_provider).
I have configure a new client in redmine and get then the consumer key and consumer secret.
I try then to make an example using the scribe library (used by jenkins to make the oauth work).
But I can't get the request token, redmine send me back a 401 with body Invalid OAuth Request.
Here is the Service I wrote
package org.scribe.builder.api;
import org.scribe.model.Token;
public class RedmineApi extends DefaultApi10a {
protected final String redmineUrl;
public RedmineApi(String redmineUrl) {
redmineUrl = redmineUrl.trim();
if (!redmineUrl.endsWith("/")) {
redmineUrl += "/";
}
this.redmineUrl = redmineUrl;
}
#Override
public String getAccessTokenEndpoint() {
return redmineUrl + "access_token";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return redmineUrl + "authorize?oauth_token=" + requestToken.getToken();
}
#Override
public String getRequestTokenEndpoint() {
return redmineUrl + "request_token";
}
}
And the client test
OAuthService service = new ServiceBuilder()
.provider(new RedmineApi("https://nuiton.org/oauth"))
.apiKey("XXX")
.apiSecret("XXX")
.debug()
.build();
Token requestToken = service.getRequestToken();
At runtime I got
using Http Header signature
sending request...
response status code: 401
Exception in thread "main" org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this: 'Invalid OAuth Request'
And on the redmine side I got
Started POST "/oauth/request_token" for 78.216.18.159 at Wed Feb 05 22:30:13 +0100 2014
Processing by OauthController#request_token as HTML
Current user: anonymous
Rendered text template (0.0ms)
Filter chain halted as # <OAuth::Controllers::ApplicationControllerMethods::Filter:0xf533d024 #options={:interactive=>false, :strategies=>:two_legged}, #strategies=[:two_legged]> rendered or redirected
Completed 401 Unauthorized in 3.5ms (Views: 0.6ms | ActiveRecord: 0.6ms)
Any help will be much appreciated.

Related

Authorization Bearer token in Apache Camel

I want to use Apache Camel to call a rest webservice which takes a bearer token as authorization. I am getting 401 unauthorized access if I use the below code--
#Component
public class LoadFileRouteBuilder extends RouteBuilder{
#Override
public void configure() throws Exception {
from("timer:mytimer?repeatCount=1")
.log("Scheduled job!")
.to("direct:httpRoute");
from("direct:httpRoute")
.log("Http Route started")
.setHeader(Exchange.HTTP_METHOD).constant(HttpMethod.GET)
.setHeader(Exchange.CONTENT_TYPE, constant("application/json"))
.setHeader("Authorization").simple("Bearer "+ myToken)
.to("https://my-rest-ws-url")
.log("Response : ${body}");
}
}
I have also tried using process method to set the authorization token exchange.getIn().setHeader("Authorization", "Bearer "+myToken); didn't work either.
Is it the correct way to send bearer token ?

Authenticate SOAP Header request for webservice

I need to authentication SOAP header and give the response accordingly in my web service. The authentication header will verify the userId and password information.
If authenticated, then SOAP body of the request will be processed, else Invalid Authentication message will be returned
Below is my controller
package com.ws.controller;
#Endpoint
public class MyWSEndpoint
#Autowired(required=true)
private WSService service;
#PayloadRoot(localPart = "myWSRequest", namespace = Constants.TARGET_NAMESPACE)
public #ResponsePayload MyWSResponse getInfo(#RequestPayload MyWSRequest request) throws Exception
{
MyWSResponse response = new MyWSResponse();
response=service.getResponse();
return response;
}
}
i'm using Spring + SOAP
Please advise if i do right or better approach to solve.
Any working samples or projects will be much appreciated

No suitable HttpMessageConverter found for response type in in case the backend response with a error status code

I'm using robospice for my RESTful Communication to a backend.
Here is the specification of the interface:
## carid [/cars]
### Register a car[POST]
+ Request (application/json)
+ Header
Accepted-Language: en
Authorization: Basic xyz
+ Body
{
"carId" : "ad885f2b-dbd3-49ad-aa34-a7671cf5e337"
}
+ Response 200 (application/json)
{
"magicQuestionDefined": true,
"newCar": true
}
+ Response 401
+ Response 404 (application/json)
{
"message" : "No car owner found"
}
+ Response 400
+ Response 500
Here ist the snipped of my listener, when I receive an error (in my case its only a problem with errors, the normal case returning a json works fine) :
private class RegisterCarRequestListener implements RequestListener<RegisterCarResult> {
#Override
public void onRequestFailure(SpiceException e) {
// e: Networkexception
// e.cause=RestClientException
// e.cause.detailMessage="Could not extract response: no suitable HttpMessageConverter found for response type [ch.mycarapp.lib.model.RegisterCarResult] and content type [text/html;charset=iso-8859-1]"
}
It all works fine until the Backend answers with a 401. In this case I except an Exception of type:
HttpStatusCodeException for evaluating the exception status-code.
The reason is "e.cause.detailMessage="Could not extract response: no suitable HttpMessageConverter found for response type [ch.mycarapp.lib.model.RegisterCarResult] and content type [text/html;charset=iso-8859-1]" but it is just a status code 401 that gets returned!
The service is created like this:
public class JsonCarService extends SpringAndroidSpiceService {
#Override
public CacheManager createCacheManager(Application application) throws CacheCreationException {
final CacheManager cacheManager = new CacheManager();
final JacksonObjectPersisterFactory jacksonObjectPersisterFactory = new JacksonObjectPersisterFactory(application);
cacheManager.addPersister(jacksonObjectPersisterFactory);
return cacheManager;
}
#Override
public RestTemplate createRestTemplate() {
final RestTemplate restTemplate = new RestTemplate();
// web services support json responses
final MappingJackson2HttpMessageConverter jsonConverter = new MappingJackson2HttpMessageConverter();
// web Services support also Text Messages ( for some error cases
final StringHttpMessageConverter stringConverter = new StringHttpMessageConverter(Charset.forName("ISO-8859-1"));
final List<HttpMessageConverter<?>> listHttpMessageConverters = restTemplate.getMessageConverters();
listHttpMessageConverters.add(jsonConverter);
listHttpMessageConverters.add(stringConverter);
restTemplate.setMessageConverters(listHttpMessageConverters);
return restTemplate;
}
}
As you can see I have added a stringConverter too, but unfortunately it did not resolve the problem.
public class RegisterCarRequest extends BaseCarSpiceRequest<RegisterCarResult> {
RegisterCarInput input;
private final Context ctx;
public RegisterDeviceRequest(RegisterCarInput input, Context ctx) {
super(RegisterCarResult.class);
this.input = input;
this.ctx = ctx;
}
#Override
public RegisterCarResult loadDataFromNetwork() throws Exception {
final String url = ctx.getString(R.string.base_url) + "/cars";
final HttpEntity<?> requestEntity = new HttpEntity<Object>(input, getRequestHeaders());
final ResponseEntity<RegisterCarResult> response = getRestTemplate().exchange(url, HttpMethod.POST, requestEntity,
RegisterCarResult.class);
return response.getBody();
}
}
The pojo for sending to the server is this one:
public class RegisterCarInput {
private String carId;
public String getCarId() {
return carId;
}
public void setCarId(String carId) {
this.carId = carId;
}
}
the pojo for the Response is this Object:
public class RegisterCarResult {
private Boolean magicQuestionDefined;
private Boolean newCar;
public Boolean getMagicQuestionDefined() {
return magicQuestionDefined;
}
public void setMagicQuestionDefined(Boolean magicQuestionDefined) {
this.magicQuestionDefined = magicQuestionDefined;
}
public Boolean getNewDevice() {
return newCar;
}
public void setNewCar(Boolean newCar) {
this.newCar = newCar;
}
}
The Request gets called like this:
private void performRequest(RegisterCarInput input, User user) {
final RegisterCarRequest request = new RegisterCarRequest(input, getApplicationContext());
request.setAuthUser(user);
spiceManager.execute(request, new RegisterCarRequestListener());
}
Does anybody see a missing link for being able to parse different structures of responses?
Is it really necessary (for robospice) to receive httpstatuscodes (with a
content-type=application/Json for errors with no payload and a 401 status code) ?
http states in the Listener when the backend returns with a 401 ?
tia
Luke
EDITED:
I did some investigation and can now say that the backend returns a 403 when my basic authentication is wrong and sends following response:
+ Response 403 (text/html)
HTTP/1.1 403 Forbidden
Date: Wed, 12 Nov 2014 08:26:14 GMT
Server: Apache
Transfer-Encoding: chunked
Content-Type: text/html
97
<html>
<head>
<title>
403 Forbidden
</title>
</head>
<body>
<h1>Forbidden</h1>
You tried to access a page without having the required permissions.<br>
</body>
</html>
Unfortunately I don't have any influence to the behavior of the backend.
Facts:
The calls are working when everything is ok (200) and receiving RegisterCarResult as json
The calls are working when only a status code (401) (with no content in the response entity) is returned.
The calls are failing when a status code (403) with html content is returned in the entity
So the question, which answer will solve my problem is this:
How can I handle RestResponses, which can have different content-types (application/json)(text/html) in their entities with ROBOSPICE?
I want to send a Json with a request to the backend => ok
I want to receive a 200 as response and a application/json in the response entity => ok
I want to receive only a status code 401 with no content => ok
I want to receive a status code 403 with text/html content => HOW?
All those responses(2,3,4) are possible answers to the one request(1)...

Redo previous request when the first fails with 401 Unauthorized

I'm building an Android application that will fetch data from a REST API.
To make the requests I'm using Retrofit together with Otto.
For all my requests I add a RequestInterceptor that will add a header (Authorization) to all my requests.
In the RequestInterceptor I'm calling a method to the my current access_token then I populate the header to the request.
RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
Token token = TokenPreferences.getToken();
request.addHeader("Authorization", token.getTokenType() + " " + token.getAccessToken());
}
};
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://example.com")
.setRequestInterceptor(requestInterceptor)
.build();
...
This works fine until the access_token has expired, then the request will fail with HTTP status 401 Unauthorized.
When this happens, I want to make a new request to get a new access_token from my refresh_token I got and then do the first request again.
I'm not really sure how to make that work.
Try a com.squareup.okhttp.Authenticator. As far as I can tell, this is preferable to com.squareup.okhttp.Interceptor (which you'll find suggested elsewhere), because it will only kick in for unauthorized requests. Here's a basic example:
public class ApiAuthenticator implements Authenticator {
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
for (Challenge challenge : response.challenges()) {
if (challenge.getScheme().equals("Bearer")) {
String authToken = // Refresh the token here
if (authToken != null) {
return response.request().newBuilder()
.header("Authorization", "Bearer " + authToken)
.build();
}
}
}
return null;
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
return null;
}
}
You can attach it to your client like this:
okHttpClient.setAuthenticator(new ApiAuthenticator());
Be aware that if you're using Retrofit to refresh your token and the token is invalid, you might get unexpected, hard-to-debug behavior for 403 codes, but the solution is just to use a try/catch block.
try {
token = oauthService.refreshAccessToken(args);
} catch (RetrofitError error) {
// Do something that leads to login
}
Retry mechanizm is not implemented in Retrofit. It will be in v2. (Retrying requests manually)
You should implement retrying by recursive call from onFailure() callback as Deepack suggested.
I am facing the same issue and I currently don't know how to retry my request after it failed due to a Unauthorized error.
Although #Yuriy Ashaev mentioned that the retry mechanism should be part of the v2.0 Retrofit version, this should be dedicated only to 5xx errors (see Request Object draft description here) and attempting to retry a request that failed for another reason will raise an exception.
As of now, you can still add your own ErrorHandler to your RestAdapter and catch the Unauthorized error to fetch a refresh token. Here is a way to achieve this:
class RestErrorHandler implements retrofit.ErrorHandler {
#Override
public Throwable handleError(RetrofitError cause) {
Response r = cause.getResponse();
if (r != null && r.getStatus() == 401) {
Token token = mTokenProvider.fetchToken();
if (token != null) {
addHeader(HEADER_AUTHORIZATION, token.getToken_type() + " " + token.getAccess_token());
}
// Todo: Relaunch previous request
return cause;
}
return cause;
}
}
However I don't see any way to retry the failed request from this point other than in all your failure() request callback which will be called when returning the RetrofitError from your ErrorHandler...
I would really appreciate if someone could point us how you could retry the failed request from the ErrorHandler as the response only point to f-the request url.

Magento Rest Oauth API (Signature Invalid) 401

I get a Signature invalid problem when I try to get data from Magento in Java. What is wrong with my code:
public class MagentoFacade {
final String MAGENTO_API_KEY = "apikey";
final String MAGENTO_API_SECRET = "apisecret";
final String MAGENTO_REST_API_URL = "urlmagento/api/rest";
public void testMethod() {
OAuthService service = new ServiceBuilder()
.provider(MagentoThreeLeggedOAuth.class)
.apiKey(MAGENTO_API_KEY)
.apiSecret(MAGENTO_API_SECRET)
.debug()
.build();
System.out.println("" + service.getVersion());
// start
Scanner in = new Scanner(System.in);
System.out.println("Magento's OAuth Workflow");
System.out.println();
// Obtain the Request Token
System.out.println("Fetching the Request Token...");
Token requestToken = service.getRequestToken();
System.out.println("Got the Request Token!");
System.out.println();
// Obtain the Authorization URL
System.out.println("Fetching the Authorization URL...");
String authorizationUrl = service.getAuthorizationUrl(requestToken);
System.out.println("Got the Authorization URL!");
System.out.println("Now go and authorize Main here:");
System.out.println(authorizationUrl);
System.out.println("And paste the authorization code here");
System.out.print(">>");
Verifier verifier = new Verifier(in.nextLine());
System.out.println();
System.out.println("Trading the Request Token for an Access Token...");
Token accessToken = service.getAccessToken(requestToken, verifier);
System.out.println("Got the Access Token!");
System.out.println("(if your curious it looks like this: "
+ accessToken + " )");
System.out.println();
OAuthRequest request = new OAuthRequest(Verb.GET, MAGENTO_REST_API_URL+ "/products?limit=2");
service.signRequest(accessToken, request);
Response response = request.send();
System.out.println();
System.out.println(response.getCode());
System.out.println(response.getBody());
System.out.println();
}
public static void main(String[] args) {
MagentoFacade mf = new MagentoFacade();
mf.testMethod();
}
}
public final class MagentoThreeLeggedOAuth extends DefaultApi10a {
private static final String BASE_URL = "urltoMagento/";
#Override
public String getRequestTokenEndpoint() {
return BASE_URL + "oauth/initiate";
}
#Override
public String getAccessTokenEndpoint() {
return BASE_URL + "oauth/token";
}
#Override
public String getAuthorizationUrl(Token requestToken) {
return BASE_URL + "richard/oauth_authorize?oauth_token="
+ requestToken.getToken(); //this implementation is for admin roles only...
}
}
signature is: NnRaB73FqCcFAAVB4evZtGkWE3k=
appended additional OAuth parameters: { oauth_callback -> oob , oauth_signature -> NnRaB73FqCcFAAVB4evZtGkWE3k= , oauth_version -> 1.0 , oauth_nonce -> 753236685 , oauth_signature_method -> HMAC-SHA1 , oauth_consumer_key -> ptrij1xt8tjisjb6kmdqed2v4rpla8av , oauth_timestamp -> 1359710704 }
using Http Header signature
sending request...
response status code: 401
response body: oauth_problem=signature_invalid&debug_sbs=MCe/RB8/GNuqV0qku00ubepc/Sc=
Exception in thread "main" org.scribe.exceptions.OAuthException: Response body is incorrect. Can't extract token and secret from this: 'oauth_problem=signature_invalid&debug_sbs=MCe/RB8/GNuqV0qku00ubepc/Sc='
at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:41)
at org.scribe.extractors.TokenExtractorImpl.extract(TokenExtractorImpl.java:27)
at org.scribe.oauth.OAuth10aServiceImpl.getRequestToken(OAuth10aServiceImpl.java:52)
at magento.MagentoFacade.testMethod(MagentoFacade.java:39)
at magento.MagentoFacade.main(MagentoFacade.java:73)
I might have an answer for you, but it may not work in your case.
I struggled hard to find out why I got signature invalid on my local machine.
Turns out that when calculating the signature in Mage_Oauth_Model_Server::_validateSignature(), Magento builds the request URI part with the URL port path trimmed : $this->_request->getHttpHost()
In my case, the local webserver runs on port 81, thus my signature and the Magento one could not match.
By passing the false parameter to the getHttpHost method you can keep prevent port trim.
I know this is very specific, but I lost all my hair figuring out why so I needed to share it. And who knows, maybe this could help.
Cheers
Bouni
I'd just like to add that in Postman I simply added another urlparameter of getHttpHost with the value of false and that worked as well. I fought with this for an entire day. I hope this saves someone else time.

Categories

Resources