Github api - how to make authenticated requests? - node.js

I am using Nodejs to write a simple web app that needs to read content from readme files using the GH api.
Everything works, but I am can't sort how to create requests as an authenticated user.
Sorry bit of a noob here :-) but is it not enough to add my client and secret key or an access_token as a parameter to my url? I have tried both and both seem to time out after 60 requests instead the 5000 the docs say.
I have looked at this site Github Rate Limits but I think I have done what it says.
Do I need to add a token on my server? Like how public and private ssh keys work? - Sorry, just trying to get an understanding of this.

This worked for me recently for getting a list of issues from Github. Nothing else set up on server or similar.
I used a token created with https://github.com/settings/tokens/new
const chalk = require("chalk");
const sa = require("superagent");
const { getProperty } = require("../context");
async function getIssues(org) {
try {
const url = `https://api.github.com/orgs/${org}/issues?state=open`;
const apiToken = await getProperty("github.token");
const res = await sa
.get(url)
.set("Authorization", `token ${apiToken}`)
.send();
res.body.forEach(issue => {
console.log(issue.title);
});
} catch (err) {
console.error(err);
}
}
module.exports = getIssues;

Related

Trouble Connecting to Google Cloud IoT via MQTT with Node.js

I'm trying to create a MQTT client that'll connect to the Google Cloud IoT Core, but for some reason, it won't connect at all. Here's what I have so far
mqtt = require("mqtt")
fs = require("fs")
var jwt = require('jsonwebtoken');
const projectId = "my-project"
const deviceId = "my-device"
const registryId = "my-degistry"
const region = "us-central1"
const algorithm = "RS256"
const privateKeyFile = "./rsa_private.pem"
const mqttBridgeHostname = "mqtt.googleapis.com"
const mqttBridgePort = 8883
const messageType = "events"
//The mqttClientId is a unique string that identifies a particular device.
//For Google Cloud IoT Core, it must be the format below
const mqttClientId = `projects/${projectId}/locations/${region}/registries/${registryId}/devices/${deviceId}`
const mqttTopic = `/devices/${deviceId}/${messageType}`;
const createJwt = (projectId, privateKeyFile, algorithm) => {
// Create a JWT to authenticate this device. The device will be disconnected
// after the token expires, and will have to reconnect with a new token. The
// audience field should always be set to the GCP project id.
const token = {
iat: parseInt(Date.now() / 1000),
exp: parseInt(Date.now() / 1000) + 20 * 60, // 20 minutes
aud: projectId,
};
const privateKey = fs.readFileSync(privateKeyFile);
return jwt.sign(token, privateKey, {algorithm: algorithm});
};
//Username field is ignored in Cloud IoT Core, but it must be set to something
//Password field sends a JWT (javascript web token) to authorize the device
//mqtts protocol causes library to connecti using SSL, which is required for IoT Core
const connectionArgs = {
host: mqttBridgeHostname,
port: mqttBridgePort,
clientId: mqttClientId,
username: "unused",
password: createJwt(projectId, privateKeyFile, algorithm),
protocol: "mqtts",
secureProtocol: "TLSv1_2_method"
}
const client = mqtt.connect(connectionArgs)
client.on("connect", (connected)=>{
console.log("Attempting to connect")
if (!connected) {
console.log("Client failed to connect")
} else {
console.log("Client is connected!")
}
})
client.on("error", err => {
console.log(err)
setTimeout(( ()=> {
console.log('Terminating process')
return process.kill(process.pid);
}), 1000);
})
client.on("packetsend", (payload) => {
console.log("Payload has been sent")
return process.kill(process.pid)
})
client.on("packetreceive", packet => {
console.log("Killing")
//return process.kill(process.pid)
})
client.on("reconnect", ()=>{
console.log("Attempting a reconnect")
//return process.kill(process.pid)
})
client.on("close", ()=>{
console.log("A disconnect occurred")
// return process.kill(process.pid)
})
client.on("offline", () => {
console.log("Client is offline")
//return process.kill(process.pid)
})
I'm not getting any errors when I try to connect to the server. In other words, everything seems to be authenticating properly and I get no error messages, but the client never connects to the Cloud and instead repeatedly tries to reconnect in an endless cycle (which is why I included code to kill the script). I tried going through the Google Cloud troubleshooting page but nothing there really seemed to help. I don't get any sort of errors messages or helpful tidbits of information when using the Cloud SDK like the guide suggested.
I've opened up the port 8883 through my firewall just in case that was the issue but it doesn't appear to be.
I based this code off some of Google's guides and based on this guide here. I have a registry, project, and device all set up with a proper RSA key.
So I'm not really sure how to proceed! If there's any additional information that would help, please let me know.
Thank you.
I realized that when I was creating the project and registry on the Google Console, I actually mistyped the name I was intending (I thought it was "testmqtt" but it was actually "tesmqtt").
So if you're having an issue similar to this, I'd suggest trying the follwing:
Make sure your you've spelled everything right. Make sure the project title is correct, the registry title, etc. It sounds dumb but these types of mistakes happen, so it doesn't hurt to check them first. Otherwise you'll overthink things like I did.
Check out this this page for troubleshooting. There's two parts of this troubleshooting page that might really help you. The first is trying to see if you can actually connect to the cloud at all. You can test if you're able to make a connection by issuing a command like openssl s_client -connect mqtt.googleapis.com:8883 on the command line. You'll need to have openssl downloaded in order to issue that command, however. You can see the page I just linked for more details about testing your connection. The second thing you can do is check to see if you have authentication by running a gcloud command using Google's sdk. The troubleshooting page I linked also has more details in this regard.
This quickstart guide is also particularly helpful. It might be confusing to navigate at first but understanding it will be your best bet.
Google's github repository also has some good examples of how to make an mqtt connection to the cloud IoT core.
DavidC in the comments below helped me find some of these helpful links, so credit should go to him.
Apart from the links I provided in the comment section and as additional to what you have found out, some users use the Project Number instead of the Project ID which leads to a similar concern which you have encountered. It really pays to double check everything in your configuration as you troubleshoot.
If you need to have a refresher about the authentication, you may also refer to this link.

Multiple singleton instances across files in TypeScript and NextJs

Recently, I have been working on a personal project involving the creation of some API endpoints using NextJs and TypeScript that call back on the Discord API using discord.js. Please don't get scared off at the mention of the discord API if you have never touched it, I don't think that library is the issue, hence why it is not included in the thread title.
Problem:
I have implemented a singleton for the discord.js API client as the client can take about a second or two to login and initialize, time I don't want to add to each response. This works great on one file/endpoint, where once that file has the instance, it keeps its. However, as soon as I load another file/endpoint, it creates another instance of the singleton, however, after its creation works fine again within that file.
My problem is that I dont want an instance per file, but instead want one instance for the entire application.
DiscordClient.ts:
import { Client } from 'discord.js';
class DiscordClient {
private static discordClient: DiscordClient;
public APIClient: Client;
private constructor() {
this.APIClient = new Client();
this.APIClient.login($TOKEN);
}
public static get Instance() {
if (!this.discordClient) {
this.discordClient = new DiscordClient();
}
return this.discordClient;
}
}
export const DiscordClientInstance = DiscordClient.Instance;
NOTE: token is merely a placeholder for the unique token of my bot application registered with discord.
/pages/api/test1.ts
import { DiscordClientInstance } from "../../DiscordClient";
export default (req, res) => {
let guild = DiscordClientInstance.APIClient.guilds.fetch($GUILD_1_ID)
.then(guild => {
console.log(guild.name);
res.statusCode = 200;
res.json({ name: guild.name });
})
.catch(console.error);
}
/pages/api/test2.ts
import { DiscordClientInstance } from "../../DiscordClient";
export default (req, res) => {
let guild = DiscordClientInstance.APIClient.guilds.fetch($GUILD_2_ID)
.then(guild => {
console.log(guild.name);
res.statusCode = 200;
res.json({ name: guild.name });
})
.catch(console.error);
}
NOTE: $GUILD_#_ID is merely a placeholder for where the the id of the discord server I am fetching would go.
As can be seen above, test1.ts and test2.ts are nearly identical and are inheriting the same const.
If anyone had any clues as to why this is happening, I would be very appreciative. Some people on other sites from my late-night googling have suggested this could be an issue with node, however, I honestly have no clue.
Thanks,
Matt :)
I use this very pattern without issues - Have you tried this in production mode? When in development mode Next.js will compile each page on-demand which I've observed breaking this pattern. Essentially, if you see "compiling..." then you've lost your persistence. In production mode this doesn't happen and you should see your single instance persisted.

How to query the gitlab API from the browser?

Just to give some context, I'd like to implement a blog with gitlab pages, so I want to use snippets to store articles and comments. The issue is that querying the API from the browser triggers a CORS error. Here is the infamous code:
const postJson = function(url, body) {
const client = new XMLHttpRequest();
client.open('POST', url);
client.setRequestHeader('Content-Type', 'application/json');
return new Promise((resolve, reject) => {
client.onreadystatechange = () => {
if (client.readyState === 4) {
client.status === 200
? resolve(client.responseText)
: reject({status: client.status, message: client.statusText, response: client.responseText})
}
}
client.send(body)
})
};
postJson('https://gitlab.com/api/graphql', `query {
project(fullPath: "Boiethios/test") {
snippets {
nodes {
title
blob {
rawPath
}
}
}
}
}`).then(console.log, console.error);
That makes perfect sense, because it would allow to fraudulently use the user's session.
There are several options:
Ideally, I would like to have an option to disable all form of authentication (particularly the session), so I could only access the information that is public for everybody.
I could use a personal access token, but I'm not comfortable with this, because the scopes are not fine-grained at all, and leaking such a PAT would allow everybody to see everything in my account. (doesn't work)
I could use OAuth2 to ask for every reader the authorization to access their gitlab account, but nobody wants to authenticate to read something.
I could create a dummy account, and then create a PAT. That's the best IMO, but that adds some unnecessary complexity. (doesn't work)
What is to correct way to query the gitlab API from the browser?
After some research, I have found this way to get the articles and the comments. The CORS policy was triggered because of the POST request with a JSON content. A mere GET request does not have this restriction.
I could recover the information in 2 times:
I created a dummy account, so that I could have a token to query the API for my public information only,
Then I used the API V4 instead of the GraphQL one:
// Gets the snippets information:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);
// Gets the comments of a snippet:
fetch('https://gitlab.com/api/v4/projects/7835068/snippets/1742788/discussions?private_token=AmPeG6zykNxh1etM-hN3')
.then(response => response.json())
.then(console.log);

How to handle server side error during authentication in react-native

So I was making an app and in that app I have say login with facebook
For login, I am using expo-web-browser
Here is my relevant code,
loginWithFacebook = async () => {
const redirectUrl = await Linking.getInitialURL()
const authUrl = config.backendUrl + '/auth/facebook'
Linking.addEventListener('url', this.handleRedirect)
try {
const authResult = await WebBrowser.openAuthSessionAsync(authUrl, redirectUrl)
console.log(authResult)
Linking.removeEventListener('url', this.handleRedirect)
} catch (err) {
console.warn('ERROR:', err)
}
}
While this works, my problem is on error handling, I am using passport Js on the backend (NodeJS). On successful authentication, I am re-directing to my deep link url
return res.redirect(myapplink://)
and currently, on error (say there is an sql connection error), I am throwing a 500 internal error
return res.status(error.status).send(error.message)
Since typically auth related events are done using href and not using ajaxy request, How would you typically handle the error in this situation? I think the answer for the app and web should be identical but if not can you please suggest the way I can handle error in the app and web.

Is there a way to instantiate a new client on server side by firebase cloud function?

I am developing an app and trying to implement news feed by getstream.io using react native and firebase.
Is there a way to generate user token by using firebase cloud function. If there is, would you please give me a pointer how i can do so? (the snippet of codes in cloud function side and client side would be super helpful..)
I have seen similar questions, only to find out no specific tutorial.. any help is appreciated!
For the cloud function side you need to create a https.onRequest endpoint that calls createUserToken like so:
const functions = require('firebase-functions');
const stream = require('getstream');
const client = stream.connect('YOUR_STREAM_KEY', 'YOUR_STREAM_SECRET', 'YOUR_STREAM_ID');
exports.getStreamToken = functions.https.onRequest((req, res) => {
const token = client.createUserToken(req.body.userId);
return { token };
});
After that, deploy with firebase deploy --only functions in the terminal & get the url for the function from your firebase dashboard.
Then you can use the url in a POST request with axios or fetch or whatever like this:
const { data } = axios({
data: {
userId: 'lukesmetham', // Pass the user id for the user you want to generate the token for here.
},
method: 'POST',
url: 'CLOUD_FUNC_URL_HERE',
});
Now, data.token will be the returned stream token and you can save it to AsyncStorage or wherever you want to store it. Are you keeping your user data in firebase/firestore or stream itself? With a bit more background I can add to the above code for you depending on your setup! 😊 Hopefully this helps!
UPDATE:
const functions = require('firebase-functions');
const stream = require('getstream');
const client = stream.connect('YOUR_STREAM_KEY', 'YOUR_STREAM_SECRET', 'YOUR_STREAM_ID');
// The onCreate listener will listen to any NEW documents created
// in the user collection and will only run when it is created for the first time.
// We then use the {userId} wildcard (you can call this whatever you like.) Which will
// be filled with the document's key at runtime through the context object below.
exports.onCreateUser = functions.firestore.document('user/{userId}').onCreate((snapshot, context) => {
// Snapshot is the newly created user data.
const { avatar, email, name } = snapshot.val();
const { userId } = context.params; // this is the wildcard from the document param above.
// you can then pass this to the createUserToken function
// and do whatever you like with it from here
const streamToken = client.createUserToken(userId);
});
Let me know if that needs clearing up, these docs are super helpful for this topic too 😊
https://firebase.google.com/docs/functions/firestore-events

Resources