Spotify Web Api/Axios/Heroku - POST Request 500 Error - node.js

I was running my React project with a node/axios backend in my localhost with no errors. However, since deploying on Heroku, I can't seem to get my Axios POST requests to work, they give the errors seen below.
The useAuth hook, takes in the code okay from the url when the user signs in, pulling it from the url. But it fails to use the useAuth function/request to get an access token, refresh, and expires token on the axios POST request.
In Spotifys dashboard, my heroku urls are in the redirect uris.
Note: I'm hacking my way through debugging this and have little experience in using node/axios. So I may be unaware of things which may seem obvious and doing things which aren't possible - feel free to point them out.
In the client folder, useAuth.js.
# The parameter code is as required in console
console.log(code)
useEffect(() => {
axios.post('https://heroku-url-inserted-here.com/login', {
code,
})
.then(res => {
setAccessToken(res.data.accessToken)
setRefreshToken(res.data.refreshToken)
setExpiresIn(res.data.expiresIn)
window.history.pushState({}, null, '/')
})
.catch((err) => {
console.log("Error in Effect 1: " + err)
console.log(err.response.data)
})
}, [code])
In server.js,
app.post('/login', (req, res) => {
const code = req.body.code;
const spotifyApi = new spotifyWebApi({
redirectUri: process.env.REDIRECT_URI,
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
})
spotifyApi.authorizationCodeGrant(code).then(data => {
res.json({
accessToken: data.body.access_token,
refreshToken: data.body.refresh_token,
expiresIn: data.body.expires_in
})
}).catch((err) =>{
console.log(err)
res.sendStatus(400)
})
})
Error in console:
Error in Effect 1: AxiosError: Request failed with status code 500
500 (Internal Server Error)
I'm hoping for the /login post to set and return the three tokens to set State, allowing major functionality to then work as it did when using local host.

ANSWER:
I was using a .env in my localhost testing environment but didn't set any config values within Heroku, as the .env is a part of the .gitignore file. This meant the redirectUri, clientId and clientSecret were all undefined in my server.js.
Configuring vars in Heroku sorted this issue: https://devcenter.heroku.com/articles/config-vars

Related

Linkedin Login API returning Status 500: Internal Server Error

been stuck for a while trying to implement LinkedIn Login using the passport framework on nodejs with passport-linkedin-oauth2 strategy.
These are my configurations for the strategy on nodejs
const strategy = new LinkedInStrategy(
{
clientID: LINKEDIN_KEY,
clientSecret: LINKEDIN_SECRET,
callbackURL: BASE_URL + "/auth/linkedin/callback",
scope: ["r_emailaddress", "r_liteprofile"],
},
(
accessToken,
refreshToken,
profile,
done
) => {
process.nextTick(() => {
return done(null, profile);
});
}
)
The callback route defined so:
router.get(
"/auth/linkedin/callback",
passport.authenticate("linkedin",{
successRedirect: "/",
failureRedirect: "/auth/linkedin",
})
);
In the dev environment on http://localhost:3000, this works perfectly fine - image showing redirection success. After a login success, I get redirected to localhost:3000.
In production however, hosted at https://www.(example_website).com, the redirection throws a code 500 internal server error - image showing redirection failure.
I'm lost as to why the issue is happening for the production site. Really appreciate any clues or help to debug this!

Why is 'currentUser' and 'onAuthStateChanged' in firebase always null?

What I want to achieve
A user, who logged in or signed up should not re-login after one hour. The restriction of one hour comes from firebase authentication, if not prevented (what I try to accomplish).
Problem
After a user is logged in via firebase authentication (signInWithEmailAndPassword) I always get null for currentUser and onAuthStateChanged.
What I tried
I'm using React (v17.0.2) using 'Create React App'. On server side I'm using NodeJS (v12). The communication between both is accomplished using axios (v0.21.1)
First I tried to send the token stored in localStorage, which came from firebase (server side), back to the server. But the server tells me, that the token is no longer valid. Server side code as follows:
module.exports = (request, response, next) => {
let idToken;
if (request.headers.authorization && request.headers.authorization.startsWith('Bearer ')) {
idToken = request.headers.authorization.split('Bearer ')[1];
console.log("idToken:", idToken);
} else {
console.error('No token found');
return response.status(403).json({ error: 'Unauthorized' });
}
admin
.auth()
.verifyIdToken(idToken)
.then((decodedToken) => {
console.log('decodedToken', decodedToken);
request.user = decodedToken;
return db.collection('users').where('userId', '==', request.user.uid).limit(1).get();
})
.catch((err) => {
console.error('Error while verifying token', err);
return response.status(403).json(err);
});
};
After that I tried the following code on client side.
handleSubmit = () => {
const userData = {
email: this.state.email,
password: this.state.password
};
axios
.post(firestoreUrl() + '/login', userData)
.then((resp) => {
console.log("token:", resp.data); //here I get a valid token
localStorage.setItem('AuthToken', `Bearer ${resp.data.token}`);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //but this is null
})
.then((data) => {
console.log(data);
console.log("firebase.auth().currentUser:", firebase.auth().currentUser); //still null
})
.catch((error) => {
console.log("Error:", error);
});
};
What irritates me is that I get a token from firebase (server side), the token is then stored in localStorage (client side) but firebase then tells me, that the currentUser is null. But presumably they are not mutually dependent =/.
I'm able to access all secured sites in my app. I can log out and in again. But whatever I do the currentUser is null.
I also tried to run the code above in componentDidMount()-method. But no success.
I tried an approach from this link (hopefully in a way it should be), but it didn't work. Still getting null for both currentUser and onAuthStateChanged if I implement following code.
firebase.auth().onAuthStateChanged(user => {
if (user) {
console.log("state = definitely signed in")
}
else {
console.log("state = definitely signed out")
}
})
I always get logged to the console, that the user is 'definitely signed out'.
During research I noticed that the point at which I should try to get the currentUser-Status is kind of tricky. So I guess that one solution is to implement the currentUser-code at another/the right place. And here I'm struggling =/.
As I found out at a similar question here on SO, I did a bad mistake. Apparently, it's not a good idea to perform the signIn- or createUser-functionality on server side. This should be done on client side. In the question mentioned above are some good reasons for doing that on server side but in my case it's quite ok to run it on client side.
Thanks to Frank van Puffelen for leading the way (see one of the comments in the question mentioned above).

CORS Error in a working setup while trying to authenticate using Google+ API in React/Node setup

I was implementing the Oauth2.0 authentication using Google. I used react-google-login npm on the frontend to authenticate the user using Google Oauth2.0. I successfully created the CLient-id and secret under google cloud platform for my project, along with the URI as needed.
The frontend is running on default localhost:3000 and backend (node/express) running on localhost:9001 with proxy enabled on frontend to redirect the request to backend.
I was able to authenticate using Google more than 2 dozen times last night as i was working on the backend siginIn contoller. I was also able to add the user to my Mongodb after successful authentication from Google.
All of a sudden, i was getting CORS error which is a bit strange as none of the code or Google configs were changed.
My Google config looks as follows.
My code on the frontend is still successfully redirecting the user to Google for authentication. Its also generating the right google credentials.
SignIn Component Code snippet passing the info to responseGoogle which resides in withLogin HOC Parent Component.
<GoogleLogin
clientId={GOOGLE_CLIENT_ID}
buttonText="Google"
render={(renderProps) => (
<button onClick={renderProps.onClick} style={customStyle}>
<img className="googleBtn" src={googleIcon} alt="GMAIL ICON" />
</button>
)}
onSuccess={responseGoogle}
onFailure={responseGoogle}
cookiePolicy={"single_host_origin"}
/>
withLogin HOC Parent Component dispatching the info to Redux thunk.
const responseGoogle = (res) => setGoogleResp(res);
useEffect(() => {
googleResp?.error &&
setValues({ ...values, serverError: "GOOGLE LOGIN FAILED" });
googleResp?.tokenId && dispatchGoogleSignInDataToBackend()
}, [googleResp]);
const dispatchGoogleSignInDataToBackend=async ()=>{
const data=await dispatch(allActions.googleSignInAction(googleResp,whoLoggedIn));
if (data.error) {
setValues({ ...values, serverError: data.error, success: false });
} else {
const {
email,
name,
_id,
role,
listOfEmailOfAllClientsForLawyerLogin,
} = data.userCred;
saveJwtToLocalStorage(
data.token,
{ name, email, _id, role, listOfEmailOfAllClientsForLawyerLogin },
() => {
setValues({
email,
serverError: false,
success: true,
});
}
);
}
}
I am sending the appropriate CORS header in the request to the backend.
export const dataHeaders = {
"Access-Control-Allow-Origin": "*",
"Content-Type": "application/json",
"Access-Control-Allow-Headers" :"*"
};
Redux thunk code:-
export const googleSignInAction=(googleResp,whoLoggedIn)=>{
console.log("Login Success: currentUser:", googleResp);
return async (dispatch) => {
dispatch({ type: SIGNIN_LOADING });
try {
const response = await axios.post(
`${API_URL}/googlesignin`,
{
googleResp,
whoLoggedIn
},
{
headers: dataHeaders,
}
);
console.log("response inside googleSignInAction", response);
// CHANGED COZ OF ESLINT WARNING.
if (
response.status === 201 &&
Object.keys(response.data).includes("token") &&
Object.keys(response.data).includes("userCred")
) {
dispatch({ type: SIGNIN_SUCCESS, data: response.data });
return response.data;
} else {
dispatch({ type: SIGNIN_FAILED });
}
} catch (error) {
dispatch({ type: SIGNIN_FAILED });
return error.response.data;
}
};
}
API URL Points to following:-
export const API_URL="http://localhost:9001/api";
No request is reaching the backend because of CORS error.
Frontend receiving the Correct Response from Google Post authentication.
Errors on the Frontend.
Browsers will first send a pre-flight request to check CORS. In your backend code, you have to allow the front-end host and port. In this case localhost:3000.
The reason you are getting the cors error is bacause its on two different ports.
But if proper cors response is given by backend (port 9000), it will resolve.
Clearing the browser cookies and cache made everything work again. googlesignin is working without cors error. I have added following line of code to serve all static files from backend to frontend.
app.use(express.static(path.join(__dirname, '../frontend/public')));

Maintaining express sessions when moving between static and dynamic pages on client side

I am trying to set up express sessions for user authentication.
I have a node.js backend, and angular and static page front end (two front ends).
My node backend accepts username and password for authentication on my route
as a post request http://localhost:3000/users/login
My journey is as follows:
1. User is presented with a static front end login page, (this is designed by injecting vue.js with axios on a static html page), wherein he requests a log in with his credentials. The client frontend is hosted on http://localhost:3011/
2. The application will now send a post request to http://localhost:3000/users/login to verify the credentials.
3. If the credentials are valid, the server will create a new session (express-session) using req.session. In this session, I also store my token that I use to authenticate the user for any subsequent requests (this token is saved in the db and verified for all further requests). The server will then respond with a 200 OKAY status.
4. Once the vue application gets a positive response, the client will redirect the application to http://localhost:3000/ where I have angular application in the dist folder.
If, in POSTMAN, I do a post to http://localhost:3000/users/login and then do a GET on http://localhost:3000/, I can see the token in the logs when the server responds to the GET request.
But, if i send a post request to http://localhost:3000/users/login from my vue application, and then redirect to http://localhost:3000/ on successful authentication, I cannot see the token in the logs.
Code Snippet on the client side (Vue)
submit() {
this.errMsg = "";
axios
.post("http://localhost:3000/users/login", {
user: this.user,
password: this.password
})
.then(response => {
window.location = "http://localhost:3000/";
})
.catch(err => {
console.log(err);
this.errMsg = err.data.statusText;
});
}
Code inside login on server side
router.route("/login").post((req, res) => {
let { user, password } = req.body;
merchant_table
.findOne({ attributes: ["id"], where: { email, password } })
.then(result => {
if (result) {
let token = 12345;
token_table
.create({ m_id: result.id, token })
.then(result => {
req.session.user = result.token;
console.log("session is", req.session); //THIS SHOWS THE TOKEN
res.status(200).json({status:"OKAY!"})
});
} else {
res.status(401).json({status:"Invalid User!"})
}
})
.catch(error => console.log(error));
});
Code inside the request API for http://localhost:3000/
app.use("/", function(req, res) {
console.log("Session is", req.session); //THIS SHOWS THE TOKEN FOR POSTMAN, NOT WHEN WORKING WITH VUE
res.sendFile(__dirname + "/dist/index.html");
});
Since the axios post request and the subsequent redirect is to http://localhost:3000/ I expected the token to be maintained. But it seem to assume this as a new connection. Is this approach wrong? Is there a way to keep track of the token on redirect? I need the token only once, because then I will store it in the angular application and send it for any other requests that I need.
How do I go about with this?

Battle.net API returns 401 Error using OAuth Token

I'm using Nodejs with express on the Battle.net API to generate an Oauth Token. https://develop.battle.net/documentation/guides/using-oauth
Generating the token itself works, as it returns me the token. But when I use the code to make a request to their API, for example:
https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN
I get a 401 Unauthorized Error Response, debug log:
{ url:
'https://eu.api.blizzard.com/wow/guild/Malfurion/The%20new%20Dimension?fields=members&locale=de_DE&access_token=HEREISMYTOKEN',
status: 401,
statusText: 'Unauthorized',
headers: Headers { [Symbol(map)]: [Object] } }
I'm trying to fetch the members of a guild via fetch().
I already tried:
Creating a new Application (with new Client Secret and ID)
setting every possible callback url in the battle.net settings:
https://localhost/
http://localhost/
https://localhost:443/
http://localhost:443/
https://localhost/auth/bnet/callback
http://localhost/auth/bnet/callback
https://localhost:443/auth/bnet/callback
http://localhost:443/auth/bnet/callback
creating a token manually by "trying out the api" (https://develop.battle.net/documentation/api-reference/world-of-warcraft-community-api), where you put in your Client ID and Secret and then get a temporary Token. THAT ONE works, also in my application.
You can compare response of those two urls (just use your browser):
First (Generated in my application): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=EU7XD8E4K9IAJKBGJSP3MDBLAVCIU2BYXS
Second (Generated trying out the API on battle.net website where you fill out clientid and secret to test out the api): https://eu.api.blizzard.com/wow/guild/Blackmoore/The%20new%20Dimension?fields=members&locale=de_DE&access_token=US23su4g0hAeS5w3EUCkKA9MJPgJ8k8bzV
CODE
server.js, simple express app
var BNET_ID = "MYID";
var BNET_SECRET = "MYSECRET";
...
// Use the BnetStrategy within Passport.
passport.use(
new BnetStrategy(
{ clientID: BNET_ID,
clientSecret: BNET_SECRET,
scope: "wow.profile sc2.profile",
callbackURL: "https://localhost/",
region: "eu" },
function(accessToken, refreshToken, profile, done) {
process.nextTick(function () {
return done(null, profile);
});
})
);
// bnet auth routes
app.get('/auth/bnet', passport.authenticate('bnet'));
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/' }),
function(req, res){
res.redirect('/');
});
controller.js
...
const res = await fetch(`https://eu.api.blizzard.com/wow/guild/${servers[iterator]}/The new Dimension?fields=members&locale=de_DE&access_token=${thetoken}`).then((res) => {
res.json();
// for debugging, shows 401 Error
console.log(res);
});
...
I actually expect a response such as this, because it works using a temporary Token:
status: 200 OK
body: {
"lastModified": 1546676373000,
"name": "The new Dimension",
"realm": "Blackmoore",
"battlegroup": "Glutsturm / Emberstorm",
"level": 25,
"side": 0,
"achievementPoints": 1005,
"members":
(......)
}
I managed to resolve the issue!
Very, very hacky but I managed to resolve the issue by hacking the oauth callback middleware like this:
set my used API token to the req.user.token.
app.get('/auth/bnet/callback',
passport.authenticate('bnet', { failureRedirect: '/?error' }),
function(req, res) {
req.session.bnettoken = req.user.token;
res.redirect('/');
}
);
I suspect that "code" or "token" is also used in my SessionStorage (express-session) to store the current session in my database. So I just hack the user.token out of the request and use that. Phew.. Hours of work.
From the documentation I can see that you need to pass the token into the Authorization header with value of : Bearer HEREISMYTOKEN.
More info on Authorization Header and Headers here:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#Headers
Example on how to use it can be found in this SO answer

Resources