Coinbase Connect (OAuth2) "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed." - node.js

Dear stackoverflowser,
I'm stying to build an Electron app with Coinbase intergration.
First I'm going to make the server (NodeJS) work with OAuth2.
Every thing works great, but when I want to change the code into an access token with the instructed post request it gives me the following error:
{
error: "invalid_request",
error_description: "The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed."
}
I already added https://localhost:3000/auth/coinbase/callback and https://localhost:3000/profile to the valid API URI's.
I wasn't successful in figuring it out after several hours.
My server is this:
var express = require('express');
var app = express();
var fs = require('fs')
var https = require('https');
var coinbase = require('coinbase')
var request = require('request');
var options = {
key: fs.readFileSync('./ssl/coinbase.dev.key'),
cert: fs.readFileSync('./ssl/coinbase.dev.crt'),
};
var client_id = 'gues it'
var client_secret = 'gues it'
app.use(express.static('static'));
app.get('/login/coinbase', function(req, res) {
res.redirect('https://www.coinbase.com/oauth/authorize?response_type=code&redirect_uri=https://localhost:3000/auth/coinbase/callback&client_id=' + client_id + '&scope=wallet:user:read,wallet:accounts:read')
})
app.get('/auth/coinbase/callback', function(req, res) {
var data = {
client_id: client_id,
client_secret: client_secret,
grant_type: 'authorization_code',
code: req.query.code,
redirect_uri: 'https://localhost:3000/profile'
}
request.post(
'https://api.coinbase.com/oauth/token', data, function (error, response, body) {
console.log(body)
res.send(body)
}
);
})
app.get('/', function(req, res) {
res.send('home')
})
app.get('/profile', function(req, res) {
res.send('profile')
})
var server = https.createServer(options, app);
server.listen(3000)
Thanks in advance,
Theo
[EDIT]
I contacted the Coinbase developers and they were surprised that there was no NodeJS example on OAuth with Coinbase, so they added it to their roadmap.

This is most likely caused by one of the following:
You do not have 'http://127.0.0.1:3000/profile' listed as a valid redirect API in your application settings.
You are reusing an authorization code that has already been exchanged for a token.
This section of this page:
OAuth2 Redirect URI
For added security, all redirect_uris must use SSL (i.e. begin with
https://). URIs without SSL can only be used for development and
testing and will not be supported in production.
Contact api#coinbase.com to get that sorted out.

Related

Slack OAuth HTTP Post Request returns "undefined" access token

I'm using the following code in a simple slash command app to handle OAuth for public distribution of my app:
const express = require("express");
const bodyParser = require("body-parser");
const fetch = require("node-fetch")
require('dotenv').config();
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
// App installation handling
app.get("/auth", async (req, res) => {
if (!req.query.code) {
console.log("Access denied!");
return;
}
var data = {form: {
client_id: process.env.SLACK_CLIENT_ID,
client_secret: process.env.SLACK_CLIENT_SECRET,
code: req.query.code,
redirect_uri: "https://6c0c-35-20-201-50.ngrok.io/auth"
}};
console.log(req.query.code);
// Send received code back to Slack and get Oauth2 access token
const config = {
method: "POST",
body: data,
headers: {'Content-type': 'application/x-www-form-urlencoded'}
};
console.log("We got something!");
try {
const slack_oauth_response = await fetch("https://slack.com/api/oauth.v2.access", config);
console.log("Access token granted!");
console.log(JSON.stringify(slack_oauth_response.access_token));
} catch (e) {
console.error(e);
}
res.sendStatus(200);
})
When I try using the Add to Slack button, I get a timeout error. My log results will look like this:
PS D:\Documents\SlackRegApp> node local_testing.js
1007612862405.3292595223126.481b3e25d2c29dc80af7dc21bcb84a8bc19c28ddec155a429c6651105903902f
We got something!
Access token granted!
undefined // where the access token should be
If I go ahead and just log the entirety of slack_oauth_response, it looks like this:
{"size":0, "timeout":0}
When I try to install the app via cURL, it works, like below:
curl -F code=1007612862405.3292595223126.481b3e25d2c29dc80af7dc21bcb84a8bc19c28ddec155a429c6651105903902f -F client_id=**SLACK_CLIENT_ID** -F client_secret=**SLACK_CLIENT_SECRET** https://slack.com/api/oauth.v2.access
Hoping for some help here, thanks!
I just used the Slack WebClient API to access the oauth.v2.access method instead of trying to make my own HTTP request.

Oauth 2.0 redirect to app after auth on server

I'm very new to web development, and am trying to implement authorization through a 3rd party for my app. Since I'm testing, the app as well as the server are both hosted locally. I have Oauth2.0 set up to authorize with github so that when I access the server (port 2), it asks for github auth then redirects to the app on port 1 with a code (the token is still null). However, if I go directly to the app hosted on port 1, I do not need to authenticate and there is no code given. Ideally, when the user goes to the app URL, they are redirected to github authentication, then back to the app.
I do not need to use the github API, I only want to use the github account login to authenticate and access the app.
API/server: localhost:3002
App: localhost:3001
const clientId = '...'
const clientSecret = '...'
const redirectUri = 'localhost:3001'
app.get('/', (req, res) => {
console.log('authorizing client through github...')
res.redirect(`https://github.com/login/oauth/authorize?client_id=${clientId}`);
});
...
const axios = require('axios');
let token = null;
app.get('/oauth-callback', (req, res) => {
const body = {
client_id: clientId,
client_secret: clientSecret,
redirect_uri: redirectUri,
code: req.query.code
};
const opts = { headers: { accept: 'application/json' } };
axios.post(`https://github.com/login/oauth/access_token`, body, opts).
then(res => res.data['access_token']).
then(_token => {
console.log('My token:', token);
token = _token;
res.json({ ok: 1 });
}).
catch(err => res.status(500).json({ message: err.message }));
});
Apologies if this is a simple question, the Oauth docs aren't very helpful.

Spotify API Authorization redirects too many times

I'm trying to use the Spotify API and following their instructions on authorization found here: https://github.com/spotify/web-api-auth-examples/blob/master/authorization_code/app.js.
Their version of the Authorization code directly uses routes in the server code, but I wanted to separate the Authorization into its own route. Here is my version of the code:
const authRouter = require("express").Router();
const config = require("../utils/config");
const request = require("request");
const querystring = require("querystring");
// Spotify client configurations
const client_id = config.CLIENT_ID;
const client_secret = config.CLIENT_SECRET;
const redirect_uri = config.REDIRECT_URI;
const stateKey = "spotify_auth_state";
//...
// #route GET /
// #desc Prompt User to Login into Spotify
authRouter.get("/", async (req, res) => {
try {
var state = generateRandomString(16);
res.cookie(stateKey, state);
// Request for user full name, profile image, and email address.
var scope = "user-read-private user-read-email";
// 1. Get the user's authorization to access data.
res.redirect(
"https://accounts.spotify.com/authorize?" +
querystring.stringify({
response_type: "code",
client_id: client_id,
scope: scope,
redirect_uri: redirect_uri,
state: state,
})
);
} catch {
console.log("error.");
}
});
// #route GET /callback
// #desc Spotify callback to request access and refresh tokens
authRouter.get("/callback", async (req, res) => {
try {
var code = req.query.code || null; // The authorization code returned by the first call.
var state = req.query.state || null;
var storedState = req.cookies ? req.cookies[stateKey] : null;
// Check the state parameter
if (state === null || state !== storedState) {
res.redirect(
"/#" +
querystring.stringify({
error: "state_mismatch",
})
);
} else {
res.clearCookie(stateKey);
const authOptions = getAuthOptions(
code,
redirect_uri,
client_id,
client_secret
);
// 2. Request an access token and refresh token
request.post(authOptions, function (error, response, body) {
if (!error && response.statusCode === 200) {
// Authorize successful. Access and Refresh Tokens granted.
var access_token = body.access_token,
refresh_token = body.refresh_token;
// Send the tokens to the client so the client can use them in requests to the Spotify API.
res.redirect(
"/#" +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token,
})
);
} else {
res.redirect(
"/#" +
querystring.stringify({
error: "invalid_token",
})
);
}
});
}
} catch {
console.log("error.");
}
});
module.exports = authRouter;
And in my app.js:
const express = require("express");
const authRouter = require("./controllers/auth");
const cors = require("cors");
var cookieParser = require("cookie-parser");
// initialize app with Express to create client to communicate with Spotify
const app = express();
app.use(cors());
app.use(cookieParser());
app.use("/", authRouter);
module.exports = app;
Now when I start my server, my browser returns: "accounts.spotify.com redirected you too many times.". When I tried starting my server in incognito mode, the Spotify login prompt appears. After I enter my credentials, it returns: "accounts.spotify.com redirected you too many times."
I've tried clearing my cookies and caches but that does not work.
In addition, I've confirmed my redirect URI for my server is the same as my redirect URI in my Spotify application's settings.
What can be the reasons the auth seems to be stuck in an infinite loop?
What's causing the infinite loop is where the code sends the access and refresh tokens back to the client:
// Send the tokens to the client so the client can use them in requests to the Spotify API.
res.redirect(
"/#" +
querystring.stringify({
access_token: access_token,
refresh_token: refresh_token,
})
);
Since I have defined the following route:
authRouter.get("/", async (req, res) => {
The access and refresh tokens are redirected to the login page, which will then lead to the callback which redirects to the login again, creating an infinite loop.
How I solved this was to redirect the access and refresh tokens to a different component, not just to "/#" + query.string... as coded in Spotify's example code.
Spotify's example code does not lead to an infinite loop since they defined a /login route for the login page, but I opted my website's root to be the login page since in my case, authenticating should be the first step.

Dropbox api V2, get access token in query param instead of url hash (#) (Nodejs)

I'm using the official Dropbox API (V2) on my Nodejs app.
It sounds like a dumb question but I really can't find out how to get the given access token from the callback url. Actually, it is supposed to be in the hash (#) part of the url (according to their documentation and javascript client-side exemple), which is not visible by the server side...
I can't find any exemple for authentication from a nodejs app, using only the basic api.
Here is my authentication code:
My express app:
//Entry point, DC is a DropboxConnector object
app.get('/connect/Dropbox', function(req, res) {
console.log('/connect/Dropbox called');
res.redirect(DC.getConnexionURL());
});
// Callback from the authentication
app.get('/authDropbox', function(req, res) {
console.log("/authDropbox called");
console.log(url.format(req.protocol + '://' + req.get('host') + req.originalUrl));
// The above log is: 'http://localhost:8080/authDropbox'
// Here is the problem, the access token is unreachable by express
DC.getToken(req.query.code, res);
connectorList.push(DC);
});
DropboxConnector.js, my dropbox api wrapper:
var REDIRECT_URI = 'http://localhost:8080/authDropbox';
//The authentication url given by the dropbox api
getConnexionURL() {
dbx = new Dropbox({ clientId: CLIENT_ID});
var authUrl = dbx.getAuthenticationUrl(REDIRECT_URI);
console.log("AuthURL: " + authUrl);
return authUrl;
}
// #param code is supposed to be the access token...
getToken(code, res) {
if (!!code) {
dbx = new Dropbox({ accessToken: code });
console.log("Authenticated!");
res.redirect(CALLBACK_URL);
} else {
console.log("No code here");
}
}
Thanks for help !
That's correct, the contents of the fragment a.k.a. hash are not visible to the server, only the client (browser). The OAuth 2 "token" flow sends the access token on the fragment, and is mainly meant for client-side apps, e.g., JavaScript in the browser. The OAuth 2 "code" flow instead sends an authorization code as a URL parameter, for server-side apps.
If you're interested, you can find more information on the two different flows in the Dropbox /oauth2/authorize documentation.
The Dropbox API v2 JavaScript SDK unfortunately currently only supports the "token" flow, but we're tracking this as a feature request for support for the "code" flow.
If you do not want to call HTTP directly, you can use my tiny dropbox-v2-api wrapper package:
const dropboxV2Api = require(dropbox-v2-api');
const dropbox = dropboxV2Api.authenticate({
client_id: 'APP_KEY',
client_secret: 'APP_SECRET',
redirect_uri: 'REDIRECT_URI'
});
//generate and visit authorization sevice
const authUrl = dropbox.generateAuthUrl();
//after redirection, you should receive code
dropbox.getToken(code, (err, response) => {
//you are authorized now!
});
Full example (see here):
const dropboxV2Api = require(dropbox-v2-api');
const Hapi = require('hapi');
const fs = require('fs');
const path = require('path');
const Opn = require('opn');
const credentials = JSON.parse(fs.readFileSync(path.join(__dirname, 'credentials.json')));
//set auth credentials
const dropbox = dropboxV2Api.authenticate({
client_id: credentials.APP_KEY,
client_secret: credentials.APP_SECRET,
redirect_uri: 'http://localhost:5000/oauth'
});
//prepare server & oauth2 response callback
const server = new Hapi.Server();
server.connection({ port: 5000 });
server.route({
method: 'GET',
path: '/oauth',
handler: function (request, reply) {
var params = request.query;
dropbox.getToken(params.code, function(err, response){
console.log('user\'s access_token: ',response.access_token);
//call api
dropbox({
resource: 'users/get_current_account'
}, function(err, response){
reply({response: response});
});
});
}
});
server.start(function(){
//open authorization url
Opn(dropbox.generateAuthUrl());
});

Using node js restify and restify-oauth2

I'm beginner in developing Web ​​Services, I have a server with node js and module restify, and I want to add authentication using module OAuth2-restify. But as I struggled to prepare a request for get token.I use node restify client for make request.Please somebody can provide me some example. The documentation say
If a valid token is supplied in the Authorization header, req.username is truthy and
The token endpoint, managed entirely by Restify–OAuth2. It generates tokens for a given client ID/client secret/username/password combination.
But how I get a token?? here is the documentacion https://github.com/domenic/restify-oauth2
When I test a sevice width a restify-client I get this error
{"error":"invalid_request","error_description":"Must supply a body."}
here is my code:
--------------------------------------Server--------------------------------
var SERVER_PORT = 8800;
var restify = require('restify');
var server = restify.createServer({
name: "Example Restify-OAuth2 Client Credentials Server",
//version: require("../../package.json").version,
formatters: {
"application/hal+json": function (req, res, body) {
return res.formatters["application/json"](req, res, body);
}
}
});
server.use(restify.authorizationParser());
server.use(restify.queryParser());
server.use(restify.bodyParser({ mapParams: false }));
var restifyOAuth2 = require("restify-oauth2");
var hooks = require("./hooks");
restifyOAuth2.cc(server, { tokenEndpoint:"/token", hooks: hooks });
//server.use(restify.acceptParser(server.acceptable));
var handlers = require('./handlers');
handlers.setHandlers(server);
server.listen(SERVER_PORT);
--------------------------------------handlers--------------------------------
module.exports = {
setHandlers: function(server)
{
var restify = require('restify');
var token=function(req,res,next) {
owner=req.body.owner;
password=req.password.owner;
if(req.username)
res.send({result:"sucess"});
}
server.get("/token",token);
}
}
-----------------------------client restify for test services--------------------------------
var restify = require('restify');
var client = restify.createJsonClient({
url: 'http://localhost:8800',
version: '*'
});
client.post('/token', { client_id: 'officialApiClient',client_secret: officialApiClient'}, function(err, req, res, obj) {
//assert.ifError(err);
//console.log('%d -> %j', res.statusCode, res.headers);
console.log('%j', obj);
});
Try adding a POST body to your request with the following parameters:
grant_type = client_credentials
You have send a POST request to the "token" endpoint using Basic Authentication and as Jordy said add grant_type parameter:
POST /oauth/token
Authorization: Basic Y2xpZW50SWQ6c2VjcmV0
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=client_credentials

Resources