TypeError when instantiaing SpotifyWebApi object - javascript

im trying to write a bot to do some playlist handling in spotify. I'm using the spotify-web-api-node package : https://github.com/thelinmichael/spotify-web-api-node
I installed the package and whenever I try to create an object with the following code:
const SpotifyWebApi = require('spotify-web-api-node');
const spotifyApp = SpotifyWebApi();
I keep getting this error:
this._credentials = credentials || {};
^
TypeError: Cannot set properties of undefined (setting '_credentials')
this is the contructor signature in the src file:
constructor(credentials?: Credentials);
Any thoughts ?

You need an access-token for access API call.
In the documentation,
If you've got an access token and want to use it for all calls, simply use the API object's set method. Handling credentials is described in detail in the Authorization section.
spotifyApi.setAccessToken('<your_access_token>');
Demo code: get access token then get Elvis' albums
const SpotifyWebApi = require('spotify-web-api-node');
const axios = require('axios')
const getToken = async () => {
try {
const response = await axios.post(
url = 'https://accounts.spotify.com/api/token',
data = '',
config = {
params: {
'grant_type': 'client_credentials'
},
auth: {
username: '<your client id>',
password: '<your client secret>'
}
}
);
return Promise.resolve(response.data.access_token);
} catch (error) {
return Promise.reject(error);
}
}
getToken()
.then(token => {
const spotifyApi = new SpotifyWebApi();
spotifyApi.setAccessToken(token);
// Passing a callback - get Elvis' albums in range [0...1]
spotifyApi.getArtistAlbums('43ZHCT0cAZBISjO8DG9PnE', { limit: 2, offset: 0 }).then(
(data) => {
console.log('Artist albums', data.body);
},
(err) => {
console.error(err);
}
);
})
.catch(error => {
console.log(error.message);
});
Result

Related

How to improve sequential promises execution and force fulfillment

This code is being used in a Sveltekit web application.
In the first step I get a user jwt token from an api like : dashboard.example.com/auth/local
and in the second step I'm using the response of the first api call to get full information from an api endpoint like this : example.com/api/users/token
This is an endpoint in an Sveltekit application:
import { json as json$1, error } from '#sveltejs/kit';
import axios from 'axios';
import md5 from 'md5';
import { SITE_ADDRESS } from '$lib/Env';
let userToken;
/** #type {import('#sveltejs/kit').RequestHandler} */
export async function POST({ request }) {
const bodyData = await request.json();
let identifier = bodyData.data.identifier;
let password = bodyData.data.password;
let loginToken = bodyData.data.loginToken;
let newLoginToken = md5(identifier + password + process.env.SECURE_HASH_TOKEN);
let dataResult = await axios
.post(`${import.meta.env.VITE_SITE_API}/auth/local`, {
identifier: identifier,
password: password
})
.then((response) => {
return response.data;
})
.then((response) => {
let userSummaryData = response;
userToken = md5(
userSummaryData.user.username + userSummaryData.user.id + process.env.SECURE_HASH_TOKEN
);
let userCompleteData = axios
.post(`${SITE_ADDRESS}/api/users/${userToken}`, {
data: {
userID: userSummaryData.user.id,
username: userSummaryData.user.username
}
})
.then((response) => {
return {
userJWT: userSummaryData.jwt,
userSummary: userSummaryData.user,
userFullSummary: response.data.userFullSummary
};
});
return userCompleteData;
})
.catch((error) => {
// console.log(' ---- Err ----');
});
if (dataResult && newLoginToken == loginToken) {
return json$1(
{
userJWT: dataResult.userJWT,
userSummary: dataResult.userSummary,
userFullSummary: dataResult.userFullSummary
},
{
headers: {
'cache-control': 'private, max-age=0, no-store'
}
}
);
} else if (dataResult && newLoginToken != loginToken) {
throw error(400, 'Something wrong happened');
}
throw error(401, 'Something wrong happened');
}
This code is work perfectly in localhost. But when I test it on host I get error 401.
and the question is :
Why this works on localhost but doesn't work on the server?
How can I improve this kind of promises (I'd like to use the response of the first api call in the second api call and return both
as a result)

Using Google Oauth in FeathersJs with an existing access token

How do I use the Google Oauth in featherjs with an existing access token? The docs do not give an example on this. The only example is through the browser as shown here.
When going through the browser, http://localhost:3030/oauth/google works ok. I can successfully add the user to my DB. Here is my code:
const { LocalStrategy } = require("#feathersjs/authentication-local");
const { expressOauth } = require("#feathersjs/authentication-oauth");
const { OAuthStrategy } = require("#feathersjs/authentication-oauth");
var uniqid = require("uniqid");
const { AuthenticationService, JWTStrategy } = require('#feathersjs/authentication');
class GoogleStrategy extends OAuthStrategy {
async getEntityData(profile) {
const baseData = await super.getEntityData(profile);
console.log({ profile });
return {
...baseData,
profilePicture: profile.picture,
email: profile.email,
password: uniqid.time(),
};
}
}
module.exports = (app) => {
const authentication = new AuthenticationService(app);
authentication.register("local", new LocalStrategy());
authentication.register('jwt', new JWTStrategy());
authentication.register("google", new GoogleStrategy());
app.use("/authentication", authentication);
app.configure(expressOauth());
};
However if I try using an access token,like so
POST http://localhost:3030/authentication
data: {
"strategy": "google",
"accessToken": "ya29.A0ARrdaM_UJa6idfZr-4taqwkJ6qGBV1Dp9wbxF-wsult8dNPaVNCVg6Fndmrqv7BhRSwxa5gAKllPvbKtsjyxS39WdmWmqkmE42HOsVZaJWHVEttxbebel3zdpD5BSxWtRiG7NuZLNVedMUaK5AdgIRrJk1u"
}
I do not get the google profile, no user is added to my DB and I get this error:
{
"name": "GeneralError",
"message": "401 Unauthorized",
"code": 500,
"className": "general-error",
"data": {},
"errors": {}
}
I have added "google" to my list of authStrategies in default.json
So my question is what do I need to do?
So I found the solution. Thought I might share. Add this to authentication.js
//add this method
async getProfile(authResult) {
const accessToken = authResult.accessToken;
const { data } = await axios
.get(
`https://openidconnect.googleapis.com/v1/userinfo?access_token=${accessToken}`
)
.then((res) => {
return res;
})
.catch((error) => console.log("autherr", error));
return data;
}

Firebase functions: Value for argument "data" is not a valid Firestore document

I am trying to follow a fireship tutorial with oAuth2 with the code below.
All functions are initialized correctly but there was this error message when I tried to authorize the App through twitter. The error message is below.
⚠ functions: Error: Value for argument "data" is not a valid Firestore document. Cannot use "undefined" as a Firestore value (found in field "accessToken"). If you want to ignore undefined values, enable ignoreUndefinedProperties.
The code is below:
const functions = require('firebase-functions')
const admin = require('firebase-admin')
admin.initializeApp()
// Database reference
const dbRef = admin.firestore().doc('tokens/demo')
const TwitterApi = require('twitter-api-v2').default
const twitterClient = new TwitterApi({
clientId: 'aabbcc',
clientSecret: 'aabbcc',
})
const callbackURL =
'http://127.0.0.1:5001/primussoft-74a49/us-central1/callback'
// STEP 1 - Auth URL
exports.auth = functions.https.onRequest(async (request, response) => {
const { url, codeVerifier, state } = twitterClient.generateOAuth2AuthLink(
callbackURL,
{ scope: ['tweet.read', 'tweet.write', 'users.read', 'offline.access'] }
)
// store verifier
await dbRef.set({ codeVerifier, state })
response.redirect(url)
})
// STEP 2 - Verify callback code, store access_token
exports.callback = functions.https.onRequest(async (request, response) => {
const { state, code } = request.query
const dbSnapshot = await dbRef.get()
const { codeVerifier, state: storedState } = dbSnapshot.data()
if (state !== storedState) {
return response.status(400).send('Stored tokens do not match!')
}
const {
client: loggedClient,
accessToken,
refreshToken,
} = twitterClient.loginWithOAuth2({
code,
codeVerifier,
redirectUri: callbackURL,
})
await dbRef.set({ accessToken, refreshToken })
const { data } = loggedClient.v2.me() // start using the client if you want
response.send(data)
})
// STEP 3 - Refresh tokens and post tweets
exports.tweet = functions.https.onRequest(async (request, response) => {
const { refreshToken } = dbRef.get().data()
const {
client: refreshedClient,
accessToken,
refreshToken: newRefreshToken,
} = twitterClient.refreshOAuth2Token(refreshToken)
await dbRef.set({ accessToken, refreshToken: newRefreshToken })
// const { data } = await refreshedClient.v2.tweet(
// nextTweet.data.choices[0].text
// )
const { dataone } = { id: 'testid', text: 'a tweet from me' }
response.send(dataone)
})
exports.tweet = functions.https.onRequest((request, response) => {})
I was actually trying to follow a tutorial by fireship in this link https://youtu.be/V7LEihbOv3Y
Any help would be greatly appreciated.
The refreshOAuth2Token method returns a promise as well. Try adding await as shown below:
const {
client: refreshedClient,
accessToken,
refreshToken: newRefreshToken,
} = await twitterClient.refreshOAuth2Token(refreshToken)

error retrieving user Id token assigned by firebase in the client side

I am using JWT based authentication using firebase Admin SDK in express js.
according to the sign in with custom token when we sign the user with the function signInWithCustomToken(token) firebase sets a user-id token for that user.
according to retrieve id tokens
firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
// Send token to your backend via HTTPS
// ...
}).catch(function(error) {
// Handle error
});
we can get the token if the user is logged in
but executing this I get error that getIdToken value is null.
i changed the code to
const getUser = async () => {
const token = await firebase.auth().currentUser.getIdToken(/* forceRefresh */true).catch(function(error) {
console.log(error)
});
const userToken = await token;
const getData = async (userToken) => {
const response = await fetch('/getUser', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({idToken: userToke})
})
const data = await response.json()
console.log(responnse)
}
}
getUser();
but still receiving the same error
I looked up for some solutions and found similar answers to the question one of which I implemented was solution
it used onAuthStateChanged method and I am using
<script src="https://www.gstatic.com/firebasejs/7.14.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/7.14.1/firebase-auth.js"></script>
in cdn but now am getting
Uncaught (in promise) TypeError: firebase.auth.onAuthStateChanged is not a function
at profile:40
at new Promise (<anonymous>)
at getIdTokenRefreshed (profile:37)
at profile:50
I changed the above code to this
firebase.initializeApp(firebaseConfig);
const getIdTokenRefreshed = async () => {
return new Promise(async (resolve, reject) => {
const unsubscribe = await firebase
.auth
.onAuthStateChanged(async user => {
unsubscribe()
const refreshedToken = await user
.getIdToken(true)
.catch(err => console.error(err))
resolve(refreshedToken)
console.log(refreshedToken)
}, reject)
});
}
getIdTokenRefreshed();
still getting the second error where onAuthStateChanged is not defined
how do I retrieve the user id token?
UPDATE
const getIdTokenRefreshed = async () => {
try {
const user = firebase.auth().currentUser
if (user) {
const token = await user.getIdToken(true)
console.log(`Token: ${token}`)
return token
} else {
console.log("No user is logged in")
}
} catch (e) {
console.log(`Something went wrong: ${e}`)
}
}
after implementing the above code this is the error
await is only valid in async functions and the top level bodies of modules
First, I'd recommend updating Firebase SDK to latest version which is 8.9.1 at the time of writing this.
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-app.js"></script>
<script src="https://www.gstatic.com/firebasejs/8.9.1/firebase-auth.js"></script>
If you take a look at onAuthStateChanged part in the documentation, it should be:
firebase.auth().onAuthStateChanged(...)
// ^^
// not firebase.auth.onAuthStateChanged
The onAuthStateChanged won't be triggered unless you call the getIdTokenRefreshed function. You can simply refactor that function to:
const getIdTokenRefreshed = async () => {
try {
const user = firebase.auth().currentUser
if (user) {
const token = await user.getIdToken(true)
console.log(`Token: ${token}`)
return token
} else {
console.log("No user is logged in")
}
} catch (e) {
console.log(`Something went wrong: ${e}`)
}
}
Lastly, the variable name is userToken but in request body it is body: JSON.stringify({idToken: userToke}) and you don't need an await before a variable name. Try refactoring the getUser function to:
//const token = await firebase.auth().currentUser.getIdToken(/* forceRefresh */true).catch(function(error) {
// console.log(error)
//});
//const userToken = await token;
const getUser = async () => {
const token = await firebase.auth().currentUser.getIdToken(true)
const response = await fetch('/getUser', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({idToken: token})
})
const data = await response.json()
console.log(data)
return data
}
getUser().then(data => {
console.log("Data received")
})

firebase reauthentication flow with Token in react js app not working properly

after an hour the user get disconnected from the firebase functions and gets an error.
the connection to firebase in the app work like this:
After the first connection via google, the token is sent to firebase functions.
after that a getIdToken(ture) to force a refresh in useEffect.
the token is saved in the state via mobX and every time a commend requires to send or get data from the data base it's passes the token to the firebase functions
I have noticed that I don't get a new token in .then(function (idToken) {...}
this is the error :
FirebaseAuthError: Firebase ID token has expired.
Get a fresh ID token from your client app and try again (auth/id-token-expired).
See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
...
...
...
> errorInfo: {
> code: 'auth/id-token-expired',
> message: 'Firebase ID token has expired.
Get a fresh ID token from your client app and try again (auth/id-token-expired).
See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.'
> },
> codePrefix: 'auth'
> }
Things that I have tried already:
is to separate the firebase.auth().currentUser.getIdToken(true).then() to a different useEffect().
call getIdToken() after I get an error from the firebase functions.
const UserSubscriber = () => {
//using mobX here
const { user } = useStores();
const token = user.store.token;
React.useEffect(() => {
if (!token.length || !firebase.auth().currentUser) return;
firebase.auth().currentUser.getIdToken(true).then(function (idToken) {
const decodedToken = jwt.decode(idToken, '', true);
if (!decodedToken.user_id) return;
const unsub = firebase.firestore().collection('users').doc(decodedToken.user_id).onSnapshot(docSnapshot => {
const data = docSnapshot.data();
//user.mergeData() is just to store data
if (!data) return user.mergeData({ noUser: true, token: idToken })
user.mergeData({ ...data, noUser: false, token: idToken })
});
return () => unsub();
}).catch(function (error) {
user.logOut();
});
}, [token, user]);
return useObserver(() => (
<div />
));
}
and in the backend
app.use(async (req, res, next) => {
try {
const decodedToken = await admin.auth().verifyIdToken(req.body.token);
let uid = decodedToken.uid;
req.uid = uid;
return next();
} catch (error) {
console.log(error);
return res.status(401).send();
}
});
I have tried firebase.auth().onAuthStateChanged(userAuth => {...}) (in side of the useEffect())
firebase.auth().onAuthStateChanged(userAuth => {
userAuth.getIdToken().then(function (idToken) {
const decodedToken = jwt.decode(idToken, '', true);
if (!decodedToken.user_id) return;
const unsub = firebase.firestore().collection('users').doc(decodedToken.user_id).onSnapshot(docSnapshot => {
const data = docSnapshot.data();
if (!data) return user.mergeData({ noUser: true, token: idToken })
user.mergeData({ ...data, noUser: false, token: idToken })
});
return () => unsub();
}).catch(function (error) {
user.logOut();
});
})
;
}

Categories

Resources