Basic Authentication with Retrofit - retrofit

I am trying to build a client for a REST API using Retrofit. The API uses basic auth and I have been unable to authenticate using Retrofit.
I tested the API using the curl below and it works as expected
curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{some_json}' -u api_key: https://apitest.com/api/v1/customers
Below is the Retrofit client
public interface UserService {
String HOST = "https://apitest.com";
public static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
public static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(HOST)
.addConverterFactory(GsonConverterFactory.create());
/*
* CREATE/UPDATE User
*/
#POST("api/v1/customers")
Call<UserAPIResponse> userUpdate(#Body UserUpdateRequest userUpdateRequest);
static UserService newInstance(String userAPIKey) {
String credentials = userAPIKey + ":";
final String basic = "Basic "+ Base64.encodeBase64(credentials.getBytes());
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", basic);
requestBuilder.header("Accept", "application/json");
requestBuilder.method(original.method(),original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(BlueshiftUserService.class);
}
When I call updateUser on the UserService
Response<UserAPIResponse> response = UserService.userUpdate(userUpdateRequest).execute();
The response.code is 401 (unauthorized/authentication failed)
The curl command with -u and the same credentials works as expected.

The issue was with the credentials encoding. I wasnt sending it as string.
byte[] encodedAuth= Base64.encodeBase64(credentials.getBytes());
final String basic = "Basic " + new String(encodedAuth);

use these libraries in Gradle file
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.3.0'
compile 'com.cookpad.android.rxt4a:rxt4a:0.9.0'
compile 'io.reactivex:rxjava:1.0.12'
and put this classes in your project
public class ServiceGenerator {
private static final String TAG = erviceGenerator.class.getSimpleName();
public static final int READ_TIMEOUT = 10000;
public static final int CONNECT_TIMEOUT = 100000;
// No need to instantiate this class.
private ServiceGenerator(){}
public static <S> S createService(Class<S> serviceClass, String
endpoint) {
// Call basic auth generator method without user and pass
return createService(serviceClass, endpoint, null, null); }
public static <S> S createService(Class<S> serviceClass, String
endpoint, String username, String password) {
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setReadTimeout(READ_TIMEOUT, TimeUnit.SECONDS);
okHttpClient.setConnectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS);
// Set endpoint url and use OkHTTP as HTTP client
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(endpoint)
.setConverter(new GsonConverter(new Gson()))
.setClient(new OkClient(okHttpClient));
if (username != null && password != null) {
// Concatenate username and password with colon for authentication
final String credentials = username + ":" + password;
builder.setRequestInterceptor(new RequestInterceptor() {
#Override
public void intercept(RequestFacade request) {
// Create Base64 encoded string
String string = "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
request.addHeader("Authorization", string);
request.addHeader("Accept", "application/json");
}
});
}
RestAdapter adapter = builder.build();
return adapter.create(serviceClass); } }
and this interface
public class TodolyClient {
private static final String TAG = TodolyClient.class.getSimpleName();
public static final String ENDPOINT = "your base URL";
public interface TodolyService {
#GET("/wp-json/wc/v2/products")(your remaining url)
Observable<Object> isAuthenticated();
}
}
and call the below method in your main activity
private void createProject() {
final TodolyClient.TodolyService service =ServiceGenerator.createService(
TodolyClient.TodolyService.class, TodolyClient.ENDPOINT, "your user name",
"your password");
Observable<Object> observable = service.isAuthenticated();
AndroidCompositeSubscription compositeSubscription = new AndroidCompositeSubscription();
observable
.lift(new OperatorAddToCompositeSubscription<Object>(compositeSubscription))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Object>() {
#Override
public void onNext(Object project) {
android.util.Log.d(TAG, "onNext: "+project.toString());
}
#Override
public void onCompleted() {
android.util.Log.d(TAG, "onNext:commm " );
}
#Override
public void onError(Throwable e) {
android.util.Log.d(TAG, "onNext: eeeeeeeee"+e.getMessage());
}
});
}

This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class), You can add any character set for encoding as the third parameter here.
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
References: https://mobikul.com/basic-authentication-retrofit-android/

Related

how to handle http response streamly by vertx http client?

#Slf4j
public class DownloadImg {
private final WebClient webClient;
private final Vertx vertx;
public DownloadImg(Vertx vertx) {
WebClientOptions newOptions = new WebClientOptions();
newOptions.setDefaultPort(12345);
newOptions.setDefaultHost("localhost");
webClient = WebClient.create(vertx,newOptions);
this.vertx = vertx;
}
public void download(String scm, String version) {
String file = String.format("/%s_%s.tar.gz", scm.replace("/", "."), version);
webClient.get("url_for_file")
.send(it -> {
it.result();// i acully
});
}
}
I do get all the file data, but I want to handle those data by stream API in case the file is too large or OOM exception.
any idea?

How to read HTTP 500 using a Spring RestTemplate client

A simple Spring Boot REST Controller
#PostMapping(path = "check-and-submit", produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<MyOutput> checkAndSave(#RequestBody #Valid MyInput input, Errors errors){
ResponseEntity<MyOutput> result = null;
if (errors.hasErrors()) {
result = new ResponseEntity<>(MyOutput.buildErrorResponse(errors), HttpStatus.INTERNAL_SERVER_ERROR);
} else {
myDao.save(input.buildEntity());
result = new ResponseEntity<>(MyOutput.buildSuccessResponse(), HttpStatus.OK);
}
return result;
}
And the test class for it
public static void main(String[] args) {
MyInput dto = new MyInput();
// set properties
RestTemplate restTemplate = new RestTemplate();
MultiValueMap<String, String> headers = new LinkedMultiValueMap<String, String>();
headers.add("Content-Type", "application/json");
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
HttpEntity<MyInput> request = new HttpEntity<MyInput>(dto, headers);
try {
ResponseEntity<MyOutput> result = restTemplate.postForEntity(URL, request, MyOutput.class);
System.out.println(result);
} catch(Exception e) {
e.printStackTrace();
}
}
For success scenario this works fine. But, for exception scenrio, i.e. HTTP 500 this fails
org.springframework.web.client.HttpServerErrorException: 500 null
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:97)
As suggested in one of the posts, I created a error-handler that can successfully read the response
public class TestHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
Scanner scanner = new Scanner(response.getBody());
String data = "";
while (scanner.hasNext())
data += scanner.next();
System.out.println(data);
scanner.close();
}
}
But how can I let RestTemplate read and deserialize the response JSON even in case of HTTP 500.
Before any other human-question-flagging-bot marks this as duplicate, here's a humble explanation on how this is different from the others.
All other questions address how to handle HTTP 500, at max read the response-body. This questions is directed at if it is possible to deserialize the response as JSON as well. Such functionality is well established in frameworks such as JBoss RESTEasy. Checking how same can be achieved in Spring.
This should work.
try {
ResponseEntity<MyOutput> result = restTemplate.postForEntity(URL, request, MyOutput.class);
} catch(HttpServerErrorException errorException) {
String responseBody = errorException.getResponseBodyAsString();
// You can use this string to create MyOutput pojo using ObjectMapper.
}

when call yandex api It gives me 403 Forbidden by retrofit

When I call this link
https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20151201T195234Z.a35326958c23a7a8.51da9f9c1ffe2d901f1ee0e4bf3cfdadfe19b3f8&ui=ru&text=apple&lang=en-ru on my browser it works, but when I call it using retrofit It gives me 403 Forbidden. Вut that I get only when I do not use the key in retrofit. When I use key, I get no callback by debug. Please help, I am suffering for the second week
I use retorfit so :
API
public interface APIService {
#GET("translate")
Call<Repo> loadRepo(
#Query(value = "key", encoded = true) String key ,
#Query("ui") String ui,
#Query("text") String text,
#Query("lang") String lang1) };
Repo r;
String text="apple"; String ui="ru";
String key="trnsl.1.1.20151201T195234Z.a35326958c23a7a8.51da9f9c1ffe2d901f1ee0e4bf3cfdadfe19b3f8";
String lang1="en-ru";
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://translate.yandex.net/api/v1.5/tr.json/")
.addConverterFactory(GsonConverterFactory.create())
// .client(httpClient)
.build();
service = retrofit.create(APIService.class);
service.loadRepo( URLEncoder.encode(key), ui, text, lang1).enqueue(new Callback<Repo>() {
#Override
public void onResponse(Response<Repo> response, Retrofit retrofit) {
r = response.body();
int a = 1; // under debug here I look response and see 403 Forbidden
}
#Override
public void onFailure(Throwable t) {
}
});

Android : Linkedin request token failed

I have used the linkedin-j-android.jar of version 1.0.429 in my android project
here is the link for downloading patched linkedin-j-android.jar with scope fix
http://db.tt/yQjhqeq3
below is the code which i have used in my project:
class ShowNetWorkUtils{
public static final String CONSUMER_KEY = "xxxxx";
public static final String CONSUMER_SECRET = "yyyy";
public static final String OAUTH_CALLBACK_SCHEME = "x-oauthflow-linkedin";
public static final String OAUTH_CALLBACK_HOST = "calback";
public String scopeParams="rw_nus+r_baseprofile";
public static final String OAUTH_CALLBACK_URL = OAUTH_CALLBACK_SCHEME
+ "://" + OAUTH_CALLBACK_HOST;
private void configureLinkedIn() {
new Thread(){
#Override
public void run() {
try {
Looper.prepare();
LinkedInOAuthService oauthService = LinkedInOAuthServiceFactory.getInstance().createLinkedInOAuthService (CONSUMER_KEY,CONSUMER_SECRET,scopeParams);
LinkedInApiClientFactory factory = LinkedInApiClientFactory.newInstance(CONSUMER_KEY,CONSUMER_SECRET);
LinkedInRequestToken liToken = oauthService.getOAuthRequestToken(OAUTH_CALLBACK_URL);
//LinkedInRequestToken liToken = oauthService.getOAuthRequestToken();
String url = liToken.getAuthorizationUrl();
/* For Linkedin dialog create and show */
LKDialog lkdDialog = new LKDialog(_context, url);
LKDialog lkdDialog.setDelegate(ShowNetWorkUtils.this);
LKDialog lkdDialog.show();
Looper.loop();
}
catch (Exception e) {
e.printStackTrace();
}
} //End run
}.start();
}
} //End Class
I am getting the exception when i request for the token in line "oauthService.getOAuthRequestToken(OAUTH_CALLBACK_URL)" and also i have used "oauthService.getOAuthRequestToken()" but i am getting the same exception in this case also.
The exception is :
com.google.code.linkedinapi.client.oauth.LinkedInOAuthServiceException: oauth.signpost.exception.OAuthCommunicationException: Communication with the service provider failed: https://api.linkedin.com/uas/oauth/requestToken?scope=rw_nus+r_baseprofile
please provide me some solution to solve this..
Replace the following line in your code
public String scopeParams="rw_nus+r_baseprofile";
with
public String scopeParams="rw_nus+r_basicprofile";
Because the permission scope is r_basicprofile but you have written as r_baseprofile
Hope it will help you.

Read http post headers

Hi I am having trouble I am trying to learn restful services.I created a web service using jax-rs which is shown below
#Path("/users")
public class Welcome {
#POST
#Consumes("text/xml")
#Produces("text/xml")
public Response welcome(String incomingXML){
return Response.status(200).entity("timestamp : " + incomingXML).build();
}
}
I use the following test client to test the service
public class TestService {
public static void main(String args[]) throws ParserConfigurationException, SAXException, IOException {
ClientConfig config = new DefaultClientConfig();
Client client=Client.create(config);
WebResource service=client.resource(getBaseURI());
String urlString = "http://localhost:8080/JaXRSDemo/rest/users";
URL url = new URL( urlString );
HttpURLConnection con = (HttpURLConnection) url.openConnection();
// set up url connection to get retrieve information back
con.setRequestMethod( "POST" );
con.setDoInput( true );
// stuff the Authorization request header
byte[] encodedPassword = ( userName + ":" + password ).getBytes();
con.setRequestProperty( "Authorization",encodedPassword.toString() );
Customer customer=new Customer();
customer.setName("noobstre");
customer.setPin(123455);
ClientResponse response=service.path("rest").path("users").type(MediaType.APPLICATION_XML).post(ClientResponse.class,customer);
System.out.println(" response " + response.getEntity(String.class));
}
private static URI getBaseURI() {
return UriBuilder.fromUri("http://localhost:8080/JaXRSDemo").build();
}
}
I want to use the password in the header at the server side and do a lookup with the database.The problem I am facing is how do I read the headers at the server.
I'm not very familiar with Jax-RS, but you might use the following methods to get the header information you're looking for:
1.) Use #HeaderParam
/**Server side******/
#Path("/users")
public class Welcome {
#POST
#Consumes("text/xml")
#Produces("text/xml")
public Response welcome(String incomingXML, #HeaderParam("Authorization") String authString)
{
//Use authString here
return Response.status(200).entity("timestamp : " + incomingXML).build();
}
}
2.) Use #Context
/**Server side******/
#Path("/users")
public class Welcome {
#POST
#Consumes("text/xml")
#Produces("text/xml")
public Response welcome(String incomingXML, #Context HttpHeaders headers)
{
//Get Authorization Header
String authString = headers.getRequestHeader("Authorization").get(0);
return Response.status(200).entity("timestamp : " + incomingXML).build();
}
}
Hope this helps!
I solved it using Jersey Client
//clientside
ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
final String userName = "admin";
final String password = "admin";
String cred = userName + ":" + password;
WebResource service = client.resource(getBaseURI());
Customer customer = new Customer();
customer.setName("noob");
customer.setPin(123455);
ClientResponse response = service.path("rest").path("users")
.accept(MediaType.APPLICATION_XML)
.header("Authorization", cred)
.post(ClientResponse.class, customer);
System.out.println(" response " + response.getEntity(String.class));
At the server side
#Path("/users")
public class Welcome {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public Response welcome(String incomingXML, #Context HttpHeaders headers) {
String s = headers.getRequestHeaders().getFirst("authorization");
return Response.status(200).entity("timestamp : " + incomingXML + s)
.build();
}
}

Resources