I am in the process of building a server-side app that is going to push the data into PowerBI's dataset. The only thing I'm struggling with is getting the bearer token to make a request to their API.
I'm getting the access token in the following way, however, I'm getting "401 Unauthorized" when using it in the request.
async getToken() {
const AuthenticationContext = adal.AuthenticationContext;
const context = new AuthenticationContext(`${ CONFIG.power_bi.authorityURI }/${ CONFIG.power_bi.tenantID }`);
return new Promise((resolve, reject) => {
context.acquireTokenWithClientCredentials(
CONFIG.power_bi.scope,
CONFIG.power_bi.clientID,
CONFIG.power_bi.clientSecret,
(error, tokenResponse) => {
if (error) {
console.log('PowerBI Token Acquire Error:', error);
return reject(tokenResponse == null ? error : tokenResponse);
}
console.log('PowerBI Token Acquired:', tokenResponse);
return resolve(tokenResponse);
}
);
});
}
async postRowsInDatasetTable() {
const token = await this.getToken().then(({ accessToken }) => accessToken);
const groupId = '{groupId}';
const datasetId = '{datasetId}';
const tableName = '{tableName}';
try {
const result = await axios.post(
`https://api.powerbi.com/v1.0/myorg/groups/${groupId}/datasets/${datasetId}/tables/${tableName}/rows`,
{
rows: [{
"status": "Deal",
"date": "2021-02-10T10:55:01.706Z"
}]
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-type': 'application/json'
}
}
);
} catch (error) {
console.log(error);
}
}
The thing is that data push functionality is working just fine with a token which I took from PowerBI's documentation page, where they provide example API calls, as it is populated automatically based on currently logged-in user (e.g. https://learn.microsoft.com/en-us/rest/api/power-bi/pushdatasets/datasets_postrowsingroup), which means I am simply getting the wrong token.
Could anyone please advise how to get the appropriate token, as I found nothing in the documentation?
Thanks
Related
I have created a Discord bot with a couple of commands in it, all fine until here.
The idea is now to ask users for their ID manually, and once this info is in, I'd like to add them as members of my guild via the endpoint described in the API "/guilds/{guild.id}/members/{user.id}".
Therefore I'll recap:
Ask the user to provide his/her ID,
Enter the following URL http://localhost:3000/add-member/:userID
Add the member to the guild.
Everything works well if I auto add myself.
The error comes when I use an ID of an external user.
The error is:
There was an error DiscordAPIError[50025]: Invalid OAuth2 access token.
The application has all the necessary permissions as guilds.join, application.commands and bot. And the bot has Admin permission.
This my repo in github:
https://github.com/Srizza93/harry-botter
For the last 2 days I'm struggling and online there is not much in detail.
Thanks in advance for your replies.
So far this is my code in sever.js:
// Adding Members
app.get(`/add-member/:userId`, async (req, res) => {
try {
await rest.put(Routes.guildMember(guildId, req.params.userId), {
headers: {
["Content-Type"]: "application/json",
Authorization: `${token}`,
},
body: {
access_token: accessToken,
nick: "New",
},
});
console.log("Successfully added memeber id " + req.params.userId);
} catch (error) {
console.log("There was an error " + error);
}
});
And this is my first point index.js:
const server = require("./server");
const fs = require("node:fs");
const path = require("node:path"); // Require the necessary discord.js classes
const { Client, Collection, Events, GatewayIntentBits } = require("discord.js");
const { token } = require("./config.json");
// Create a new client instance
const client = new Client({
intents: [GatewayIntentBits.Guilds, GatewayIntentBits.GuildMembers],
});
server(client);
client.commands = new Collection();
const commandsPath = path.join(__dirname, "commands");
const commandFiles = fs
.readdirSync(commandsPath)
.filter((file) => file.endsWith(".js"));
for (const file of commandFiles) {
const filePath = path.join(commandsPath, file);
const command = require(filePath);
// Set a new item in the Collection with the key as the command name and the value as the exported module
if ("data" in command && "execute" in command) {
client.commands.set(command.data.name, command);
} else {
console.log(
`[WARNING] The command at ${filePath} is missing a required "data" or "execute" property.`
);
}
}
// When the client is ready, run this code (only once)
// We use 'c' for the event parameter to keep it separate from the already defined 'client'
client.once(Events.ClientReady, (c) => {
console.log(`Ready! Logged in as ${c.user.tag}`);
});
// Once ready, listen for events
client.on(Events.InteractionCreate, async (interaction) => {
if (!interaction.isChatInputCommand()) return;
const command = interaction.client.commands.get(interaction.commandName);
if (!command) {
console.error(`No command matching ${interaction.commandName} was found.`);
return;
}
try {
await command.execute(interaction);
} catch (error) {
console.error(error);
await interaction.reply({
content: "There was an error while executing this command!",
});
}
});
// Add a Default role to each new member
client.on(Events.GuildMemberAdd, (member) => {
try {
const role = member.guild.roles.cache.find(
(role) => role.name === "discorder"
);
if (role) {
member.roles.add(role);
console.log(member.user.id + " is in da house");
} else {
console.log(
`The role discorder was not assigned to '${member}' as it wasn't created`
);
}
} catch (error) {
console.error(error);
}
});
// Log in to Discord with your client's token
client.login(token);
EDIT
I managed to exchange the OAuth2 and send my request with the below code:
app.get("/login", (req, res) => {
// Redirect the client to the authorization URL
res.redirect(
`https://discord.com/api/oauth2/authorize?client_id=1055994237243637812&permissions=8&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback&response_type=code&scope=bot%20guilds.join%20applications.commands`
);
});
app.get("/callback", async (req, res) => {
// Get the OAuth2 token from the query parameters
const code = req.query.code;
// Exchange the code for an OAuth2 token
if (code) {
try {
const tokenResponseData = await request(
"https://discord.com/api/oauth2/token",
{
method: "POST",
body: new URLSearchParams({
client_id: clientId,
client_secret: clientSecret,
code,
grant_type: "authorization_code",
redirect_uri: redirectUri,
scope: "guilds.join",
}).toString(),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
},
}
);
const oauthData = await tokenResponseData.body.json();
await rest.put(Routes.guildMember(guildId, oauthData.owner_id), {
headers: {
["Content-Type"]: "application/json",
Authorization: `${token}`,
},
body: {
access_token: oauthData.access_token,
nick: "New",
},
});
console.log(`Successfully added user ${oauthData.owner_id}`);
} catch (error) {
// NOTE: An unauthorized token will not throw an error
// tokenResponseData.statusCode will be 401
console.error(error);
}
}
});
However, the error is now:
DiscordAPIError[20001]: Bots cannot use this endpoint
But I'm actually using a server and not the bot. At this point my question is, is it even possible to add members via a server?
Finally I managed to solve this issue with the below code:
// Add member
await rest
.put(Routes.guildMember(guildId, user.id), {
body: {
access_token: oauthData.access_token,
nick: "New",
roles: [role.id, channelRole.id],
},
headers: {
Authorization: `Bot ${botToken}`,
["Content-Type"]: "application/json",
},
})
.catch(console.error);
The error was pointing out to the bots permissions, however, the syntax was incorrect.
Hi I have managed to store the JWT token generated on Login to a cookie in the browser. How can I use the token to display a list of users from a protected route in Nodejs APi.
I have accessed the token from the cookie using following code
Cookies.get("access")
the Fetch API Code is
const url = "http://localhost:4000/users/";
const getUsers = () => {
fetch(
url,
{ method: "GET" },
{
headers: {
Authorization: "Bearer" + Cookies.get("access"),
"x-auth-token": Cookies.get("access"),
},
}
)
.then((data) => data.json())
.then((prdt) => console.log(prdt));
};
useEffect(getUsers, []);
Node API Code is as follows:
//http://localhost:4000/users
router.get("/", auth, async function (request, response) {
try {
const result = await getAllUsers();
response.send(result);
} catch {
response.status(400).send({
message: error,
});
}
});
the Nodejs Auth Code is as follows:
export const auth = (request, response, next) => {
try {
const token = request.header("x-auth-token");
console.log("token", token);
jwt.verify(token, process.env.SECRET_KEY);
next();
} catch (err) {
response.status(401).send({
error: err.message,
});
}
};
I am not sure where I am going wrong
the Nodejs Part works as tried in Postman The issue is originating on the react side fetch API where I am not sure how to send the keyname "x-auth-token" with jwt token to retrieve the data.
I'm building a mobile app and on some actions (like userCreate), I trigger some Firebase functions to perform an API call to a third service, and then write something in the Firestore database.
Everything works in theory, but in practice, the API calls are quite fast (even with cold start scenarios), but the database writes can take several MINUTES to complete (if they do at all, I suspect that sometimes it takes too long and times out).
Since the API call work just fine, and so does the DB write on some occasion, I suspect that this is simply due to very poor async management from me since I know about nothing in JS.
Here are two of many example functions which are concerned by this issue, just to showcase that it happens whereas I'm triggering functions onCreate or on HTTPS.
onCreate function
const functions = require("firebase-functions");
const axios = require('axios')
// The Firebase Admin SDK to access the Firestore.
const admin = require('firebase-admin');
admin.initializeApp();
// Third party service credentials generation during onCreate request
exports.
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
db.collection('users').doc(user.uid).set(newUser)
.catch(function (error) {
//handle error
console.log(error);
});
})
.catch(function (response) {
//handle error
console.log(response);
});
})
HTTPS onCall function
exports.paymentRequest = functions.https.onCall(async (data, context) => {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
"destinations": [
{
"accountNumber": recipientIban,
"type": "iban"
}
],
"amount": productPrice,
"currency": "EUR",
"market": "FR",
"recipientName": recipientName,
"sourceMessage": paymentDescription,
"remittanceInformation": {
"type": "UNSTRUCTURED",
"value": paymentReference
},
"paymentScheme": "SEPA_INSTANT_CREDIT_TRANSFER"
};
(async function(){
response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
.catch(function (error) {
//handle error
console.log(error);
});
console.log(response.data)
return response.data
})()
})
Am I on the right track thinking that this is an async problem?
Or is it a Firebase/Firestore issue?
Thanks
You are not returning the promises returned by the asynchronous methods (axios() and set()), potentially generating some "erratic" behavior of the Cloud Function.
As you will see in the three videos about "JavaScript Promises" from the official Firebase video series you MUST return a Promise or a value in a background triggered Cloud Function, to indicate to the platform that it has completed, and to avoid it is terminated before the asynchronous operations are done or it continues running after the work has been completed.
The following adaptations should do the trick (untested):
onCreate Function:
buildCredentials = functions.auth.user().onCreate((user) => {
// Request to Third party to generate an Client Access Token
return axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/oauth/token",
data: "client_id=xxx&client_secret=yyy&grant_type=client_credentials&scope=all:all",
headers: { "Content-Type": "application/x-www-form-urlencoded" },
})
.then(function (response) {
//handle success
console.log('new user created: ')
console.log(user.id);
console.log(response.data);
// We write a new document in the users collection with the user ID and the Client Access Token
const db = admin.firestore();
const newUser = {
uid: user.uid,
clientAccessToken: response.data.access_token
};
return db.collection('users').doc(user.uid).set(newUser)
})
.catch(function (response) {
//handle error
console.log(response);
return null;
});
})
Callable Function:
exports.paymentRequest = functions.https.onCall(async (data, context) => {
try {
const clientAccessToken = data.clientAccessToken;
const recipientIban = data.recipientIban;
const recipientName = data.recipientName;
const paymentDescription = data.paymentDescription;
const paymentReference = data.paymentReference;
const productPrice = parseInt(data.productPrice);
const uid = data.uid;
const jsonData = {
// ...
};
const response = await axios({
method: "post",
url: "https://api.ThirdParty.com/api/v1/pay",
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
Authorization: `Bearer ${clientAccessToken}`,
},
data: jsonData,
})
// We write a the payment request ID in the user's document
const db = admin.firestore();
const paymentRequestID = response.data.id;
await db.collection('users').doc(uid).set({
paymentRequestID: paymentRequestID
}, { merge: true })
console.log(response.data)
return response.data
} catch (error) {
// See https://firebase.google.com/docs/functions/callable#handle_errors
}
})
I can't figure out how to prepare the POST command to access keycloak with a service account.
I'm using nodejs to access a clean keycloak 4.8.1 server and tried a few configurations of POST but couldn't have success in log in through service account yet.
I'm having a hard time trying to simply login to a client through service account. I've tried some configurations but never got success in log in. The last code I've tried was like this:
function serviceAccountAuthenticate (client) {
return function login (realmName, secret) {
const clientSecret = `procempa-admin:${secret}`;
const basicToken = `Basic: ${btoa(clientSecret)}`;
return new Promise((resolve, reject) => {
const req = {
form: {'grant_type': 'client_credentials'},
authorization: basicToken,
'Content-Type': 'application/x-www-form-urlencoded',
};
const url = `${client.baseUrl}/realms/${realmName}/protocol/openid-connect/token`;
request.post(url, req, (err, resp, body) => {
if (err) {
return reject(err);
}
// Check that the status cod
if (resp.statusCode !== 200) {
return reject(body);
}
return resolve(body);
});
});
};
}
and the result was sad like this:
{ form: { grant_type: 'client_credentials' },
authorization: 'Basic: cHJvY2VtcGEtYWRtaW46YzZmZmUxNzUtNDgyZS00NjMxLWE5YjEtMTBjNWIyNjZlYzZi',
'Content-Type': 'application/x-www-form-urlencoded' }
Error {"error":"unauthorized_client","error_description":"INVALID_CREDENTIALS: Invalid client credentials"}
I'm following this tutorial, I got to the point to send an HTTP request to the fcm endpoint, but I'm getting the following error:
{
error:
{
code: 400,
message: 'Request contains an invalid argument.',
status: 'INVALID_ARGUMENT'
}
}
Instead of sending the request using curl I'm using a cloud function using node.js | express with the following code:
exports.messages = function (req, res) {
const api = 'https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send';
const headers = {
'Accept': 'application/json',
'Content-type': 'application/json',
};
return getAccessToken().then((accessToken) => {
headers.Authorization = `Bearer ${accessToken}` // <-- accessToken is OK
const { title, body, token } = req.body;
return fetch(api, {
headers,
method: 'POST',
body: JSON.stringify(
{
'message': {
'token': token, // <-- this is the client fcm token
'notification': {
'title': title,
'body': body
}
}
}
)
}).then(res => res.json()).then(data => {
console.log(data);
res.sendStatus(200);
}).catch((error) => {
console.error(error);
});
});
}
Where the token is an OAuth 2 token for my service account
function getAccessToken() {
return new Promise(function (resolve, reject) {
var key = require('../keys/service-account.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
[
'https://www.googleapis.com/auth/firebase',
'https://www.googleapis.com/auth/firebase.database',
'https://www.googleapis.com/auth/firebase.messaging',
],
null
);
jwtClient.authorize(function (err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
what am I missing here? any advice will be appreciated
I just realized I copied the uri for the fcm endpoint from the tutorial. I changed it to:
const api = 'https://fcm.googleapis.com/v1/projects/{project-Id}/messages:send';
and it worked!