What combination of requests and responses are needed to get an Oauth token from eBay? What is a runame and what headers do I need to keep eBay happy?
After three frustrating days of trying to get Ebay's oauth to give me an access token, I have finally worked it out. As the docs are pain and there is little to no help online, I have decided to post my solution here in the hope that it will help others. I am no good at StackOverflow so let me know if I need to improve my formatting.
app.get("/login/ebay", (req, res) => {
res.redirect(`https://auth.sandbox.ebay.com/oauth2/authorize?client_id=DeanSchm-TestApp-SBX-b843acc90-fd663cbb&redirect_uri=Dean_Schmid-DeanSchm-TestAp-kqmgc&response_type=code`
);
});
The first thing you need to do is redirect to this URL.
The format is like this
https://auth.sandbox.ebay.com/oauth2/authorize?client_id=&redirect_uri=&response_type=code
There is also a scope property, but I don't understand that yet, and I got back a token without is so me.
That URL takes you to the eBay login page. If you are using the sandbox, you need to create a sandbox user and login with sandbox credentials.
Once you log in, eBay will redirect you to a URL of your choosing. You enter the URL you want to be redirected to here.
It's in the ebay developer section under Get A Token From Ebay Via your Application.
This URL can be anything. you just have to handle it in node or express or whatever, because as soon as someone signs in that URL is where they are heading.
Here is how I handled it
app.get("/auth/ebay/callback", (req, res) => {
axios("https://api.sandbox.ebay.com/identity/v1/oauth2/token", {
method: "post",
headers: {
"Content-Type": "application/x-www-form-urlencoded",
Authorization:
"Basic " +
btoa(
`client public key:client secret keys`
)
},
data: qs.stringify({
grant_type: "authorization_code",
// parsed from redirect URI after returning from eBay,
code: req.query.code,
// this is set in your dev account, also called RuName
redirect_uri: "Dean_Schmid-DeanSchm-TestAp-kqmgc"
})
})
.then(response => console.log(response))
.catch(err => console.log(err));
});
A few gotchas that got me.
Make sure you have space after "Basic " in the authorisation
header.
bota is a 3rd party library that base 64 encodes your public and
secret keys. There are many ways to do this. I just did it this way because I stole a bunch of code.
With Axios, the request body is called data but with fetch and other
methods it might be called something else like body or param
The Axios method is in a get request because of the redirect from ebay
defaults to an http get.
ebay now uses https. Make sure you are using
sandbox URLs
We also had to use JS for the eBay API and solved your mention problem with developing a new Lib. It's available here. This lib will also automatically try to refresh the token if it's expires.
This is how we obtain the oAuth token:
import eBayApi from 'ebay-api';
const eBay = new eBayApi({
appId: '-- or Client ID --',
certId: '-- or Client Secret',
sandbox: false,
siteId: eBayApi.SiteId.EBAY_US,
ruName: '-- eBay Redirect URL name --' //in this case: Dean_Schmid-DeanSchm-TestAp-kqmgc
});
// This will generate the URL you need to visit
const url = eBay.oAuth2.generateAuthUrl();
// After grant access, eBay will redirect you to RuName page and set the ?code query.
// Grab the ?code and get the token with:
eBay.oAuth2.getToken(code).then((token) => {
console.log('Token', token);
ebay.oAuth2.setCredentials(token);
// Now you can make request to eBay API:
eBay.buy.browse.getItem('v1|382282567190|651094235351')
.then(item => {
console.log(JSON.stringify(item, null, 2));
})
.catch(e => {
console.log(e);
});
});
Another example with scope can we found here.
Some hints:
with "scope" you tell eBay what you plan to use. You can find the
Descriptions here, under Sandbox/Production Keys Box. (OAuth
Scopes)
if you use axios you can use the auth config, so you dont't
need btoa:
axios("https://api.sandbox.ebay.com/identity/v1/oauth2/token", {
// ...
auth: {
username: 'appId',
password: 'certId'
}
});
To use sandbox without https, e.g. localhost, you can setup a redirect on a https site and redirec/pass the code to non-https site.
I created simple web page using react and express. One module contains simple form with text input field, email input field and submit button and on submit it should send the mail to me, containing data from input fields. I used nodemail to create this sending mail thing! Luckily it works on chrome, unluckily it doesnt work on other browsers( firefox, IE, chrome on mobile ).
I found out that the problem is not in the backend side, but in the function connecting frontend with backend, but after that I got stuck and dont know what to do :(
onSubmit = e => {
var newMessage = {
msg_sender: this.state.msg_sender,
msg_content: this.state.msg_content
}
axios.post("http://localhost:4000/message", newMessage)
.then(res => console.log(res.data))
.catch(err => console.log("Error! " + err)
);
};
That is the part connecting frontend with backend - as I said, it works fine in chrome, but doesnt in other browsers.
Arrows functions are still a relatively new feature in JavaScript and unfortunately some browsers are still not up-to-date and likely never will be able to support them (Looking at you Internet Explorer).
There's a couple of ways to work around this.
1) You could rework all your arrow functions into standard functions:
onSubmit = function(e){
var newMessage = {
msg_sender: this.state.msg_sender,
msg_content: this.state.msg_content
}
var axiosSetup = axios.create({
baseURL: "http://localhost:4000"
})
axiosSetup.post("/message", newMessage)
.then(function(res){ console.log(res.data) })
.catch(function(err){ console.log("Error! " + err) })
);
};
2) You can integrate Babel in your application, which is a compiler that converts your javascript into compatible code for all browsers:
https://babeljs.io/
If you're building application for browser-compatibility it will also be in your best interest to check out mozilla's web developer guide which is a great reference to check if your code will work on other browsers.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Alternatively, you can also use JavaScript's native fetch API. The benefit being that you do not have to install any libraries and its setup for use will be the same across all browsers
onSubmit = function(e){
var newMessage = {
msg_sender: this.state.msg_sender,
msg_content: this.state.msg_content
}
fetch('http://localhost:4000/message', {
method: 'POST',
body: newMessage
})
.then(function(res){ console.log(res.json()) })
.catch(function(err){ console.log("Error! " + err) })
};
See documentation here:
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
I'm trying to build a small application on Nativescript-vue where I'm having backend laravel framework for api calls which needs to be called to get relevant data. For example if user wants to login he needs to validate its credentials through api oauth/token so I'm trying to call this through axios here is my code:
My settings.js file contains.
export const authHeader = {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
this is being imported inside my axios calls:
const postData = {
grant_type: 'password',
username: user.email,
password: user.password,
client_id: clientId,
client_secret: clientSecret,
scope: '',
provider: provider
}
const authUser = {}
axios.post(authUrl, postData, {headers: authHeader}).then(response => {
console.log('Inside oauth/token url')
if(response.status === 200)
{
console.log('response received')
}
})
.catch((err) => {
if(err.response.status === 401){
reject('Validation error')
}
else
reject('Something went wrong')
})
when I try to build with command tns debug android --bundle I get chrome-devtools which shows me:
Digging more deep into it I can see headers are being passed but those are only provisional:
As you can see I've console.log inside my application which show me:
Even while compiling I get following errors:
Guide me how can I achieve this. Thanks.
Edit:
Similarly I used nativescript's own http documentation something like this:
const httpModule = require("http");
httpModule.request({
url: "http://iguru.local/oauth/token",
method: "POST",
headers: { "Content-Type": "application/json" },
content: JSON.stringify({
grant_type: 'password',
username: this.user.email,
password: this.user.password,
client_id: 'check',
client_secret: 'check',
scope: '',
provider: 'check'
})
}).then((response) => {
// Argument (response) is HttpResponse
console.log('Action called')
if(response.status === 200)
{
console.log('Response recieved')
}
}, (e) => {
console.log('Something went wrong')
});
I'm getting the same result, moreover I tried api from server end ex http://confidenceeducare.com/oauth/token it happens to be same. Normal Vue application calls the api perfectly fine. I guess there is some problem with the nativescript application. Do I need to import something more? I'm stuck with it.
If anybody is thinking my api end point is broken, I tried using the urls mentioned in example i.e. https://httpbin.org/post:
and:
When I checked my api in postman it is working over there, I'm getting at least a response with status code:
Edit 2:
For reference github repository https://github.com/nitish1986/sample_mobile_app
I tested the exact same project you have shared in Github with Android 8.0, it works perfectly fine. The axios call hits the error block as the response status (error.response.status) is 401 and data (error.response.data) returns the exact same response you see from Postman.
If you are using Android 9 or later it might fail as you haven't enabled useCleartextTraffic on your application tag in the AndroidManifest.xml.
<application
android:usesCleartextTraffic="true"
android:name="com.tns.NativeScriptApplication"
With iOS also it would fail as you haven't enabled app transport security. Just to allow all Http communication within your app, you have to add the following key to your info.plist
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
In case if you want to allow only specific domains, then use NSExceptionDomains key and list the endpoints.
The problem has to do with how axios is imported. Try:
import axios from 'axios/dist/axios'
this also solves the Module not found: Error: Can't resolve 'net' in... error.
When importing the package normally my requests failed, returning the error
Error in v-on handler (Promise/async): "Error: Request failed with status code null"
I'm trying to implement Google plus sign up on my web app and I followed the google docs to set up the sign up however when I attempt a signup after accepting permissions and using the access token returned to me any api restcall I make returns the Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup error. I have already signed up my app with a ouath 2.0 key, so I don't seem to get what I'm doing wrong. Here is my code.
Cient Side:
const clientId = "5XXX000XX.apps.googleusercontent.com";
const apiKey = "AIzaSyCAXE5JSa36jcC*X7HV40SBcIWBiVGUTBE";
const scopes = "https://www.googleapis.com/auth/plus.login";
let accessToken = null;
function initer() {
gapi.client.setApiKey(apiKey);
// alert("Hello init");
if ($("#authorize-button").length > 0) {
$("#authorize-button").click(onLoginClick);
}
}
function onLoginClick() {
// $("#modalLoading").modal();
// alert("yeah");
gapi.auth.authorize({ client_id: clientId, scope: scopes, immediate: false }, onConnect);
}
function onConnect(authResult) {
// alert("On connect");
if (authResult && !authResult.error) {
alert("Hey");
accessToken = authResult.access_token;
triggerLogin();
} else {
alert("Error");
}
}
triggerLogin = function() {
alert("Triggering login");
$("#modalLoading").modal();
$.ajax({
url: window.config.site_root + "account/google_login",
type: "POST",
data: "access_token=" + accessToken,
success: onLogin,
error() {
onError("Logging In", "starting your session");
},
});
};
onLogin = function(login) {
alert("Login start");
$("#modalLoading").modal("hide");
if (login.operation) {
location.reload();
} else {
alert("Register will start");
triggerRegistration();
}
};
triggerRegistration = function() {
$("#modalLoading").modal();
$.ajax({
url: window.config.site_root + "account/google_registration",
type: "POST",
data: "access_token=" + accessToken,
success: onRegistration,
error() {
alert("An Error");
},
});
};
onRegistration = function(data) {
alert("Handling register");
$("#modalLoading").modal("hide");
if (data.account_exists) {
stage.showErrorModal(
"Account already registered",
"There is already an account with that email address, are you sure you created an account using this login method?",
);
} else if (data.operation) {
alert("Login now");
triggerLogin();
} else {
alert("Error");
onError("Registering", "creating your account");
}
};
Here is my server side code
public function google_registration()
{
$access_token = (isset($_POST["access_token"]) && !empty($_POST["access_token"])) ? $_POST["access_token"] : null;
$name = null;
$email = null;
$account_id = null;
$picture = null;
$gender = null;
try
{
if($access_token)
{
$me = file_get_contents("https://www.googleapis.com/plus/v1/people/me?access_token=".$access_token);
if($me)
{
$me = json_decode($me);
$name = $me->name.formatted;
$email = $me->email;
$account_id = $me->id;
$picture = $me->image;
$gender = ($me->gender == "female") ? 1 : 0;
}
}
}
catch(Exception $error)
{
// let the system handle the error quietly.
}
return $this->service_registration("google", $name, $email, $account_id, $picture, $gender);
}
I too ran into the same error - "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup".
I went and checked my google developer console under APIs for the project associated with the API key/ auth key, eg, https://console.developers.google.com/project/<your app id>/apiui/api. The status for Google+API was set to OFF. I turned it ON.
I then got another access token, and then tried with the new one. It worked, ie, the error was gone and I got the profile details. To cross-check if that was indeed the cause of the error, I went back to console and disabled Google+ API. But now, I get the error:
"Access Not Configured. Please use Google Developers Console to activate the API for your project."
So, I am not 100% sure that it was the turning on/off of the Google+ API in my developer console, but do ensure that this is turned on. Also, wait a few minutes after turning on, and ensure that you get a fresh token each time before trying it.
Make sure you have Google+ Api here enabled.
Without it you will get errors like:
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
To enable it:
1) open https://console.developers.google.com
2) choose your project (top right corner)
3) search for "Google+ API" in search box and enable it if it is not enabled already.
This issue happens when you are already logged in and still try to login again and again. I faced same error so did some experiments 1) I opened website on my mobile and everything was fine. 2) Then i tried on another laptop and used different gmail account to login and it again worked fine. 3) On my first laptop i tied again by clicking "Signin" button i got same error, so i opened google.com then logged out completely and then tried again, this time it worked. So i believe, Issue is clicking login button again and again without logout.
I am not sure if this is a really a issue, but atleast this is what i found. I am still trying, trying and trying , will post if i found anything else.
Cheers !!
You have to add the apiKey with the URL:
$curl = curl_init( 'https://www.googleapis.com/urlshortener/v1/url?key=AIza3834-Key' );
So I ran into this issue and the above two methods/solutions (enabling API access, and signing out of accounts) did not work for me because for google+ signin you have to pass in the access token in the authorization header of the http call.
Here's how you do it via jQuery:
$.ajax({
type: "GET",
url: "https://www.googleapis.com/plus/v1/people/me",
headers: {
"Authorization":"Bearer " + {access_token},
}
});
It seems your issue is with the server side code where you pass in access_token in the params (which is not necessary).
Here's my attempt on what the PHP implementation would look like:
$opts = array(
'http'=>array(
'method'=>"GET",
'header'=>"Authorization: Bearer ".$access_token
)
);
$context = stream_context_create($opts);
// Open the file using the HTTP headers set above
$file = file_get_contents('https://www.googleapis.com/plus/v1/people/me', false, $context);'
Hopefully you're sending that access token over https! It might be worth considering using the code instead and doing an exchange on the server side for an access token, for improved security if nothing else, there's some documentation on that approach here: https://developers.google.com/+/web/signin/server-side-flow
With regards to the problem you're seeing, it seems like the access token is bad, or not making it through correctly. Can you check the access token that you receive against the tokeninfo endpoint: https://www.googleapis.com/oauth2/v1/tokeninfo?access_token= - that should show valid information. Nothing stands out as being off in the code, but if the token is getting mangled you might see a similar error.
I got have the same issue, The solution is: set APIKEY
I was also desperate, and finally I managed to find a solution. The only problem is to add the correct api token linked to your app, in my case was the browser token, and it all works.
Example: I wanted to have all the events associated with my calendar
https://www.googleapis.com/calendar/v3/calendars/{calendar_id}/events?&key={api_key}
maybe I will help someone who is having the same problem.
Good Luck
I was trying to use access_token to fetch a returning user's name and picture when they already logged in during a prior session. After running into the "unauthenticated use" error message, this is what finally worked via cURL in PHP.
//initialize provider specific params for oauth2
$access_token = <fetched from app database from last login> //remember to never expose this to users
$api_url = 'https://www.googleapis.com/plus/v1/people/me';
$headers = [
'Authorization: Bearer '.$access_token
];
//send curl request
$curl = curl_init();
$curl_opts = [
CURLOPT_RETURNTRANSFER => 1,
CURLOPT_POST => 0, //GET
CURLOPT_TIMEOUT => 30,
CURLOPT_SSL_VERIFYPEER => 1,
CURLOPT_SSL_VERIFYHOST => 2,
CURLOPT_URL => $api_url,
CURLOPT_HTTPHEADER => $headers,
];
curl_setopt_array($curl, $curl_opts);
$curl_response = json_decode(curl_exec($curl), true);
$curl_status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
//parse curl response from provider into name and icon fields for my app
$name = $curl_response['displayName'];
$icon = $curl_response['image']['url'];
Note that https://www.googleapis.com/plus/v1/people/me (for returning users via access_token) and https://www.googleapis.com/oauth2/v4/token (for initial login via code) return the user name and picture in different fields/structures.
I had a problem on JavaScript side (Client Side).
In my case it was necessary to add API_KEY to gapi.client.init()
Full my function:
async function initClient () {
return new Promise((resolve, reject) => {
const API_KEY = "YOUR_GOOGLE_API_KEY"
const CLIENT_ID = "YOUR_GOOGLE_OAUTH2_CLIENT_ID"
const DISCOVERY_DOCS = ['https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest']
const SCOPES = 'https://www.googleapis.com/auth/youtube.readonly'
const initData = { apiKey: API_KEY, clientId: CLIENT_ID, discoveryDocs: DISCOVERY_DOCS, scope: SCOPES }
gapi.client.init(initData).then(function () {
// YOUR CODE HERE
}, function (error) {
reject(new Error('Reject Error: ' + error))
})
.catch(err => console.log('Catch Error', err))
})
}
API_KEY and CLIENT_ID can be taken from here (Google Cloud Platform -> APIs & Services -> Credentials -> Credentials [Tab])
In my case, it happened at a specific time of the day, After spending few hours finally found that my daily quota limit (Queries per 100 seconds) was exceeded at that time because of a high number of requests. so it was throwing the error. I have contacted google support to increase them.
In my case, it was because I was using an old access token. You must keep in mind that access tokens have a short life span, so you must use the refresh token to generate a new access token. Example (using a PHP Class):
// Get the access token from DB or filesystem
$accessToken = $this->getAccessToken();
// Set the access token
$this->client->setAccessToken($accessToken);
// Refresh the token if it's expired.
if ($this->client->isAccessTokenExpired()) {
$this->client->fetchAccessTokenWithRefreshToken($this->client->getRefreshToken());
// Save the new access token to DB or filesystem
$this->saveAccessToken($this->client->getAccessToken());
}
I just had same problem and found out what was wrong.
When you enable your Google Plus API from your Library, there is extra steps to completely activate. You have to click "Manage" button in the Google Plus API Library page and fill up two question answer and submit. That's all!!
This fixed my issue and I hope its fixes yours as well. Here is the screenshot of that page where i was taken after I clicked "Manage" button which says "Add credentials to your project".
I'm getting the following error:
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use
requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use
requires signup."
}
}
I'm not sure but i think its caused in my case on the OAuth consent Screen of the Google Developers Console, there is this message,
The user cap limits the number of users that can grant permission to your app when requesting unapproved sensitive or restricted scopes. The user cap applies over the entire lifetime of the project, and it cannot be reset or changed. Verified apps will still display the user cap on this page, but the user cap does not apply if you are requesting only approved sensitive or restricted scopes. If your users are seeing the "unverified app" screen , it is because your OAuth request includes additional scopes that haven't been approved.
I'm requesting restricted scopes:
private val SCOPES: List<String> = arrayListOf(DriveScopes.DRIVE_FILE, DriveScopes.DRIVE_APPDATA, DriveScopes.DRIVE)
Although my app is in development it seems to require verification, I think over the lifetime of the development of my app i have signed in over 100 times. (See OAuth user quotas)
https://support.google.com/cloud/answer/7454865?authuser=0
I Guess i have to verify my App Does anyone know if this is the case...?
I think (at least for some) this error could be leading them in the wrong direction. In my case, my API call was working, then suddenly stopped. Here was my thought process while working through this error, hopefully it will help someone:
OBSERVATIONS
New error was produced
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
This occurred during a test attempting to send an update to the Google Calendar API with the following call:
service.events().patch(calendarId='primary', eventId=id, sendNotifications=True, body=update).execute()
This occurred shortly after I updated the data in my time_start variable referenced in the json that gets sent to google:
update = {
'start': {
'dateTime': time_start, # Specifically this variable (time_start) was changed
'timeZone': time_zone,
},
'end': {
'dateTime': time_end,
'timeZone': time_zone,
},
'description': description,
}
The information in my time_start variable changed from:
2020-05-13T17:06:42-07:00 to 2020-05-13T17:06:42 (note the missing UTC offset)
ACTIONS
Updated my code populating time_start to include the missing UTC offset that was being passed to google calendar API.
RESULTS
The payload successfully transmitted to google calendar and my event was patched with the update
Just in case someone has gotten this issue while using Drive API (in my case, using Python as programming language), this can also occur due to an incorrect metadata update. Here is my error report:
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "dailyLimitExceededUnreg",
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup.",
"extendedHelp": "https://code.google.com/apis/console"
}
],
"code": 403,
"message": "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
}
}
This was, in essence, due to the fact that I was trying to alter both copyRequiresWriterPermission and viewersCanCopyContent fields over an application/vnd.google-apps.folder, which is not allowed.
No clue about why Google chose to send an error report so unrelated to the actual error.
This is because the scope you use for Drive API is labelled restricted.
You can make a limited number of requests to get file data/content.
Once you have reached the limit you have to get another access_token for the existing session. It is like having refresh_token.
You can wrap your code that sends requests to get files in try...catch
If that error is thrown you can requestAccessToken.
NOTE: For refreshing the access token in an existing session, you do not need to ask for content again.