Make an API endpoint private - angularjs

I'm developing a little webApp with AngularJS, express, nodejs and Passportjs. I have been creating endpoints on my API on demand but now I have a problem.
I have an endpoint which cannot be called by the users when they want. This API call is made when a user make an specific action in the app, so they earn points.
The endpoint is something like /api/users/updatePoints and I don't want the users with the developers tools resending the call and earning points they don't deserve.
How could I accomplish this? I have been thinking for a day but I can't think of anything reasonable.
Thank you in advance :)
--EDIT--
At last I have just deleted that ENDPOINT and write directly in the database in server-side. Not the solution I wanted but good enough. Thank you!!

It's already too late to answer but I think this could help someone.
To Privatize an endpoint, Allow only your whitelisted Origins by setting the respective response headers and for all other users send a 403 status code (Which implies forbidden, 401 says try again).
Here is an example of middleware that privatizes endpoints.
module.exports = function (allowedOrigins) {
const whitelistedOrigins = Array.isArray(allowedOrigins) ? allowedOrigins : [allowedOrigins];
return function (req, res, next) {
const origin = req.headers.origin;
if (whitelistedOrigins.indexOf(origin) > -1) {
res.setHeader("Access-Control-Allow-Origin", origin);
next();
} else {
res.status(403).json({
msg: "This is a private Endpoint, Please contact the Admin",
});
}
};
};
Here is an example of usage.
const privatizeEndpoint = require("../customMiddlewares/privatizeEndpoint");
router.post("/someEndpoint", privatizeEndpoint(["http://myprivate.domain.com"]), (req, res) => {
console.log("Inside Private Endpoint");
});
However, your endpoint will be still exposed but at least it will be served only to your whitelisted domains.
Notice that even requests without origin will be blocked (curl commands or through POSTMAN).

Related

GCP Server to Server Authentication with Service Account

I'm trying to authenticate a request from my Google Cloud Function to my API on App Engine (Standard environment).
I have something working, but I'm new to OAuth2 and am looking for a sanity check.
In my Cloud Function, I send an authenticated request to my API doing the following:
import { GoogleAuth } from 'google-auth-library';
// Send Request Code:
const auth = new GoogleAuth();
const tokenClient = await auth.getIdTokenClient(`/protectedEndpoint`);
await tokenClient.request({
url: `https://${process.env.GCLOUD_PROJECT}.appspot.com/protectedEndpoint`,
method: 'POST',
});
In the API (on App Engine), I do the following:
import { GoogleAuth } from 'google-auth-library';
// Handle Request Code:
const token = <Bearer token parsed from request headers>
const googleAuth = new GoogleAuth();
const tokenClient = await googleAuth.getIdTokenClient('');
const loginTicket = await tokenClient.verifyIdToken({
idToken: token,
audience: '/protectedEndpoint',
});
if (loginTicket.getUserId() !== process.env.SERVICE_ACCOUNT_ID)) {
throw new Error('Unauthenticated Service Account');
}
return 'Successful Authentication'
Note: In both cases, I'm using Google's default application credentials to initialize the GoogleAuth client. (my Default App Engine service account)
This all works. My function sends a request to my API, and my API is able to parse the bearer token and tell me that it came from my approved service account... but I'm not 100% confident that this is actually secure. Is it possible for someone to spoof my service account without having its credentials?
Thanks in advance!
Is it possible for someone to spoof my service account without having
its credentials?
A precise answer requires the specification of time. Given enough time and processing power, any authentication/authorization/encryption/hashing/signing method can be broken.
A Google service account contains an RSA 2048-bit private key. Current guesstimates are 300 trillion years to break RSA 2048 bit encryption. With the rapid advances in computers, let's assume your data will probably not be of any use/value by the time RSA is broken.
The private key is used to sign a JWT. The Signed JWT is used to request OAuth Access/Identity Tokens.
Spoofing would require signing with the same private key. Therefore, spoofing is not possible with today's technology.
Stealing/leaking the private key or the generated OAuth tokens is the only realistic method today.

Why does a POST request become a GET request in Microsoft Edge?

I'm using Axios and React in my frontend app. When I'm trying to send POST request over HTTPS with Axios (xhr, fetch) and faced with the strange issue - my POST request turns into GET in Edge dev tools.
Here is my request:
const response = await axios.post(
config.local + "/api/login/credentials",
{
login,
password
}
);
Then I tried to dig dipper - created a simple HTTPS server and tried to send POST request from the client.
const https = require('https');
const fs = require('fs');
const options = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt')
};
const PORT = 8188;
function handleRequest(req, res){
console.log(req.method);
}
const server = https.createServer(options, handleRequest);
server.listen(PORT, function(){
console.log("Server listening on: https://localhost:" + PORT);
});
And then, as I understand it, that request does not reach the server.
Here are some links:
Issue link 1
Issue link 2
Is there any error in console? You could use Fiddler to trace the network traffic and see the details. Also mentioned in the first link you provided, you could also try the two solutions in your GitHub link:
Solution 1:
My issue is caused by HTTPS; the backend is requiring HTTPS while I send an HTTP post from front side. Now I fixed by changing both to HTTPS.
Or Solution 2:
I solved it by passing the data in a different format using the "URLSearchParams " class.
I had the same problem with:
Microsoft Edge 44.18362.267.0
Microsoft EdgeHTML 18.18362
Windows 10
I think the problem is that Edge only supports certain data types in post requests. If you want to use the content-type 'application/x-www-form-urlencoded' then use URLSearchParams to make it work in Edge and other browsers like Firefox and Chrome. Passing a querystring seems not to work in Edge even if it does in other browsers.
Modifying the original post source code, the result would be:
import Axios from 'axios'
import Promise from 'es6-promise'
Promise.polyfill()
const URL= 'http://192.168.0.112/account/login/username'
// Use URLSearchParams instead:
const dataParams = new URLSearchParams()
dataParams.append('username', 'admin')
dataParams.append('password', 'admin')
Axios.post(URL, dataParams, {
// if you still have problems try more specific options like:
// withCredentials: true,
// crossdomain: true,
// ...
})
.then(res=>{
console.log(res)
}
)
.catch(error=>{
console.log(error)
}
)
Aside from that, the issue in your question is usually caused by CORS. If you use CORS and request an untrusted origin, then Microsoft Edge will only send the GET request and lead to the failure of other requests. You could also refer to this thread to understand why CORS requests fail in Microsoft Edge but work in other browsers. Microsoft Edge also uses Enhanced Protected Mode, the outcome is that: if the site is trusted, it will make two requests, OPTIONS and GET, but if it's not trusted, it only makes the GET request which causes it to fail.
In my case problem was caused by a self-sign certificate. As soon as I started using normal certificate everything began to work.

How to use the SalesForce API?

I'm trying to use an express/node application to make an api call to the salesforce api and return me data according to the records I have with the account.
But it is returning this:
[{"errorCode":"NOT_FOUND","message":"The requested resource does not exist"}]
Currently my code looks like this
const express = require('express')
const app = express();
var request = require('request');
app.get('/',(req,res, next) =>{
request({
url: 'https://nav4.lightning.force.com/services/data"',
}).pipe(res);
});
})
const port = process.env.PORT || 3000; app.listen(3000, ()=>
console.log(`listening on port ${port}`))
I think my URL is wrong and might need authentication also.
You do need to authenticate first to https://login.salesforce.com. I would suggest reading and following the documentation at their documentation. The username password flow is only recommended for testing purposes though, so once you get it working you might want to look into a different oauth flow if you are building a real application depending on your use case.
For consume the Salesforce API below are the Details to notes.
Find which api to consume REST OR SOAP
Needs :
Base URL
Endpoint URL
Consumer Key and secret key
This are the Point To archive
Below are for Your Reference
https://developer.salesforce.com/page/Consuming_Force.com_SOAP_and_REST_Web_Services_from_.NET_Applications
https://trailhead.salesforce.com/content/learn/modules/apex_integration_services

Using Firebase Auth to access the Google Calendar API

I am building a web application using AngularJS, Firebase (SDK v3) and Google Calendar API. I'm authenticating users using Google OAuth. My purpose is to be able to create calendar events from database nodes in Firebase. So far I've managed to request access to the calendar scope with:
_authProvider = new firebase.auth.GoogleAuthProvider();
// Get permission to manage Calendar
_authProvider.addScope("https://www.googleapis.com/auth/calendar");
_fbAuthObject.signInWithRedirect(_authProvider);
I'm authenticating with the redirect flow so the authentication redirect is available as:
_fbAuthObject.getRedirectResult()
.then(function _readToken(result) {
if (result.credential) {
_googleToken = result.credential.accessToken;
var authHeader = 'Bearer '+ _googleToken;
// Just a test call to the api, returns 200 OK
$http({
method: 'GET',
headers: {
'Authorization': authHeader
},
url: 'https://www.googleapis.com/calendar/v3/users/me/calendarList/primary'
})
.then(function success(response) {
console.log('Cal response', response);
},
function error(response) {
console.log('Error', response);
});
However, it seems like outside the initial login it's not possible to get the Google access token through the Firebase SDK. It seems only possible to access the Firebase JWT token, no use with the Calendar API. I could store the access token, but this wouldn't resolve the problems when refreshing the token, etc. Is there any way to get the current Google Access token with Firebase SDK and if not, what other solutions is there to the problem without having to authenticate the user twice?
UPDATE 1:
Seems like someone else has struggled with similar problems with Facebook authentication. On that question there was a link to the Firebase documentation stating that Firebase Authentication no longer persists the access token. So how can I handle token refreshes? Is there really no answer to this?
UPDATE 2:
So, I contacted Firebase Support with a feature request about this problem and they gave me the following answer:
Thanks for taking your time to write us.
I've got your point here, this is indeed a good suggestion. We're definitely aware that many users, such as yourself, would like OAuth feature that will access token upon refresh. We're exploring potential solutions, but I cannot guarantee if this will be available anytime soon. We'll keep your feedback in consideration moving forward though. Continuous improvement is very important for our community, so thanks for bringing this up!
Keep an eye out on our release notes for any further updates.
So It seems like the access tokens are not available through the Firebase SDK at the moment. I'm still trying to find a workaround, so if anyone has ideas about a valid solution I'd be glad to hear them. And of course, I'll be posting it here if I ever find a working solution myself.
I finally got around this problem by handling the authentication outside Firebase with the Google APIs JavaScript client. This solution requires including the Google auth client as documented here. Manually handling the Firebase sign-in flow is documented here.
gapi.auth2.getAuthInstance().signIn()
.then(function _firebaseSignIn(googleUser) {
var unsubscribe = firebase.auth().onAuthStateChanged(function(firebaseUser) {
unsubscribe();
// Check if we are already signed-in Firebase with the correct user.
if (!_isUserEqual(googleUser, firebaseUser)) {
// Build Firebase credential with the Google ID token.
var credential = firebase.auth.GoogleAuthProvider.credential(
googleUser.getAuthResponse().id_token);
// Sign in with credential from the Google user.
return firebase.auth().signInWithCredential(credential)
.then(function(result) {
// other stuff...
});
The _isUserEqual function:
function _isUserEqual(googleUser, firebaseUser) {
if (firebaseUser) {
var providerData = firebaseUser.providerData;
for (var i = 0; i < providerData.length; i++) {
if (providerData[i].providerId === firebase.auth.GoogleAuthProvider.PROVIDER_ID &&
providerData[i].uid === googleUser.getBasicProfile().getId()) {
// We don't need to reauth the Firebase connection.
return true;
}
}
}
return false;
}
Now I can reference the access token like this:
var user = gapi.auth2.getAuthInstance().currentUser.get();
return user.getAuthResponse().access_token;
This still isn't the ideal solution for me, but it works for now, and I'm able to authenticate to both Firebase and the Calendar API.

Responding with data to a Slack url in Express

I have the following route in my Node backend with express middleware. My app uses the Slack backslash api to post a link to a user's channel. When user clicks it opens a form on an angular app hosted on heroku.
What I am looking to do is update the Slack user when the form get submitted.
So the question, how can I send a post request to slack url when the route below gets triggered(obviously the res is pointing to the /update). I've researched quite a bit and tried rewriting headers and using low level http methods, but I have a feeling there is a better solution to this. Many thanks for your help.
app.post("/update", function(req,res,next) {
res url -> http://slackapi/12345/
res.json({"text":"hello":}
})
res is not supposed to be sent to slack, this is the response to whoever contacted you. you need to send a different request to slack, and return whatever you want with res.
You can use request module to generate a http request to slack.
var request = require('request');
app.post("/update", function(req,res,next) {
request('http://slackapi/12345/', function (error, response, body) {
if (!error && response.statusCode == 200) {
console.log(body);
res.json({"text":"some answer to the requester"});
} else {
res.json({"text":"notify requester sending to slack falied"});
}
});
});

Resources