Meteor - should inserting data from an api go into a method? - meteor

Real simple and short question here, should I put an API into a method when trying to upload files or keep it on the client side? And also what is the point of methods, I know it's to keep your app safe, but I am not sure how a user would change break the app. Also, can you explain when to use methods?
readImage(e){
let file = e.target.files[0];
const CLOUDINARY_URL = "my_URL";
const CLOUDIARY_UPLOAD_PRESET = "my_Upload_Preset"
let formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", CLOUDIARY_UPLOAD_PRESET)
axios({
url: CLOUDINARY_URL,
method: "POST",
headers: {
"Content-Type": "application/x-www-form-urlencoded"
},
data: formData
}).then(function(res){
console.log(res)
console.log(res.data.secure_url);
}).catch(function(err){
console.log(err);
})
console.log(file);
}

The file upload itself needs to happen on the client, but it is good practice to put any processing into a server method. If you do all the processing on the client, it means that you need to enable database access from the client, meaning that a malicious user could modify your database from the browser console.
In the server you should check if the logged in user has permission to do the operation requested. These methods can also be called by entering commands from the console, but you make the attack surface much smaller by reducing the number of operations available.

Related

What is the difference between mediaLink and SignedUrl in firebase storage

I have a firebase function to upload files to firebase storage, after upload I have to return the url (as Reset response) so that user can view the file
const bucket = admin.storage().bucket();
const [file, meta] = await bucket.upload(tempLocalFile, {
destination: uploadPath,
resumable: false,
public: true,
});
I have two options
1- const signedUrl = await file.getSignedUrl({ action: 'read', expires: '03-09-2491' });
2- meta.mediaLink
SignedUrl will be like https://storage.googleapis.com/web-scanner-dev.appspot.com/pwc%2Fwww.x.com%2F2019-11-17%2Fdesktop%2Fscreenshot-2019-11-17-1125.png?GoogleAccessId=firebase-gcloud%40scanner-dev.iam.gserviceaccount.com&Expires=16447035600&Signature=w49DJpGU9%2BnT7nlpCiJRgfAc98x4i2I%2FiP5UjQipZQGweXmTCl9n%2FnGWmPivkYHJNvkC7Ilgxfxc558%2F%2BuWWJ2pflsDY9HJ%2Bnm6TbwCrsmoVH56nuGZHJ7ggp9c3jSiGmQj3lOvxXfwMHXcWBtvcBaVj%2BH2H8uhxOtJoJOXj%2BOq3EC7XH8hamLY8dUbUkTRtaWPB9mlLUZ78soZ1mwI%2FY8DqLFwb75iob4zwwnDZe16yNnr4nApMDS7BYPxh4cAPSiokq30hPR8RUSNTn2GxpRom5ZiiI8dV4w%2BxYZ0DvdJxn%2FW83kqnjx6RSdZ%2B9S3P9yuND3qieAQ%3D%3D
and mediaLink will be like https://storage.googleapis.com/download/storage/v1/b/web-scanner-dev.appspot.com/o/pwc%2Fwww.x.com%2F2019-11-17%2Fdesktop%2Fscreenshot-2019-11-17-1125.png?generation=1574007908157173&alt=media
What is the pros and cons of each?
The mediaLink does not convey any access permissions on its own -- thus, the object itself will need to be publicly readable in order for end uers to make use of the link (or you will need to be authenticated as an account with read access to that bucket when you execute the link).
On the other hand, a URL returned by getSignedUrl will have a signature that allows access for as long as the URL hasn't expired. Thus, the link alone is sufficient (if temporary) permission to access the blob. Additionally, the URL that is generated maintains the permissions of the user who created it -- if that user loses access to the blob before the link would otherwise expire, the link will no longer function.

Firebase session persistence in Express

I just started learning node, express, and Firebase and after digging around, I've decided to ditch express's express-session API and go with Firebase's authentication system.
I'm trying to build a simple app that can handle multiple user sign-ins with express but I'm lost on where and when to use Firebase functions. I know I need some sort of session on the client side, but I'm unsure how to implement it.
Below is what I want my app to do:
Log in with user credentials
Store user information in a session object
Redirect to the dashboard
Retrieve user details from session object
Here is what I have so far:
app.post('/login', (req, res, next) => {
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(function() {
firebase.auth().signInWithEmailAndPassword(req.body.email, req.body.password).then((user) => {
res.redirect('/dashboard');
})
.catch((err) => {
res.send(err);
});
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
console.log(errorMessage);
});
});
I've read up on Admin SDKs, authChange, tokens and client SDKs. I'm a total newbie at this and I'm blown away by all the information. I feel like I'm missing an onAuthChange statement, but I'm unsure where to put it. This is also a testing nightmare because my local server returns an error when I use persistence.
How can I use session-like objects in Express? What do I need to implement to make sure multiple users can use my app at the same time?
I found my answer. There's no need to initiate sessions in the back end because Firebase functions create a session object in LocalStorage. Powerful stuff.

How to run ElasticSearch on user's devices AND keep ElasticSearch server safe?

Writing a mobile app with Firebase being my backend, also using ES to power my search. I'm completely new to ES.
Suppose each user can publish articles, each of which contains some number of tags, which denotes what this article is about, kind of like questions asked here. Users can search for articles, with tags, and articles containing that tag will be displayed. I manage to do that with Cloud Function, so, the Cloud Function basically looks like this:
exports.articleSearch = functions.https.onRequest((req, res) => {
const { tag } = req.query;
const ElasticSearchConfig = {
uri: '..<my elastic cloud url>/articles/article/_search...',
method: 'GET',
body: ...,
json: true,
auth: {
username: '...<my elastic cloud username>...',
password: '...<my elastic cloud password>...'
}
};
// If succeeds, send results back to user, if not, send error back
request(ElasticSearchConfig).then((results) => ...)
.catch((error) => ...);
});
This works, however, it's a bit slow, because I'm not running ElasticSearch on user's devices, instead, through a cloud function. But if I do run the above code on user's devices, you noticed auth property of ElasticSearchConfig object, I'm basically giving everybody permissions to access and manipulate my ES server. How can I run the above code on user's devices and at the same time, prevent them from reading or writing anything without proper permission?
There's no secure way to do what your asking. Even if it was possible, you don't want that kind of processing client side draining the battery, especially on mobile. Your slow response from cloud functions may be caused from the function entering a timeout state, meaning it hasn't been called in a while.

Encoded / Encrypted body before verifying a Pact

A server I need to integrate with returns its answers encoded as a JWT. Worse, the response body actually is a json, of the form:
{d: token} with token = JWT.encode({id: 123, field: "John", etc.})
I'd like to use a pact verification on the content of the decoded token. I know I can easily have a pact verifying that I get back a {d: string}, I can't do an exact match on the string (as the JWT contains some varying IDs). What I want is the following, which presumes the addition of a new Pact.JWT functionality.
my_provider.
upon_receiving('my request')
.with(method: :post,
path: '/order',
headers: {'Content-Type' => 'application/json'}
).will_respond_with(
status: 200,
headers: {'Content-Type' => 'application/json; charset=utf-8'},
body: {
d: Pact::JWT( {
id: Pact.like(123),
field: Pact.term(generate: "John", matcher: /J.*/
},signing_key,algo
)
})
Short of adding this Pact::JWT, is there a way to achive this kind of result?
I am already using the pact proxy to run my verification. I know you can modify the request before sending it for verification (How do I verify pacts against an API that requires an auth token?). Can you modify the request once you receive it from the proxy server?
If that's the case, I can plan for the following work around:
a switch in my actual code to sometimes expect the answers decoded instead of in the JWT
run my tests once with the swich off (normal code behaviour, mocks returns JWT data encoded.
run my tests a second time with the swich off (code expect data already decoded, mocks return decoded data.)
use the contract json from this second run
hook into the proxy:verify task to decode the JWT on the fly, and use the existing pact mechanisms for verification. (Step that I do not know how to do).
My code is in ruby. I do not have access to the provider.
Any suggestions appreciated! Thanks
You can modify the request or response by using (another) proxy app.
class ProxyApp
def initialize real_provider_app
#real_provider_app = real_provider_app
end
def call env
response = #real_provider_app.call(env)
# modify response here
response
end
end
Pact.service_provider "My Service Provider" do
app { ProxyApp.new(RealApp) }
end
Pact as a tool, I don't expect it to give this behavior out of the box.
In my opinion, the best is,
Do not change source code only for tests
Make sure your tests verifies encoded json only (generate encoded expected json in test & verify that with actual)

Meteor.user() on iron-router server side

How can check, on server side route, if user is logged?
I would add check on 'before', but Metor.user() don't work here.
thanks in advance.
p.s. I have found How to get Meteor.user() to return on the server side?, but not work on iron-router
I'm afraid that this is not possible. I guess that the problem comes from the fact that you're trying to connect to the server with two different protocols - both literally and in logically - so there is no obvious way to relate this two actions.
There is, however, a pretty simple solution that may suit your needs. You'll need to develop a simple system of privileges tokens, or secret keys, or whatever you call them. First, create a server method
var Secrets = new Meteor.Collection("secrets"); // only on server!!!
Meteor.methods({
getSecretKey: function () {
if (!this.userId)
// check if the user has privileges
throw Meteor.Error(403);
return Secrets.insert({_id: Random.id(), user: this.userId});
},
});
Then, you can now use it on the client to get the secretKey which attach to your AJAX request (or something), either within the HTTP header or in the URL itself. Fear not!
They will all be encrypted if you're using HTTPS.
On the server side you can now retrieve the secretKey from the incoming request and check if it is present in the Secrets collection. You'll know then if the user is granted certain privileges or not.
Also you may want to remove your secret keys from the collection after some time for safety reasons.
If what you're looking to do is to authenticate the Meteor.user making the request, I'm currently doing this within the context of IronRouter.route(). The request must be made with a valid user ID and auth token in the header. I call this function from within Router.route(), which then gives me access to this.user:
###
Verify the request is being made by an actively logged in user
#context: IronRouter.Router.route()
###
authenticate = ->
# Get the auth info from header
userId = this.request.headers['x-user-id']
loginToken = this.request.headers['x-auth-token']
# Get the user from the database
if userId and loginToken
user = Meteor.users.findOne {'_id': userId, 'services.resume.loginTokens.token': loginToken}
# Return an error if the login token does not match any belonging to the user
if not user
respond.call this, {success: false, message: "You must be logged in to do this."}, 401
# Attach the user to the context so they can be accessed at this.user within route
this.user = user
###
Respond to an HTTP request
#context: IronRouter.Router.route()
###
respond = (body, statusCode=200, headers={'Content-Type':'text/json'}) ->
this.response.writeHead statusCode, headers
this.response.write(JSON.stringify(body))
this.response.end()
This code was heavily inspired by RestStop and RestStop2. It's part of a meteor package for writing REST APIs in Meteor 0.9.0+ (built on top of Iron Router). You can check out the complete source code here:
https://github.com/krose72205/meteor-restivus

Resources