4

I have tried to tell the entire story in one screenshot, so feel free to look at it first before reading on. You may notice the problem before I even tell you what it is.

(This is live production code, but all sensitive information is redacted in bright purple. I'm my own client, so it's my information to share, but please let me know if I've accidentally shared something I shouldn't.)

I am trying to gain an access token for the Etsy API. Documentation for how to do so can be found here: https://developer.etsy.com/documentation/essentials/authentication/

Note: this is all for the API v3, not v2. There is a lot of documentation for previous versions, but none of it applies here.

I have gained an access token once before, but it was so long ago. I don't know if I'm logging in differently from last time or if the API has changed since last time. My last successful login was using Postman, but that was a long time ago. Making what I am fairly confident should be the exact same call as last time using Postman is also getting exactly the same error described below. (Though I'm not ruling out the possibility that maybe I forgot to save the working call, and maybe the call I found and tried recently has never worked.)

The code on the left runs (off the live website) as shown on the right. This screenshot was taken after clicking the "sign in" button. This particular time both email address and password were blank, but it gets the same error no matter what is typed into both boxes.

"An error has occurred, please try again!" only appears after clicking.

I know that I need to be able to log in to grant myself the access token, as I have done it once before, but this time, I cannot log in. What am I doing wrong?

enter image description here

That code again is:

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
    CURLOPT_URL => 'https://www.etsy.com/oauth/connect?response_type=code&client_id=/*redacted*/&scope=address_r%2520address_w%2520billing_r%2520cart_r%2520cart_w%2520email_r%2520favorites_r%2520favorites_w%2520feedback_r%2520listings_d%2520listings_r%2520listings_w%2520profile_r%2520profile_w%2520recommend_r%2520recommend_w%2520shops_r%2520shops_w%2520transactions_r%2520transactions_w' . '&code_challenge=DSWlW2Abh-cf8CeLL8-g3hQ2WQyYdKyiu83u_s7nRhI&code_challenge_method=S256',
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => '',
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 0,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => 'GET',
    CURLOPT_HTTPHEADER => array(
            'Cookie: /*redacted*/'
    ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;
Olivier
  • 13,283
  • 1
  • 8
  • 24
  • @Phil okay, I recreated the code. I genuinely wish I could make the example more minimal and reproducible. I did the best I could within my abilities so I'll just have to wear the reputation hit from the downvotes. Because it's either that or wear the bank account hit from my Etsy account not working. If it was only a case of putting a few more hours into editing the question, I'd do it, but this is genuinely the limit of my question asking abilities. (Though I am willing to learn.) Thanks for the articles anyway. The image of code one was useful. The other two, I already knew. – Jonathon Philip Chambers Aug 15 '22 at 00:12
  • @Phil what makes this particular issue difficult to make minimal, is the amount of work I must do before login and getting absolutely no feedback about which step was wrong. If I remove any steps, the code is no longer reproduceable. When it's a function I struggle with, yes I can just call the function with some simple integer arguments, echo it out and I'm done. But this? I am genuinely hungry to learn how to make this more minimal. I have no idea. Not saying my reputation hit is undeserved, but I do need to wear it. – Jonathon Philip Chambers Aug 15 '22 at 00:27
  • 1
    Looking at the docs, you're not meant to use an internal request (like via curl) to `/oauth/connect`. You're meant to direct the user to that URL (as in they open it in their browser) and provide a `redirect_uri` in the query params. Etsy will authenticate the user and request they grant your app access. It will then redirect back to the `redirect_url` and provide a `code` query param. You then make an internal request (via `curl`) to `https://api.etsy.com/v3/public/oauth/token` to exchange that code for an access token. The docs seem pretty clear on this flow – Phil Aug 15 '22 at 00:55
  • @Phil I need to read that a couple more times before it properly sinks in, which I will do later today, but on first reading it seems like you have earned the bounty. If you rewrite this comment as an answer, I'll test it, and if it works, I'll give you the green tick and the bounty. It may be a bit premature in saying this but, "Good job! Legend! If you were here in person I'd definitely buy you whatever you're drinking!" Also, forgive whatever I did to earn my downvotes. I'm still learning, but I'm sure I deserved them. – Jonathon Philip Chambers Aug 15 '22 at 01:02
  • I don't like providing answers that are essentially [_read the docs_](https://en.wikipedia.org/wiki/RTFM). I'd close this as a duplicate of [What is the difference between the OAuth Authorization Code and Implicit workflows? When to use each one?](https://stackoverflow.com/q/16321455/283366) but your bounty prevents that. See also this guide to the [Authorization Code Grant](https://www.oauth.com/oauth2-servers/server-side-apps/authorization-code/) – Phil Aug 15 '22 at 01:19
  • @Phil That's fair. In my defence, I must have read that page more times than I can count, but obviously I must have read it wrong more times than I can count. – Jonathon Philip Chambers Aug 15 '22 at 01:55
  • 2
    It's subtle but the main point is right near the start... _"To begin the flow, **direct the user** to `https://www.etsy.com/oauth/connect`"_. This is a common confusion with the code grant flow. You can find tons of questions on StackOverflow with folks trying to use AJAX or server-side requests so don't feel too bad. The guide I linked does a better job explaining it... _"The first step of the web flow is to request authorization from the user. This is accomplished by creating an authorization request link **for the user to click on**."_ – Phil Aug 15 '22 at 02:00
  • *"all sensitive information is redacted in bright purple"* `client_id` is not secret. Users will see it in the address bar of their browser. – Olivier Aug 15 '22 at 09:29
  • @Olivier yes, but it ties the code to the application. Much like, if you're seen naked, cover your face. That way no one will know who they saw. If in doubt, redact. – Jonathon Philip Chambers Aug 16 '22 at 00:57
  • @Phil Different error message means progress! It's a shame to leave 50 reputation in limbo like that, but if you don't take it someone else will. I now see why the correct request was never saved in Postman. The correct request cannot be saved there, because it's not put there. The biggest frustration in this community is every question feels like a stupid question AFTER I know the answer. – Jonathon Philip Chambers Aug 16 '22 at 02:03
  • @JonathonPhilipChambers if you figure it all out, you could always write and accept your own answer `` – Phil Aug 16 '22 at 02:58
  • You've got 20 answers, none of which have a negative score. Just sayin'. Anyway, best of luck with the rest of your project – Phil Aug 17 '22 at 01:00
  • @Phil yeah, I delete my downvoted answers, but okay. I had a stab at it. Though if this gets downvoted, I'll learn my lesson for next time. – Jonathon Philip Chambers Aug 17 '22 at 02:23

1 Answers1

2

I solved it myself, and although it feels like a stupid question in retrospect (don't they all?) here's the answer it deserves:

It all comes from a fundamental misunderstanding of the documentation. It says:

To begin the flow, direct the user to https://www.etsy.com/oauth/connect with a GET request including the following URL parameters:

My misreading of that sentence was:

To begin the flow, perform a GET request to https://www.etsy.com/oauth/connect including the following URL parameters, then direct the user to the URL in the response body.

If you can already see how both sentences are completely different instructions then you already know exactly what I did wrong.

The critical thing to understand, is that visiting a website is a GET request. By extension, visiting a website with URL parameters (for example https://www.example.com?parameter1=value1&parameter2=value2) is a GET request with parameters.

Therefore, directing a user to a website with a GET request with parameters simply means writing the parameters into the URL path. It doesn't mean performing a request server side and trying to find a URL path in the response.