Authenticating API using options with got library - javascript

I am attempting to execute a basic API call that requires authorization using the got library. I attempted to use options to verify my username and password, but I get an HTTPerror.
Am I using options correctly?
const got = require('got');
(async () => {
const options = {
username: "myprojectID",
password: "thesecret"
}
try {
let raw_response = await got('https://altona.infura.io/beacon/block?epoch=1', options);
let parsed_response = JSON.parse(raw_response.body);
return parsed_response
} catch (error) {
console.log(error);
}
})()

I was using options incorrectly. Instead of username and password I used the following:
const options = {
auth: "myprojectID:thesecret",
}

Related

SvelteKit: How to call mongodb without using endpoints?

(1/9/2023) Update : SvelteKit now supports server only load functions and Form actions to send requests to the server.
I want to call my database, but I don't want it be able to get accessed by end users by them going to the API endpoint that I set up. I was wondering how I would be able to just call my database from a file in the lib folder and just returning the data there. When I try it I get the error global not defined:
lib/db.js:
import dotenv from "dotenv";
dotenv.config();
import { MongoClient } from "mongodb";
const uri = process.env["MONGODB_URI"];
const options = {
useUnifiedTopology: true,
useNewUrlParser: true,
};
let client;
let clientPromise;
if (!uri) {
throw new Error("Please add your Mongo URI to .env.local");
}
if (process.env["NODE_ENV"] === "development") {
if (!global._mongoClientPromise) {
client = new MongoClient(uri, options);
global._mongoClientPromise = client.connect();
}
clientPromise = global._mongoClientPromise;
} else {
client = new MongoClient(uri, options);
clientPromise = client.connect();
}
export default clientPromise;
routes/items/index.js:
import clientPromise from "$lib/db";
export async function get() {
const client = await clientPromise;
const db = client.db();
const data = await db.collection("items").find({}).toArray();
const items = data.map(({ name }) => ({ name }));
if (items) {
return {
body: {
items,
},
};
}
}
My attempt:
lib/stores/items.js
import clientPromise from "$lib/db";
import { writable } from "svelte/store";
export const items= writable([]);
const fetchItems = async () => {
const client = await clientPromise;
const db = client.db();
const data = await db.collection("items").find({}).toArray();
const items = data.map(({ name }) => ({ name }));
substances.set(items);
};
fetchItems();
Trying the above code in various places always yields a global not defined error in the client.
I found one question from someone with the same problem, but I couldn't figure out how to create a helper file.
Protecting API is done on back-end side. Usually it either server (like NodeJS) or tools Nginx/Apache (proxy, etc.). You're basically looking for Content-Security-Policy topic, which is vaporous but not related to SvelteKit.
Btw, calling DB directly from the Front-end wouldn't be secure and is not possible.
To get data from any database, you should create enpoint
For user authentication, you can create handle hook:
export async function handle({ request, resolve }) {
let user = await authenticate(request)
request.locals.user = user
request.locals.isAuthenticated = !!user
if (request.path.startsWith('/api')) {
if (!user) {
return {
status: 401,
body: JSON.stringify({
error: {
message: 'Unauthorized'
}
})
}
}
const response = await resolve(request)
return response
}

How to migrate request-promise to axios or fetch

I want to code a app with React-Native which loads JSON-files from a website with cookie-authentication.
For testing I tried it in a normal JS-file without React-native and with request-promise.
const fs = require("fs");
const request = require("request-promise").defaults({ jar: true });
async function main() {
var incodeHeader = "";
var incodeToken = "";
try {
const loginResult = await request.post("https://somepage/login.php", {
form: {
client: "XXX",
login: "username",
password: "password",
},
});
} catch (err) {
console.log(err);
}
incodeHeader = getIncodeHeader();
incodeToken = getIncodeToken();
const data = await request.post("https://somepage/load.json", {
headers: {
[incodeHeader]: incodeToken,
},
form: {
max: "10",
},
});
fs.writeFileSync("data.json", data);
}
main();
This worked well, so I wanted to use this method in my App, but I couldn't find a way to use request-promise in React-Native so I decided to use axios.
const axios = require("axios");
const qs = require("qs");
axios.defaults.withCredentials = true;
async function main() {
const data = {
client: "XXX",
login: "username",
password: "password",
};
await axios
.post("https://somepage/login.php", qs.stringify(data))
.catch((err) => console.log(err));
const incodeHeader = getIncodeHeader();
const incodeToken = getIncodetoken();
await axios
.get(
"https://somepage/load.json",
{ data: { max: "5" } },
{
headers: {
[incodeHeader]: incodeToken,
},
}
)
.then((respone) => console.log(respone))
.catch((err) => console.log(err));
}
main();
But in this code not even the login works and I really don't know why. Can somebody tell me how to do this right, or can tell me another solution which works in React-Native?
First, I don't know why you're stringifying the request body in the first request, axios already handle this, you can pass just the data object, maybe it's the solution for your problem.
Second (just a tip). Create a helper object to make http requests and do not instance axios directly, so then, you can change the http request handler in an easy way instead changing it on each file, one day you probably will need to do this if you want to keep your app updated.
Third, don't mix await and then, choose:
try {
const result = await action();
// ...
} catch (err) {
// ...
}
or
action()
.then((result) => {
// ...
})
.catch((err) => {
// ...
});
change await axios.get to await axios.post

Using promises in async function

I'm trying to listen for a Stripe webhook call, then carry out some actions such as sending an email. My site is on Netlify and I've adapted some code I found in a tutorial:
This works locally, but not when I run it as a Netlify function (basically a lambda). Basically, the part from "client.getSpace.." doesn't appear to run at all. I suspect this is something to do with using these .then promises within an async function, but I'm not sure.
require('dotenv').config();
const stripe = require('stripe')(process.env.STRIPE_SECRET_KEY);
const contentful = require('contentful-management');
const client = contentful.createClient({
accessToken:
process.env.CONTENTFUL_CONTENT_MANAGEMENT_ACCESS_TOKEN
});
var postmark = require("postmark");
var serverToken = process.env.POSTMARK_SERVER_TOKEN;
var postmark_client = new postmark.ServerClient(serverToken);
exports.handler = async ({ body, headers }) => {
try {
const stripeEvent = stripe.webhooks.constructEvent(
body,
headers['stripe-signature'],
process.env.STRIPE_WEBHOOK_SECRET
);
if (stripeEvent.type === 'checkout.session.completed') {
console.log('confirm checkout session completed');
const eventObject = stripeEvent.data.object;
const entryId = eventObject.client_reference_id;
let email = "";
let secret = "";
client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
.then(space => space.getEnvironment('master'))
.then(environment => environment.getEntry(entryId))
.then(entry => {
entry.fields.paymentStatus['en-GB'] = 'Paid';
email = entry.fields.email['en-GB'];
return entry.update();
})
.then(entry => entry.publish())
.then(entry => postmark_client.sendEmailWithTemplate({
"From": "x#test.com",
"To": email,
"TemplateId": 12345678,
"TemplateModel": {
"abc": "xyz"
}
}))
.catch(console.error)
}
return {
statusCode: 200,
body: JSON.stringify({ received: true }),
};
} catch (err) {
console.log(`Stripe webhook failed with ${err}`);
return {
statusCode: 400,
body: `Webhook Error: ${err.message}`,
};
}
};
For what it's worth to you and anyone else who comes across this question. I had a similar issue using NextJS on Vercel. I rewrote the .then syntax using async/await and the problem seems to be solved, so far. I'm no expert, but I think in this case you would begin by replacing
client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
.then(space => space.getEnvironment('master'))
with something like
const send = await client.getSpace(process.env.WEBSITE_CONTENTFUL_SPACE_ID)
const getEnvironment = await space.getEnvironment('master')
so on and so forth. I'm not sure how you would rewrite everything else, or if this will even help, but it put me back on track.

Getting a 404 error when using the call log API

I'm using Node and trying to do a simple API call to the call log. My code came from the docs, but modified a bit. Login works just fine so my credentials are ok.
const fetch = require('node-fetch');
const SDK = require('#ringcentral/sdk').SDK;
RINGCENTRAL_CLIENTID = '<SECRET>';
RINGCENTRAL_CLIENTSECRET = '<SECRET>';
RINGCENTRAL_SERVER = 'https://platform.devtest.ringcentral.com';
RINGCENTRAL_USERNAME = '<SECRET>';
RINGCENTRAL_PASSWORD = '<SECRET>';
RINGCENTRAL_EXTENSION = '101';
var rcsdk = new SDK({
server: RINGCENTRAL_SERVER,
clientId: RINGCENTRAL_CLIENTID,
clientSecret: RINGCENTRAL_CLIENTSECRET
});
const platform = rcsdk.platform();
exports.handler = async function(event, context) {
await rcLogin();
let callLog = await rcCallLog();
console.log(callLog);
return {
statusCode: 200,
body: JSON.stringify(callLog)
}
}
async function rcLogin() {
return platform.login({
username: RINGCENTRAL_USERNAME,
password: RINGCENTRAL_PASSWORD,
extension: RINGCENTRAL_EXTENSION
});
}
async function rcCallLog() {
return platform.get('/account/~/extension/~/call-log', {
view: 'Detailed'
})
}
It logs in just fine, but then the callLog call returns errorMessage: '404 Not Found'
So I figured it out. Changing the call to this worked:
return platform.get('/restapi/v1.0/account/~/extension/~/call-log', {
It may just be a bug in the docs for reading the call log (https://developers.ringcentral.com/guide/voice/call-log/reading-call-log).

async authentication using Google APIs

I have the following code:
async function initApi() {
const googleKey = await readJSON(appRoot + '/secrets/google-auth.json');
const jwt = new google.auth.JWT(
googleKey.client_email, null, googleKey.private_key, scopes);
return jwt.authorize();
}
const calendar = {
events: events,
api: google.calendar({
version: 'v3',
auth: this.jwt
}),
list: async function() {
await this.api.calendarList.list();
},
};
module.exports = async () => Object.assign(calendar, { jwt: await initApi() });
I am continuously getting "Error login required". Yet the jwt resolves just fine to an access token when I log the result:
const Calendar = require('./above-code.js');
Calendar().then(c => console.log(c.jwt));
// { access_token: ... }
Calendar().then(c => console.log(c.list());
// Error: login required
I cannot work out why. It is driving me absolutely insane. I want to throw my laptop out a window.
What am I doing wrong? Is this the best pattern using async and await with module.exports? Is there a better way to just return the object so I can call Calendar.list() and get the result without having to jump through the hoops I currently do to just get the API and call the methods directly? E.g.:
const Calendar = require('./above-code.js');
await Calendar.list();
Took a break before I came back to this - the best solution for myself seems to be getting less frustrated before I keep at a frustrating problem.
Firstly, I moved the api property to the initApi function, and returned that there - that seemed to solve my question:
async function initApi() {
const googleKey = await readJSON(appRoot + '/secrets/google-auth.json');
const jwt = new google.auth.JWT(
googleKey.client_email, null, googleKey.private_key, scopes);
jwt.authorize();
return google.calendar({
version: 'v3',
auth: jwt
});
}
const calendar = {
events: events,
list: async function() {
await this.api.calendarList.list();
},
};
module.exports = async () => Object.assign(calendar, { api: await initApi() });
However a second caveat was that now it kept complaining about a client.request not being found - turns out Google has two sets of auth tools.
After changing to google-auth-library (instead of using googleapis's in-built auth.JWT) I got a response back from the server, with no client.request complaints:
const { google } = require('googleapis');
const { JWT } = require('google-auth-library');
...
async function initApi() {
const googleKey = await readJSON(appRoot + '/secrets/google-auth.json');
const jwt = new JWT(
googleKey.client_email, null, googleKey.private_key, scopes);
return await google.calendar({
version: 'v3',
auth: jwt
});
}
Now it works! Hope that helps anybody who has this problem.
EDIT: Furthermore, see Google's example here for loading auth from the downloadable JSON file from the Google API dashboard.
The answer is somewhat hard to follow because of the way the author builds the module object.
Here is a simplified version that uses promises and does not require the google-auth-library.
const { google } = require("googleapis");
function auth() {
const gAccount = //
const jwt = new google.auth.JWT(gAccount.client_email, null, gAccount.private_key, gAccount.scope);
return jwt.authorize().then(() => google.calendar({ version: "v3", auth: jwt }));
}
function listCalendars() {
return auth().then(calendar => {
return calendar
.calendarList.list({ showHidden: true })
.then(res => res.data.items);
});
}
You add jwt to the object, not as a separate variable, so it should be:
google.calendar({
version: 'v3',
auth: this.jwt //here
}),
Object.assign(calendar, { jwt: 'value' }); it is equivalent to calendar.jwt = 'value'. And you refer as if it were done in this way:
let jwt = 'value';
google.calendar({
version: 'v3',
auth: jwt //here
}),

Categories

Resources