1

I want to build an android client that can interact with the WooCommerce based site using the Rest Api provided by WooCommerce

This is my android code. I am using OkHttp library for networking.

public class MainActivity extends AppCompatActivity {
    OkHttpClient client;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String cred = Credentials.basic("ck_...","cs_...");

        OkHttpClient client = new OkHttpClient
                                    .Builder()
                                    .build();
        Request req = new Request
                            .Builder()
                .addHeader("Authorization",cred)

                .url("http://10.0.2.2:8080/woocom/wp-json/wc/v2/products")
                            .build();
        client.newCall(req).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                Log.d("api resp", "onFailure: ");
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.d("Api resp", "onResponse: "+response.body().string());
            }
        });
    }
}

This is the error log after running the app

com.example.android.woocommerceapiintegration D/Api resp: onResponse: {"code":"woocommerce_rest_cannot_view","message":"Sorry, you cannot list resources.","data":{"status":401}}

What am I doing wrong here. I tried out the NodeJS client provided by WooCommerce which works fine.

Also I cannot access the rest api via curl according to the command given in the docs

Can someone tell me what I am doing wrong ?

Update: The selected answer is what needs to be done in production environments, and is the technically right thing to do. If you want to avoid the hassle of OAuth while on a development server, I have answered it seperately.

Faizaan Gagan
  • 722
  • 1
  • 8
  • 20
  • Please make sure you're contributing positively to the question. What you said is not news to me. – Faizaan Gagan Sep 19 '18 at 11:15
  • According to WooCommerce REST API documentation, when communicating over http connections you must use OAuth. BasicAuth apparently is only supported for https connections. http://woocommerce.github.io/woocommerce-rest-api-docs/#authentication-over-https – akseli Sep 19 '18 at 11:16
  • Ahaan I got it.I can confirm this because the nodejs client I tested also used OAuth. I'll offer you the bounty if you can give me the way to do it in android(irrespective of which http client you use to implement it) – Faizaan Gagan Sep 19 '18 at 11:19
  • You can find a working example of exactly what you are looking for in this repository: https://github.com/rameshvoltella/WoocommerceAndroidOAuth1 – akseli Sep 19 '18 at 11:22
  • @akseli I'll test it and come back with issues. If it works, the bounty will surely go to you. – Faizaan Gagan Sep 19 '18 at 11:24
  • @akseli and thanks in general. You contribution surely bring me closer to the solution. – Faizaan Gagan Sep 19 '18 at 11:27
  • Let me know if you’d like me to write out the information in a bit more detail as a full-on answer. – akseli Sep 19 '18 at 11:39
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/180348/discussion-between-faizaan-gagan-and-akseli). – Faizaan Gagan Sep 19 '18 at 12:25

2 Answers2

1

The 401 code indicates an authorization issue with your connection. Specifically your issue is caused by your usage of BasicAuth with an HTTP connection.

The WooCommerce REST API Documentation indicates that BasicAuth is only supported for HTTPS connections, and HTTP connections must use OAuth 1.0a "one-Legged" authentication to "ensure" your credentials aren't intercepted by an attacker. It's important to note that even with OAuth 1.0a, an HTTP connection will never be truly secure and it's highly recommended to switch your app over to a secure HTTPS connection.

Meanwhile, in order to get your code working, you'll have to implement the OAuth 1.0a authentication method for your Android app.

You can find a complete set of instructions and a complete project example of OAuth 1.0a implementation for Android here. The GitHub Page has an excellent guide with step-by-step instructions on using the library linked above. Just make sure that when using the code provided you make sure to account for the fact that you're using OKHttp. Luckily, the author has commented the code in the instructions very well and makes a note of changes to make when using something like OkHttp.

You could use Retrofit and simply write an Interceptor which takes care of the 'nitty-gritty' part as detailed in the documentation here.

You could also follow the step by step guide detailed in the WooCommerce Documentation here to generate your OAuth signature and finally encodedUrl and then pass it to your http client. This processs involves: (see the Documentation for detailed specs for each section)

  1. Collecting the request method and URL
  2. Collecting all oauth_* parameters and encoding them into a single string using percent encoding and correct ordering. For Example (taken from WooCommerce Docs): oauth_consumer_key=abc123&oauth_signature_method=HMAC-SHA1
  3. Create the base string for the signature by joining the values from 1 and 2. For example: (once again from Docs):

GET&http%3A%2F%2Fwww.example.com%2Fwp-json%2Fwc%2Fv2%2Forders&oauth_consumer_key%3Dabc123%26oauth_signature_method%3DHMAC-SHA1

  1. Finally generate the signature using HMAC-SHA1 (HMAC-SHA256 is also supported).

Personally I would recommend either the first or second approach. "No need to reinvent the wheel".

EDIT: You can look at this question which discusses how you can use self-signed certificates in a local dev environment with OkHttp.

akseli
  • 1,416
  • 14
  • 22
  • thanks for your reply. Actually I am using apache server locally as I am experimenting with the woocommerce api and thats the reason the connection is not secure. I'll surely do it the way you mentioned. Although I want to ask that whether it is possible to turn my dev server to a mock https server so that I can use basic auth in dev phase of my app. – Faizaan Gagan Sep 19 '18 at 14:51
  • Due to time constraint I could only test the sample app on the given link. I am getting 404 error when I try to fire the /products route to display all route. – Faizaan Gagan Sep 20 '18 at 06:11
  • Do you get the same 404 error no matter what client you’re using, or only with OkHttp. Personally I love the Insomnia (https://insomnia.rest) REST client for testing purposes. It’s streamlined, quite lightweight, highly configurable and simple to use. – akseli Sep 21 '18 at 12:24
  • Hey. I was getting a 401 error actually. The app you linked in your answer logs a FileNotFoundException and it generates a url in the log. When I access the site with generated url, it shows signatures dont match. – Faizaan Gagan Sep 29 '18 at 10:57
1

Thanks akseli for answering my question.I've also awarded you the bounty and thanks for adding to my knowledge. Despite everything, I've found a simple solution to this problem.

My concern was that while development, We generally don't have an https based server and hence have to go through that tedious OAuth based process which won't be used is production anyway as the server we will probably use will be https enabled.

So, to use basic authentication while on an http dev server, you need to go to [your wordpress directory]/wp-content/woocommerce/includes/api. Find out class-wc-rest-authentication.php. This class handles the api authentication. Find the authenticate function which looks like this

public function authenticate( $user_id ) {
    // Do not authenticate twice and check if is a request to our endpoint in the WP REST API.
    if ( ! empty( $user_id ) || ! $this->is_request_to_rest_api() ) {
        return $user_id;
    }

    if ( is_ssl() ) {
        return $this->perform_basic_authentication();
    }

    return $this->perform_oauth_authentication();
}

comment out the condition is_ssl and simply return $this->perform_basic_authentication(), so that in any case, basic authentication is performed.

Note:This is a hack just to avoid the hassle of OAuth authentication while in dev environment and is not at all recomended in production.

Faizaan Gagan
  • 722
  • 1
  • 8
  • 20