I'm creating a Discord login, but when I get redirected, I get the error. The error happens after I press authorize on the Discord OAuth page
{"status":"ERROR","error":"Response code 400 (Bad Request)"}
Here is my code for Discord.js
const express = require('express');
const router = express.Router();
const fetch = require('got');
const btoa = require('btoa');
const { catchAsync } = require('../utils');
const CLIENT_ID = '#';
const CLIENT_SECRET = '#';
const redirect = encodeURIComponent('https://ezapplications.live/api/discord/callback');
router.get('/callback', catchAsync(async (req, res) => {
if (!req.query.code) throw new Error('NoCodeProvided');
const response = await fetch(`https://discordapp.com/api/oauth2/authorize?client_id=${CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirect}`, {
method: 'POST',
body: new URLSearchParams({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "authorization_code",
redirect_uri: "https://ezapplications.live/api/discord/callback",
code: req.query.code
})
});
const json = await response.json();
res.redirect(`/?token=${json.access_token}`);
}));
router.get('/login', (req, res) => {
res.redirect(`https://discordapp.com/api/oauth2/authorize?client_id=${CLIENT_ID}&scope=identify&response_type=code&redirect_uri=${redirect}`);
});
module.exports = router;
Here is my code for Server.js
const express = require('express');
const path = require('path');
const app = express();
app.get('/', (req, res) => {
res.status(200).sendFile(path.join(__dirname, 'index.html'));
});
app.use('/api/discord', require('./api/discord'));
app.use((err, req, res, next) => {
switch (err.message) {
case 'NoCodeProvided':
return res.status(400).send({
status: 'ERROR',
error: err.message,
});
default:
return res.status(500).send({
status: 'ERROR',
error: err.message,
});
}
});
app.listen(50451, () => {
console.info('Running on port 50451');
});
Here is the code for utils.js
// async/await error catcher
const catchAsyncErrors = fn => (
(req, res, next) => {
const routePromise = fn(req, res, next);
if (routePromise.catch) {
routePromise.catch(err => next(err));
}
}
);
exports.catchAsync = catchAsyncErrors;
I'm not sure why I get this error as everything seems fine to me, thanks.
As per the Authorization Code Grant docs, you need to pass the parameters in the request body instead of the query string.
const response = await fetch("https://discord.com/api/oauth2/token", {
method: 'POST',
headers: {
"content-type": "application/x-www-form-urlencoded"
},
body: new URLSearchParams({
client_id: CLIENT_ID,
client_secret: CLIENT_SECRET,
grant_type: "authorization_code",
redirect_uri: "https://ezapplications.live/api/discord/callback",
code: req.query.code
}).toString()
});
Regarding HTTP Basic auth...
You can also pass your client_id and client_secret as basic authentication with client_id as the username and client_secret as the password
...but I don't really see the point. Your choice
Related
When I make a post request to the /login endpoint in postman it works fine and returns all the information. However when I try to navigate to the end point in the url the route returns unfound. In the console I get GET http://localhost:5000/login 404 (Not Found). Why is the console returning for a get request? If I try to call the post request in axios I get xhr.js:177 POST http://localhost:3000/login 404 (Not Found).
app.js
require("dotenv").config();
const express = require('express');
const morgan = require('morgan');
const bodyParser = require('body-parser');
const router = express.Router();
const cors = require('cors');
const app = express();
app.use(cors())
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: true
}));
const mongoose = require('mongoose');
const connection = "password"
mongoose.connect(connection, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
const clientRoutes = require('./routes/clientRoutes');
const traderRoutes = require('./routes/traderRoutes');
const loginRoute = require('./routes/loginRoute')
app.use('/', clientRoutes, traderRoutes, loginRoute);
// setup a friendly greeting for the root route
app.get('/', (req, res) => {
res.json({
message: 'Welcome to the REST API for Pave!',
});
});
// send 404 if no other route matched
app.use((req, res) => {
res.status(404).json({
message: 'Route Not Found',
});
});
// setup a global error handler
app.use((err, req, res, next) => {
if (enableGlobalErrorLogging) {
console.error(`Global error handler: ${JSON.stringify(err.stack)}`);
}
res.status(err.status || 500).json({
message: err.message,
error: {},
});
});
app.listen(5000, () => console.log('Listening on port 5000!'))
loginRoute.js
require("dotenv").config();
const express = require("express");
const router = express.Router();
const jwt = require("jsonwebtoken");
const bcryptjs = require("bcryptjs");
const Client = require("../models/clientSchema");
const Trader = require("../models/traderSchema");
function asyncHandler(callback) {
return async (req, res, next) => {
try {
await callback(req, res, next);
} catch (error) {
next(error);
console.log(error);
}
};
}
router.post('/login', asyncHandler(async (req, res, next) => {
let user = req.body;
const trader = await Trader.findOne({ emailAddress: req.body.emailAddress })
if (user && trader) {
console.log(trader)
let traderAuthenticated = await bcryptjs.compareSync(user.password, trader.password);
console.log(traderAuthenticated)
if (traderAuthenticated) {
console.log('Trader match')
const accessToken = jwt.sign(trader.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/trader');
res.json({
trader: trader,
accessToken: accessToken
}).end();
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else if (user && !trader) {
const client = await Client.findOne({emailAddress: req.body.emailAddress})
console.log(client)
let clientAuthenticated = await bcryptjs.compareSync(user.password, client.password);
console.log(clientAuthenticated)
if (clientAuthenticated) {
console.log('Client match')
const accessToken = jwt.sign(client.toJSON(), process.env.ACCESS_TOKEN_SECRET)
res.location('/client');
res.json({
client: client,
accessToken: accessToken
});
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
} else {
res.status(403).send({ error: 'Login failed: Please try again'}).end();
}
})
);
module.exports = router;
You set POSTMAN to make a POST request, right? When you enter a url in the browser, that causes a GET request - and you have no route to manage this that I can see, but for the default Not found.
you are calling with axios with wrong port no. it should, POST method http://localhost:5000/login as your application is running on port 5000.
but you are calling, POST http://localhost:3000/login
I'm testing the server using postman and everything works fine, in the sense that i get the answer:
But if I making post request from the browser to the same address it throws an error and the answer is undefined
Postman has the following headers:
How do I send a post request correctly to get a response?
Main file (App.js):
const express = require('express');
const config = require('config');
const mongoose = require('mongoose');
const cors = require('cors');
const app = express();
const corsOptions = {
origin: config.get('CORS.whiteList'),
optionsSuccessStatus: config.get('CORS.optionsSuccessStatus')
}
app.use(cors(corsOptions));
app.use('/api/auth', require('./routes/auth.routes'));
const PORT = config.get('PORT') || 5000;
async function startServer() {
try {
await mongoose.connect(config.get('mongoUri'), {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true
});
app.listen(PORT, () => console.log(`App has been started on port: ${PORT}`));
} catch (err) {
console.log(`Server error: ${err.message}`);
process.exit(1);
}
}
startServer();
And router:
const { body, validationResult } = require('express-validator');
const User = require('../models/User');
const config = require('config');
const bodyParser = require('body-parser');
const router = express.Router();
const jsonParser = bodyParser.json();
const urlencodedParser = bodyParser.urlencoded({ extended: false });
router.post('/login',
urlencodedParser, [body('email', 'Некоректный email').isEmail()],
async(req, res) => {
try {
const errors = validationResult(req);
if (!errors.isEmpty()) {
console.log(3)
return await res.status(400).json({
errors: errors.array()[0].msg,
message: 'Некорректные данные при регистрации'
})
}
const email = req;
console.log(email)
const candidate = await User.findOne({ email: email });
console.log(3)
if (candidate) {
return await res.status(400).json({
msg: 'Такой email уже зарегестрирован'
});
}
const user = new User({
email
});
await user.save();
} catch (err) {
console.log(err)
return await res.status(500).json({
msg: 'Что-то пошло не так, попробуйте снова',
err: err.stack
});
}
}
);
module.exports = router;
As I understand it, the problem is in expressValidator.
UPD
I've tryed to use formData, but it doesn't working.
you are expecting x-www-form-encoded, but you are sending json.
you should do this
const onSubmit = () => {
fetch(url,
{ method: 'post',
headers: {
{ /* depending on server, this may not be needed */}
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams({ 'email': 'daw' });
}
}
I wrote a small node.js server with a login system and I am trying to protect my routes. I have created the middleware that should check authentication on each protected route, but it seems that I am not sending the JWT token correctly, because every time I log in I get the Authentication failed message. How can I send the JWT token correctly and log in if the password and username are correct? Here is my Node.js server:
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
require('dotenv').config();
const PORT = process.env.PORT || 1337;
const jwt = require('jsonwebtoken');
const checkAuth = require('./middleware/check-auth.js')
let Post = require('./models/post.model.js');
app.use(cors());
app.use("/assets", express.static(__dirname + "/assets"));
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view-engine', 'ejs');
app.get('/', (req, res) => {
res.render('index.ejs');
});
app.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const token = jwt.sign({
username: username,
}, process.env.SECRET_KEY, {
expiresIn: '1h'
});
res.redirect(`/dashboard?token=${token}`);
}
});
app.get('/dashboard', checkAuth, (req, res) => {
res.render('dashboard.ejs');
});
app.get('/dashboard/createPost', checkAuth, (req, res) => {
res.render('post.ejs');
});
app.post('/dashboard/createPost', async (req, res) => {
let collection = connection.collection(process.env.POSTS_WITH_TAGS);
res.setHeader('Content-Type', 'application/json');
let post = new Post(req.body);
collection.insertOne(post)
.then(post => {
res.redirect('/dashboard')
})
.catch(err => {
res.status(400).send(err);
});
});
app.listen(PORT);
and here is my check-auth middleware:
const jwt = require('jsonwebtoken');
module.exports = (req, res, next) => {
try {
const token = req.headers.authorization.split(' ')[1];
console.log(token);
const decoded = jwt.verify(token, process.env.SECRET_KEY, null);
req.body.decoded = decoded;
console.log(req.body.decoded);
} catch (error) {
return res.status(401).json({
message: 'Authentication failed'
});
}
next();
};
Use Javascript fetch API for sending JWT token as header authorization
fetch('backend_domain/dashboard', {
method: 'get',
headers: {
Authorization: JWT_Token
}
}).then(data => {..your operation here..})
Reference: fetch_mdn for better understanding of fetch API
I think the problem here is that you are getting an empty object in req.body when you try to pass it as a json string in postman. I would recommend either
pass your credentials in x-www-form-urlencoded tag in postman or
Using express.Router() and creating different files for routes
In your index.js file write:
app.use('/', require('./routes.js'))
And create a file with name routes.js and put your routes as this:
const express = require('express');
const router = express.Router();
router.post('/', (req, res) => {
let username = req.body.username;
let password = req.body.password;
console.log(req.body);
if (username !== process.env.USER_NAME && password !== process.env.USER_PASSWORD) {
res.json('Invalid credentials');
} else {
const token = jwt.sign({
username: username
}, process.env.SECRET_KEY, {
expiresIn: 3600
});
return res.send({token});
}
});
module.exports = router;
With the following code Im trying to create a accessToken with the simple-oauth2 library in node.
I have the server.js and the app.js files. The problem is that every time I try to call the getToken method it returns the following error The content-type is not JSON compatible. Im calling the /token endpoint with postman where the request´s Content-Type is set to application/json.
Has anyone encountered this problem before?
server.js
'use strict';
const app = require('express')()
const bodyParser = require('body-parser')
app.use(bodyParser.json())
const port = 3000;
module.exports = (cb) => {
app.listen(port, (err) => {
if (err) return console.error(err);
console.log(`Express server listening at http://localhost:${port}`);
return cb({
app
});
});
};
app.js
const createApplication = require('./server');
const simpleOauthModule = require('simple-oauth2');
require('dotenv').config()
const credentials = {
client: {
id: process.env.CLIENT_ID,
secret: process.env.CLIENT_SECRET
},
auth: {
tokenHost: 'http://localhost:3000/test'
}
};
createApplication(({ app }) => {
app.post('/token', async (req, res) => {
const oauth2 = simpleOauthModule.create(credentials);
var contype = req.headers['content-type']; // <-- application/json
const tokenConfig = {
username: "test",
password: "1234"
};
try {
const result = await oauth2.ownerPassword.getToken(tokenConfig); //Error occuress
const accessToken = oauth2.accessToken.create(result);
console.log('Access Token: ', accessToken);
} catch (error) {
console.log('Access Token Error', error.message);
}
});
app.post('/test', async (req, res) => {
});
});
I am not exactly sure how to go about using/accessing the data requested from a discord oauth2 authentication. I have requested to access the guilds the user is in and the username and avatar of the user. I get a successful authentication, but my question is how do i use and access that data? This is my code currently:
server.js
const express = require('express');
const path = require('path');
const app = express();
app.use('/static', express.static(path.join(__dirname, 'static')));
app.get('/', (req, res) => {
res.status(200).sendFile(path.join(__dirname, 'index.html'));
});
app.listen(50451, () => {
console.info('Running on port 50451');
});
app.use('/api/discord', require('./api/discord'));
app.use((err, req, res, next) => {
switch (err.message) {
case 'NoCodeProvided':
return res.status(400).send({
status: 'ERROR',
error: err.message,
});
default:
return res.status(500).send({
status: 'ERROR',
error: err.message,
});
}
});
discord.js
const express = require('express');
const dotenv = require('dotenv').config()
const fetch = require('node-fetch');
const btoa = require('btoa');
const { catchAsync } = require('../utils');
const router = express.Router();
const scopes = ['identify', 'guilds'];
const CLIENT_ID = process.env.CLIENT_ID;
const CLIENT_SECRET = process.env.CLIENT_SECRET;
const redirect =
encodeURIComponent('http://localhost:50451/api/discord/callback');
router.get('/login', (req, res) => {
res.redirect(`https://discordapp.com/api/oauth2/authorize?client_id=${CLIENT_ID}&redirect_uri=${redirect}&response_type=code&scope=identify%20guilds`);
});
router.get('/callback', catchAsync(async (req, res) => {
if (!req.query.code) throw new Error('NoCodeProvided');
const code = req.query.code;
const creds = btoa(`${CLIENT_ID}:${CLIENT_SECRET}`);
const response = await fetch(`https://discordapp.com/api/oauth2/token?grant_type=authorization_code&code=${code}&redirect_uri=${redirect}`,
{
method: 'POST',
headers: {
Authorization: `Basic ${creds}`,
},
});
const json = await response.json();
res.redirect(`/success/?token=${json.access_token}`);
}));
module.exports = router;
Any help will be greatly appreciated. Thanks!
It's almost the same as the way you use the req.query.code to get the access_token.
const fetchDiscordUserInfo = await fetch('http://discordapp.com/api/users/#me', {
headers: {
Authorization: `Bearer ${json.access_token}`,
}
});
const userInfo = await fetchDiscordUserInfo.json();
yourUserId = `${userInfo.id}`;
yourUserName = `${userInfo.username}`;
// or simply...
console.log(userInfo);