I am trying to implement a google oauth 2.0 login without using any libraries in my Node.js application.
I have created an app on the Google API console with the redirect url as http://localhost:3000. During login my response_type is code which returns a one-time use code that needs to be exchanged with the token_endpoint as described here.
The exchange is done on my node.js server with the following snippet.
axios({
url: 'https://www.googleapis.com/oauth2/v4/token',
method: 'post',
data: {
code: code,
client_id: sso.clientId,
client_secret: sso.clientSecret,
redirect_uri: sso.redirect_uri,
grant_type: 'authorization_code',
}
})
.then((response) => {
console.log(response.data);
})
.catch(function(err) {
console.log(err.response.data);
});
But this is is sending me back an error response of
{
"error": "unsupported_grant_type",
"error_description": "Invalid grant_type: "
}
instead of the user token.
Please help me identify the issue.
I tried doing a POSTMAN query as well with the same payload in the raw with content-type set to application/json, and it gave me the same error.
You need to use params in place of your data while making your exchange call through axios, revised block will be like
params: {
code: code,
client_id: sso.clientId,
client_secret: sso.clientSecret,
redirect_uri: sso.redirect_uri,
grant_type: 'authorization_code',
}
Hope this helps!
NEVER include things like a clientSecret in GET parameters. This can lead to serious security issues !
The google doc is very clear about how to send the data ;
As a POST body - as always in OAuth2 :
https://developers.google.com/identity/protocols/OAuth2WebServer - Step 5, REST code sample
They must be sent as a string but in the POST body / data :
The string is the urlencoded parameters like
code=4/P7q7W91a-oMsCeLvIaQm6bTrgtp7&
client_id=your_client_id&
client_secret=your_client_secret&
redirect_uri=https://yourOauth2redirectUrl.example.com/code&
grant_type=authorization_code
Related
I am trying to request an authorization token through auth0 using an M2M application with the code:
var axios = require("axios").default;
var options = {
method: 'POST',
url: 'https://chaptify.us.auth0.com/oauth/token',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: {
grant_type: 'client_credentials',
client_id: 'KbFUk08Qq24VA03i1mVAkLc3uPKc6V79',
client_secret: process.env.AUTH0_M2M_CLIENT_SECRET,
audience: process.env.AUTH0_AUDIENCE
}
};
axios.request(options).then(function(response) {
console.log(response.data);
}).catch(function(error) {
console.error(error);
});
which is pulled straight from auth0's website. but every time I've gotten a 401 error so far I've tried
using different M2M applications
making sure all the M2M applications are authorized and trying again
checking the Token Endpoint Authentication Method is set to None
checking all the fields are correct
googling to see if I can find absolutely anything to help
pasting in the values directly
unauthorizing the M2M shutting down the application, waiting a while then starting it up again, nothing
I'm getting a 400 error and isAxiosError: true. I think the problem is the auth line is not formatted correctly, or I'm not quite understanding how params work in axios and what's needed by the api? What am I doing wrong in my translating of python to Axios/JS?
Here's the Voila Norbert API documentation.
Here's my Axios api call.
axios.post('https://api.voilanorbert.com/2018-01-08/search/name', {
params: {
auth: {any_string: API_KEY},
data: {
domain: 'amazon.com',
name: 'Jeff Bezos'
}
}
})
Here's the python version:
API_TOKEN = 'abcde'
req = requests.post(
'https://api.voilanorbert.com/2018-01-08/search/name',
auth=('any_string', API_TOKEN),
data = {
'name': 'Cyril Nicodeme',
'domain': 'reflectiv.net'
}
)
I am a year late with this answer but I found your question while dealing with this same error with the same API. The API documentation's suggested Python code works for me with a successful response, but I want to do it in Node and the JS code returns a 400 error. I'm sharing my solution in case it helps others in the future.
I believe the core issue is that the API expects the data to be posted as form data, not as JSON. I followed an example in another post to post form data with Axios but still was receiving a 400 error.
Then I tried posting it using the request package instead of Axios, and that results in a successful response (no error):
const request = require('request');
var data = {'name': 'Jeff Bezos', 'domain': 'amazon.com'};
request.post({
url: 'https://any_string:API_KEY#api.voilanorbert.com/2018-01-08/search/name',
form: data,
}, function(error, response, body){
console.log(body);
});
This example works when the data is included in the form: field but does not work when it is in body:
Please note the request package is deprecated and in maintenance mode.
According to their documentation, https://github.com/axios/axios, you need to give auth as a separate field, not inside params:
axios.post('https://api.voilanorbert.com/2018-01-08/search/name', {
auth: {
username: 'any_string',
password: API_KEY
},
data: {
domain: 'amazon.com',
name: 'Jeff Bezos'
}
})
Updated: removed the nesting of data in params. They should be sent as POST body, not URL params.
I am really struggling to get a successful response when doing a post request to the google recaptcha api. I am receiving the following response:
{
"success": false,
"error-codes": [
"invalid-input-response",
"invalid-input-secret"
]
}
I had a look at reCAPTCHA - error-codes: 'missing-input-response', 'missing-input-secret' when verifying user's response (missing details on POST) and followed the answer as closely as possible but with no success.
Here is my file below:
var request = require('request');
module.exports = {
verifyCaptcha: function(req, res) {
var secret = 'SECRET_KEY';
var response = JSON.stringify(req.body.response);
request({
url: 'https://www.google.com/recaptcha/api/siteverify',
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${secret}&response=${response}`,
}, function (err, response, body) {
if (err) {
res.status(500).send({
error: "Could not verify captcha"
});
} else {
res.status(200).send({
message: body
});
}
});
},
}
If anyone has a solution to this problem please let me know!
Due to the docs: https://developers.google.com/recaptcha/docs/verify
invalid-input-secret: The secret parameter is invalid or malformed.
Maybe you have mixed the site_key and the secret_key.
You need to add the user remote IP address.
var user_ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;
request({
url: 'https://www.google.com/recaptcha/api/siteverify',
method: 'POST',
headers: { "Content-Type": "application/x-www-form-urlencoded" },
body: `secret=${secret}&response=${response}&remoteip=${user_ip}`}...
Another thing I see that you are not using template literal, you should change the quotes to ` instead of '.
OR, You should use a ready-made module for reCaptcha, like this one:
https://www.npmjs.com/package/recaptcha
For reCAPTCHA Enterprise, check the official docs: https://cloud.google.com/recaptcha-enterprise/docs/create-assessment.
In short, you need to use the library that Google provides:
const { RecaptchaEnterpriseServiceClient } =
require('#google-cloud/recaptcha-enterprise');
const client = new RecaptchaEnterpriseServiceClient();
const [ response ] = await client.createAssessment({...});
RecaptchaEnterpriseServiceClient requires a service account to be created beforehand as described here. The key for that account with the right roles set can then be read by the app. Check the arguments of the constructor to see the available options to pass the data if the file cannot be retrieved automatically.
var response = JSON.stringify(req.body.response);
The stringifying here is probably the cause of the invalid-input-response error.
If your body is something like {"g-recaptcha-response": "..."}, you need to pull out the response value and pass that directly in your post.
Regarding invalid-input-secret, if you have set up your key and secret through the classic interface at https://www.google.com/u/1/recaptcha/admin/create, then you shouldn't have a problem.However if you set up a key with recaptcha Enterprise on Google Cloud, then it requires that you do Oauth authentication to the Google Cloud API and then use the create.assessment endpoint to get back information on the validity of the user. As Yuuhn implied, the Google provided library makes interaction with recaptcha Enterprise easier, without a lot of documentation digging to find where your REST API calls need to go.
I'm using bell and hapijs and trying to get the office365 provider to work, but it seems like the https://login.microsoftonline.com/common/oauth2/v2.0/token endpoint isn't giving me the access_token required for getting profile information.
This is the OAuth 2.0 flow I'm seeing:
First it redirects to
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id=[client-id]
&response_type=code
&redirect_uri=http%3A%2F%2Flocalhost%3A5430%2Fapi%2Fv1%2Flogin%2Fazure-ad
&state=[state]
&scope=openid%20offline_access%20profile
in oauth.js#L197
After a successful sign in from the Microsoft login, it redirects to the server and bell does a POST to https://login.microsoftonline.com/common/oauth2/v2.0/token with payload
{
payload: 'grant_type=authorization_code&code=[code]&redirect_uri=http%3A%2F%2Flocalhost%3A5430%2Fapi%2Fv1%2Flogin%2Fazure-ad&client_id=[client-id]&client_secret=[client-secret]',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
in oauth.js#L242
This in turn gives me the following response
{
"refresh_token": "MCTrMmd...",
"id_token": "eyJ0eXAiOiJKV..."
}
From the OAuth 2.0 Authorization Code Flow documentation, it seems like I should be getting something more like
{
"access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6Ik5HVEZ2ZEstZnl0aEV1Q...",
"token_type": "Bearer",
"expires_in": 3599,
"scope": "https%3A%2F%2Fgraph.microsoft.com%2Fmail.read",
"refresh_token": "AwABAAAAvPM1KaPlrEqdFSBzjqfTGAMxZGUTdM0t4B4...",
"id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJhdWQiOiIyZDRkMTFhMi1mODE0LTQ2YTctOD...",
}
Specifically, I need an access_token, because the following profile get request (oauth.js#L270) requires it for
Authorization: 'Bearer ' + payload.access_token
Even in Calling /token endpoint does not give me an access_token, it seems like the /token request gets more fields in the response.
Is there something I'm missing in the request?
Looking at your first request, it doesn't have the response_mode=query header, in contrast to the documentation:
In which it also states the expected successful response:
UPDATE: I was able to replicate this when I don't include the scope in the payload when trying to get the token:
Including the scope in the payload returns the access_token:
The scopes openid, email, profile, and offline_access don't seem to return an access_token.
Adding the User.Read scope does provide an access_token.
For bell, you would need something like:
server.auth.strategy('office365', 'bell', {
provider: 'office365',
scope: [
'User.Read'
],
password: 'something',
clientId: 'clientId',
clientSecret: 'clientSecret',
});
Although, there are still issues with the endpoint that bell's office365 provider has, documented here: How do I get the logged in users profile for Azure AD OAuth logins?
Figured this out from the investigation in https://stackoverflow.com/a/49424859/111884.
I'm trying to get an OAuth token for the Reddit API following the Application Only OAuth instructions. My reddit app is an installed app, so for my grant_type I'm using https://oauth.reddit.com/grants/installed_client.
Currently I'm running a very short JS script to query the API and get a token:
const APP_ID = 'MY_APP_ID'
const DEVICE_ID = 'TRACKING_ID_20_TO_30_CHARS'
let form = new FormData()
form.append('grant_type', 'https://oauth.reddit.com/grants/installed_client')
form.append('device_id', DEVICE_ID)
fetch('https://www.reddit.com/api/v1/access_token', {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': `Basic ${btoa(`${APP_ID}:`)}`,
}),
body: form })
.then(handleResponse)
.then(function(data) {
console.log(data)
})
.catch(error => console.error(error))
function handleResponse(response) {
return response.json()
}
(Note: running the snippet as-is will give you a NetworkError because the APP_ID isn't a real one and I don't want to give mine out.)
The response I get is:
{
"error": "unsupported_grant_type"
}
When I try the same API request using a REST client I get back the expected response, so this makes me think that the problem is JavaScript-related. Since the grant_type matches what the instructions say I'm not really sure what to do with the error. I'm hoping someone else more experienced with OAuth will know what is going on here.
The problem was the use of the FormData object. In earlier stages of troubleshooting I found this answer on Reddit and decided to use it, but that didn't work for me.
It was submitting the data as multipart/form-data rather than application/x-www-form-urlencoded, which Reddit's OAuth server did not like. I wrote a helper function based on this answer which did the trick:
function urlEncode(data) {
let out = [];
for (let key in data) {
out.push(`${key}=${encodeURIComponent(data[key])}`);
}
return out.join('&')
}