BigCommerce oAuth auth token request always returning 401 - oauth

I can not figure out what I'm doing wrong. I'm developing an App for BigCommerce and can not get the simple oAuth exchange to work correctly.
The initial get request is being made to https://www.my-app.com/oauth/bigcommerce/auth. This is the code in the controller for that request. It's a Laravel 5.6 app:
use Illuminate\Http\Request;
use Bigcommerce\Api\Client as Bigcommerce;
class BigcommerceOAuthController extends Controller
{
public function auth(Request $request)
{
$object = new \stdClass();
$object->client_id = 'my-client-id';
$object->client_secret = 'my-client-secret';
$object->redirect_uri = 'https://my-app.com/oauth/bigcommerce/auth';
$object->code = $request->get('code');
$object->context = $request->get('context');
$object->scope = $request->get('scope');
$authTokenResponse = Bigcommerce::getAuthToken($object);
$storeHash = str_replace('stores/', '', $request->get('context'));
Bigcommerce::configure(array(
'client_id' => 'my-client-id',
'auth_token' => $authTokenResponse->access_token,
'store_hash' => $storeHash
));
echo "<pre>";
print_r($authTokenResponse);
print_r(Bigcommerce::getTime());
echo "</pre>";
}
}
Every time I try to install my draft app from the BigCommerce control panel, I get an error because $authTokenResponse is not an object. When I debug further into the Bigcommerce\Api\Connection class, I can see that the response from the server is empty, and the status is a 401, which means "Unauthorized".
I can't figure out why I am getting this error. As far as I can see, I'm doing everything right. I've tried urlencoding the string retrieved from $request->get('scope'), since that string becomes unencoded by Laravel, but that didn't seem to help.
I am also confused how this is even supposed to work at all. In the BigCommerce docs, they show this example POST request, which uses application/x-www-form-urlencoded Content-Type and passes the request body as a url encoded string:
POST /oauth2/token HTTP/1.1 Host: login.bigcommerce.com Content-Type:
application/x-www-form-urlencoded Content-Length: 186
client_id={CLIENT_ID}&client_secret={CLIENT_SECRET}&code=qr6h3thvbvag2ffq&scope=store_v2_orders&grant_type=authorization_code&redirect_uri=https://app.example.com/oauth&context=stores/{STORE_HASH}
However, if you inspect what's going on in the Connection class, you can see that the Content-Type is being set to application/x-www-form-urlencoded as the docs say, but the request body is being passed in as a json string, not a url string. Shouldn't the request be a url encoded string as the docs suggest?

A couple of things here to check:
Do you have a public URL where you can receive the Auth Callback?
If so, did the store owner registered the app successfully? https://developer.bigcommerce.com/api/registration
When you have the client_id and secret_id. You should have all of the details needed to send a POST request to the BC Auth Token Service at https://login.bigcommerce.com/oauth2/token
The content uses URL encode Make sure to URL encode your content. Be careful of of the encoding of & and = signs when those are actually being used as separators.
More details can be found in this post:
Can BigCommerce Private Apps use OAuth

Related

Twitter API v1.1 media INIT error code 32

I am trying to get the media_id for a media upload. See docs here.
When using postman, my request is processed successfully and I get a response like this:
{
"media_id": 1222234872222222401,
"media_id_string": "1222734822222102201",
"expires_after_secs": 86399
}
Unfortunately, using postman for our app is not an option. However, when I post a tweet with just text, the tweet is posted successfully using our own native code. I have also recreated the request from postman, and can successfully recreated the same oauth_signature needed for the media upload authorization. So I know that the backend is working in that I can create valid credentials, but I think I need some help structuring the POST request itself.
Here is the code (Lucee ColdFusion):
mediaEndpoint = "https://upload.twitter.com/1.1/media/upload.json?command=INIT&total_bytes=10240&media_type=image/jpg&oauth_consumer_key=consumerKeyHere&oauth_token=tokenHere&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1653075352&oauth_nonce=UU5V18WLaPN&oauth_version=1.0&oauth_signature=verifiedSignature";
cfhttp(url=mediaEndpoint, method="POST", result="init") {
cfhttpparam(type="header", name="Content-Type", value="application/x-www-form-urlencoded");
cfhttpparam(type="header", name="Accepts", value="*/*");
cfhttpparam(type="header", name="Accept-Encoding", value="gzip, deflate, br");
cfhttpparam(type="header", name="Connection", value="keep-alive");
cfhttpparam(type="body", value="command=INIT&media_type=#mediaParameters.media_type#&total_bytes=#mediaParameters.total_bytes#");
}
But I keep getting the following 401:
{"errors":[{"code":32,"message":"Could not authenticate you."}]}
I believe you're supposed to send your authorization token in the header. You're sending it in the URL as a query string. Twitter's documentation would indicate you need to include an authorization header as such:
cfhttpparam(type="header", name="Authorization", value="Bearer: #YourAccessToken#");

restsharp and Postman

I am attempting to get an OAuth2 access token from ZOHO using RestSharp code. The Postman simulation works correctly so I know there is something I'm missing in my code.
I always get an "invalid client id" result status. However in Postman, it works and returns a code when I click the "Get new access token". I have the same items as in the Postman authorization tab (client_id, client_secret, etc). In Postman, "Body" is set to "none", and there are no parameters or headers. The only difference between my code and postman, is that Postman requires the Callback URL. My code is trying to get the code using "self-client", which bypasses the callback URL.
I have tried several different alternatives to the request call including ParameterType.Body, and ParameterType.GetOrPost. Is GetOrPost the same as a form?
client = New RestClient(ZOHO_API_URL)
request = New RestRequest(TokenUrl, Method.POST)
request.AddHeader("content-type", "application/x-www-form-urlencoded") ' also tried: "application/json")
request.AddParameter("grant_type", "authorization_code",
ParameterType.GetOrPost)
request.AddParameter("client_id", Client_ID, ParameterType.GetOrPost)
request.AddParameter("client_secret", Client_Secret,
ParameterType.GetOrPost)
request.AddParameter("code", Grant_Token, ParameterType.GetOrPost)
response = client.Execute(request)
This is the translated Postman code for RestSharp:
var client = new RestClient("http://");
var request = new RestRequest(Method.POST);
request.AddHeader("Postman-Token", "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx");
request.AddHeader("cache-control", "no-cache");
IRestResponse response = client.Execute(request);
Any ideas on what I am doing wrong. I have tried to view the raw data coming across with Fiddler, but when I do that, Postman indicates a failure.
What code do I need to use to duplicate what Postman is doing?
How do I implement a callback URL if that is also required?
I quickly checked ZoHo REST API docs and it seems like you should use the Limited Input Device authentication flow.
From what I can understand from their help page, you indeed need to do a POST request, but parameters must be specified as query parameters:
https://accounts.zoho.com/oauth/v3/device/code?
client_id=1000.GMB0YULZHJK411248S8I5GZ4CHUEX0&
scope=AaaServer.profile.READ&
grant_type=device_request
You will also get better off with JSON set as a default for serialisation and content type just by using client.UseJson().
It maybe that Postman is following a redirect from your API endpoint as the functionality is different Postman verses RestSharp (possibly missing a trailing slash or similar).
Try adding
client.FollowRedirects = false;
to your RestSharp code and analyse the result.

AWS API Gateway issue for HTTP Method

I created an AWS API-gateway for an HTTP method PUT. When I do a test in API-gateway, that works fine, but when I call it from a REST client, I get 404 bad-request and missing authentication token errors. I didn't set any authorization to true or a required API key to true.
I passed these query parameters to a REST client:
auth_id : 8798iuyiu123123
time_stamp :1231231
test_json : [{"id"=>"1","value"=>"mount"},{"id"=>"2","value"=>"chart"}]
HEADER
content-type : application/json
When I change the test_json value to %5B%7B%22id%22:%221%22,%22value%22:%22test%22%7D,%7B%22id%22:%222%22,%22value%22:%2213+%D8%B4%D8%A7%D8%B1%D8%, then I get the response.
i am new to react, calling from react
Request.put('https://api-gateway.sqwdwed123.com/eretw/update-chart')
.set('Content-Type', 'application/json')
.query({ auth_id: localStorage.auth_id})
.query({ time_stamp:this.props.time_stamp})
.query({ test_json:JSON.stringify(newadd)})
should i pass this test_json through body?
Am I doing anything wrong?
This is usually related to requesting a URL that doesn't exist. Please make sure you're using the correct HTTP method and resource path to a valid resource (the sample invoke URL does not include any resource path). If this still doesn't work. Make sure you actually deployed your API.
The HTTP Response of Bad Request is because you have the Query Parameter that are not URL Encoded. There are 2 things that you can do now:
Pass the test_json as Query Param but making sure that they are URL Encoded. This will put a restriction on the size of the string and hence Not Recommended.
Pass the test_json as Request Body. (Recommended)

Desire2Learn Valence API | JSON not loading

I'm using the Python Requests library with the Valence-provided Python SDK to attempt to do a GET request. Something odd is happening with the URL and I'm not sure what. The response I get is 200 (which leads me to believe that the authentication is working), but when I try to print the JSON from the Request object, it instead prints the HTML of the page instead of the JSON.
I'm using modified code that I read from http://docs.valence.desire2learn.com/clients/python/auth.html.
Here's the Python code:
import requests
import auth as d2lauth
from auth import *
app_creds = { 'app_id': '----', 'app_key': '----' }
ac = d2lauth.fashion_app_context(app_id=app_creds['app_id'], app_key=app_creds['app_key'])
auth_url = ac.create_url_for_authentication('ugatest2.view.usg.edu', 'http://localhost:8080')
redirect_url = "https://localhost:8080?x_a=3----&x_b=3dMRgCBAHXJDTA2E6DJIfdWq-gYl-pk77fF_3X5oDUuqc"
uc = ac.create_user_context(auth_url, 'ugatest2.view.usg.edu', True)
route = 'ugatest2.view.usg.edu/d2l/api/versions/'
url = uc.create_authenticated_url(route)
r = requests.get(url)
print(r.text)
The output is the HTML of a page instead of JSON. If I do print(r), I get a status of 200. I think my redirect URL may be the issue, but I'm not sure what exactly is wrong. Thanks for any help!
Two things look off to me:
Using auth_url to create a user context isn't going to work, that's the URL you need to send the user to so they can authenticate. You need to use the URL you were redirected to after authenticating to build the user context. Assuming redirect_url is that URL, you should be passing that to create_user_context and not auth_url.
ugatest2.view.usg.edu/d2l/api/versions/ is not a valid value for passing to create_authenticated_route, /d2l/api/versions is probably what you want. The SDK will prepend the scheme, domain, and port so including those in the value passed is going to result in an incorrect URI.
Once your app is working properly, you'll be able to access a JSON response by using r.json() rather than r.text.

Authenticating to an API with a token

I'm working with the Zendesk API, an HTTPS-only, JSON API and authentication is required to update a resource, and the API supplies an API token to use when authenticating as different users. When updating a resource, I issue a PUT request over SSL, assign the appropriate JSON content to the request body, and specify the Content-Type request header as application/json.
Next, the API instructs its users to authenticate as the end-user by either using the user's email and password (which I can't do for several reasons) or to use the user's email address along with the API token. The following is my attempt to authorize to the API with the Authorization header:
#id = params[:id]
#comment_body = params[:comment]
uri = URI.parse "https://{subdomain}.zendesk.com/api/v2/requests/#{#id}.json"
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Put.new(uri.request_uri)
req.body = '{"request": {"comment":{"value":' + "\"#{#comment_body}\"" + '}}}'
req['Content-Type'] = 'application/json'
#The following two lines do not work!
credentials = Base64::encode64("{user_email}/token:{api_token}")
request.headers['Authorization'] = "Basic #{credentials}"
response = http.request(req)
The API specifies that the format for authentication using the API token is {user_email}/token:{api_token}. I encoded that format with Base64::encode64 and passed it to the Authorization Header preceded with Basic, but the response is a 401 Unauthorized. However, replacing those two lines with req.basic_auth {user_email}, {user_password} works fine.
So my question is, how can I authenticate as a different user using the email and the given API token as authentication instead of supplying the user's email and password to req.basic_auth?
The googling I've done on the topic has revealed very little; apparently it's a lot more common to use the normal {username}:{password} format when dealing with the Authorization header than an API token.
Thanks in advance!!
Update: Weirdly, trying to authenticate as the end-user with req['Authorization'] = "Basic #{credentials}" does not return a 401 Unauthorized Error or a WWW-Authenticate header while trying to authorize as request.headers['Authorize'] = "Basic #{credentials}" does.
Finally figured it out after much head-banging and nearly throwing my laptop out the window. Suddenly, the answer seems incredibly obvious.
When using Net::HTTP, its basic_auth method can also accept tokens depending on the API, and the Zendesk API specifies that the format for using the API token is {email}/token:{token}. Basic authentication uses the format {username}:{password}, where the two fields are separated by a colon, meaning in Zendesk's case I can place {email}/token as the first argument and {token} as the second argument (instead of the username as the first argument and the password as the second argument), so the following code is correct:
req.basic_auth "{email}/token", "{api_token}"
I hope anyone who found this useful could leave a comment. Nice to know I spared someone from this frustration.

Resources