Authenticating Firebase Facebook Token in REST / GraphQL - node.js

I followed this document https://firebase.google.com/docs/auth/web/facebook-login
token is a new accessToken given from the client
const context = ({req}) => {
const credential = firebase.auth.FacebookAuthProvider.credential(token)
try {
firebase.auth().signInAndRetrieveDataWithCredential(credential)
} catch(error) {
console.error(error)
}
}
but I'm receiving this error (100% sure the token is copied correctly)
{ [Error: Your API key is invalid, please check you have copied it correctly.]
code: 'auth/invalid-api-key',
message:
'Your API key is invalid, please check you have copied it correctly.' }
{ _extensionStack:
GraphQLExtensionStack { extensions: [ [CacheControlExtension] ] } }
Am I doing this wrong?

Found a solution!
On the client side I had to change the facebook accessToken into an IdToken via:
getIdToken = async () => {
const provider = new firebase.auth.FacebookAuthProvider()
const result = await firebase.auth().signInWithPopup(provider)
const IdToken = await firebase.auth().currentUser.getIdToken(true)
}
Then verify the IdToken in the backend
try {
const decodedToken = await admin.auth().verifyIdToken(token)
console.log(decodedToken)
} catch (error) {
// error code
}

Related

DIscord API - add a guild member with node js express

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.

Expressjs: google-auth-libary id-token verification "token used too late number > number..."

I have tried to add google auth login for my reactjs/expressjs web app. On the frontend I am using the react-google-login package:
import React from "react"
import { GoogleLogin } from "react-google-login"
import { useHistory } from "react-router-dom"
const axios = require('axios')
export default function LoginButton() {
let history = useHistory()
const { REACT_APP_GOOGLE_CLIENT_ID, REACT_APP_API_URL } = process.env
const onSuccess = async (response) => {
console.log(response)
const data = { token: response.tokenId }
const res = await axios.post(REACT_APP_API_URL + "/auth/google", data, {
'Content-Type' : 'text/json'
})
.then((res) => {
history.push('/home')
})
.catch((err) => {
console.log("[LOGIN FAILED]")
})
}
const onFailure = (response) => {
console.log(response)
}
return(
<GoogleLogin
clientId={REACT_APP_GOOGLE_CLIENT_ID}
buttonText="Log in with Google"
onSuccess={onSuccess}
onFailure={onFailure}
cookiePolicy={'single_host_origin'}
/>
)
}
From what it seems I am getting the correct data from this button. I am sending the token to the expressjs api. This is where I am having issues. When using google-auth-library to verify the token I am getting the error: "Error: Token used too late, number_1 > number_2". As far as I know the idToken has expired, but isn't it weird considering I sent it as soon as possible from the frontend. Backend code:
const { OAuth2Client } = require('google-auth-library')
require('dotenv').config()
const client = new OAuth2Client(process.env.CLIENT_ID)
const postGoogleLogin = async (req, res) => {
const { token } = req.body
try{
const ticket = await client.verifyIdToken({
idToken: token,
audience: process.env.CLIENT_ID
})
const { name, email, picture } = ticket.getPayload()
res.status(201).json({
name: name,
email: email,
picture: picture
})
}
catch(e){
console.log(e)
}
}
I have tried to verify the token using the endpoint https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 which says the token is valid, but as far as I know this endpoint should not be used in production
The issue appears to be from the library itself. As long as you're using the same client id on both React and ExpressJs, verifyIdToken should return a success response as long as the token is still valid.
Also, you can make use of https://oauth2.googleapis.com/tokeninfo?id_token=XYZ123 in your production code.
Internally, the library call the same endpoint to verify your token.

How to get auth token for PowerBI REST API?

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

Get JWT token in redux

I am using JWT to handle authentication and I have a update new password function. My problem is that how do I pass this JWT to my server in the UpdatePasswordAction ? Actually, I have that JWT in my cookies but when I submit the data to the server, the data is not passed correctly. Do I pass it right?
From my server, the JWT can not be retrieved.
console.log(JSON.stringify(req.cookies));// -> {}
console.log(req.headers.authorization);// -> undefined
In my User.actions.jsx, what I want to do is to get the token stored in cookies and pass the updatePasswordState which contains current password, new password, and new confirmed password.
import { Cookies } from 'js-cookie';
export const UpdatePasswordAction = (updatePasswordState) => {
return async (dispatch) => {
try {
// what I do is to pass the new password and JWT to the server to handle new password update
const token = Cookies.get('jwt');
const res = await axios.patch(`http://127.0.0.1:3000/users/updateMyPassword`, updatePasswordState, token);
const { data } = res;
dispatch({ type: UserActionTypes.UPDATE_PASSWORD_SUCCESS, payload: data });
alert('Update Password Successfully');
} catch (error) {
if (error.response) {
dispatch({
type: UserActionTypes.UPDATE_PASSWORD_FAIL,
payload: error.response.data.message,
});
console.log(error.response.data.message);
}
}
};
};
In my server, I have a middleware to check whether the user is login or not.
exports.protect = catchAsync(async (req, res, next) => {
//Getting token and check of it's there
let token;
console.log(JSON.stringify(req.cookies));// -> {}
console.log(req.headers.authorization);// -> undefined
if (
req.headers.authorization &&
req.headers.authorization.startsWith('Bearer')
) {
token = req.headers.authorization.split(' ')[1];
} else if (req.cookies.jwt) {
token = req.cookies.jwt;
}
if (!token) {
return next(
new AppError('You are not logged in! Please log in to get access.', 401)
);
}
// rest jwt decode and verification
});
Any solution?
You are not correctly sending the data to the server. The axios#patch signature is this:
axios#patch(url[, data[, config]])
So you should change it to something like this:
await axios.patch(`http://127.0.0.1:3000/users/updateMyPassword`, updatePasswordState, {
headers: {
"Authorization": token
}
});
And on your server side you can access the Authorization header with req.headers.authorization

In Adonis.js Why refreshToken is null?

I am learning Adonisjs and wanted to implement the logout part in my api rest, but I can't due to whenever I will logout refresh_token is requested in the request, but I don't know where this refresh_token comes from. I noticed that when I log in, I get a "requestToken" parameter, but it's null anyway
When I log in it works and returns something like to me:
{
"type": "bearer",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsImlhdCI6MTU3MTAxNTAwMH0.xXX6oDvdvdz59UJ2fstrFmOJEGP8luwKPtTeVF-Y224",
"refreshToken": null
}
Here's the code:
async login ({ request, auth }) {
const { email, password } = request.all()
const token = await auth.attempt(email, password)
return token
}
async logout({ request, response}) {
const rules = {
refresh_token: 'required'
};
const { refresh_token } = request.only(['refresh_token']);
const validation = await validate({ refresh_token }, rules);
const decrypted = Encryption.decrypt(refresh_token);
if (!validation.fails()) {
try {
const refreshToken = await Token.findBy('token', decrypted);
if (refreshToken) {
refreshToken.delete();
response.status(200).send({ status: 'ok' });
} else {
response.status(401).send({ error: 'Invalid refresh token' });
}
} catch (err) {
response.status(401).send({ error: err.toString()});
}
} else {
response.status(401).send(validation.messages());
}
}
}
I tried to take a look at some git api or even adonisjs authentication documentation, but to no avail.
I also tried to add the token that is answered in request "request_token", but it returns invalid token:
{
"error": "Invalid refresh token"
}
So, how can i solve that?
As per adonis doc, follow this api call - which generates refresh token during login - authentication flow
Instruct the JWT authenticator to generate a refresh token as well:
await auth
.withRefreshToken()
.attempt(uid, password)
Ref : https://adonisjs.com/docs/4.1/authentication
I tried using the sample example as ref in the docs https://legacy.adonisjs.com/docs/4.0/authentication#_jwt but didn't work. got
await auth.withRefreshToken().attempt(uid, password)
output
{
"type": "bearer",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsImlhdCI6MTU3MTAxNTAwMH0.xXX6oDvdvdz59UJ2fstrFmOJEGP8luwKPtTeVF-Y224",
"refreshToken": null
}
instead use this
if (await auth.attempt(uid, password)) {
const savedUser = await User.findBy("uid", uid);
await auth.withRefreshToken().generate(savedUser);
}
output
{
"type": "bearer",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1aWQiOjEsImlhdCI6MTYzNDMyMjc2MywiZXhwIjoxNzIwNzIyNzYzLCJpc3MiOiJpc3MifQ.mj48h77eQQZfn7TjqyW_HDv5AIaTGtTabbID3J2Tnqw"
"refreshToken": "d62308224c28a1569b0381c3f2f5b29fORSRBIdsHgfqHsqC+0RJQ3OCNZ/8/XW1NEq4AtEQTUfYonzodsqLUAy+e2sHsjIz"
}

Resources