password reset (Parse-server/Heroku) -> {"error":"unauthorized"} - http

On an app (ParseServer) that I have with HEROKU/mLab, providing password reset for the user.
After I click the link to reset my account password, I can only see this (in the browser):
{"error":"unauthorized"}
and am not able to reset my password as I should.
Does anyone know how to fix this problem?
For reference here is some code, which may be useful to better grasp the issue:
var api = new ParseServer({
databaseURI: databaseUri,
cloud: process.env.CLOUD_CODE_MAIN || __dirname + '/cloud/main.js',
appId: process.env.APP_ID || 'myAppId',
masterKey: process.env.MASTER_KEY || '',
serverURL: "https://myapp.herokuapp.com/parse",
publicServerURL: 'https://myapp.herokuapp.com/parse',
appName: 'TheApp',
liveQuery: {
classNames: ["Stuff_List"]
},
verifyUserEmails: true,
emailAdapter: {
module: '#parse/simple-mailgun-adapter',
options: {
fromAddress: 'sadguy#hmail.cor',
domain: 'dmn.net',
apiKey: process.env.EML_ADAPTER_KEY
}
}
});

I guess this could also happen to other people so I post the solution that I ended up finding. Hoping to avoid someone to lose a few hours like I did. The problem was in fact, rather basic (somewhat disappointing).
The issue was the APP_ID. It is used as part of the URL the user receives (for password resetting), so if it contains weird characters, that you may have chosen to make the ID complicated. It will break the URL. I guess, if one chooses "abcd1234" there will be no problem at all. But you may want to have something different for other reasons. So one has to find a good balance, being careful about the characters used in APP_ID.
Once I fixed that all went as expected.

Related

Firebase: Delete user analytics data - userdeletionRequests:upsert - GDPR

My question is, how can I delete a users analytics data from firebase using "Google User Deletion API" and its method: userdeletionRequests:upsert? This is important for me to fully fulfill GDPR.
I tried searching for this, but didn't a solution for using it in combination with "NodeJS" and "firebase-cloud-functions".
My biggest problem is, how I get the access, token, this is what I have for now:
const accessToken = (await admin.credential.applicationDefault().getAccessToken()).access_token;
return ky.post(constants.googleUserDeletionURL, {
body: JSON.stringify({
kind: "analytics#userDeletionRequest",
id: {
type: "USER_ID",
userId: uid,
},
propertyId: constants.googleUserDeletionPropertyId,
}),
headers: {
"Authorization": `Bearer ${accessToken}`,
},
}).catch((err) => {
functions.logger.log(`An Error happened trying to delete user-anayltics ${(err as Error).message}`);
});
But I always get An Error happened trying to delete user-anayltics Request failed with status code 403 Forbidden
Okay, after some painful and long days (literally took me like >20 hours), I've figured out how to achieve this request. Here is a step for step guide:
Step 1 - Needed Dependencies
To send a post-request to google, we need a http-client-library. I've choosen "ky", so we need to install it first with:
npm install ky
Furthermore, we need to create or OWN oAuth2 token, otherwise the post request will be denied with "error 403". To create our own oAuth token, we need another dependency:
npm install #googleapis/docs
Step 2 - Needed Google Property ID
With your request, Google needs to know which property-id / project you are targeting (where it should later delete the user-analytics-data). To get this property-id, we need to log in into GCP via Firebase (not the "real" GCP, the one via Firebase).
For this, go into your "console.firebase.google.com" → Select your project → Analytics Dashboard → "View more in Google Analytics (button at the right corner)"
Write "property-id" into the search field and then save it somewhere (we need it later)
Step 3 - Creating Client-Secret
The third step is to create a service-account, which we will later add into our functions-folder, in order to create our oAuthClient (don't worry, you will see what I mean to a later point)
To create your new service.json, log in to google cloud platform via "https://cloud.google.com/" and then follow the pictures:
FIRST:
SECOND:
THIRD:
FOURTH:
FIFTH
Step 4 - Download JSON
After we created our "oAuth-Deleter service-account", we need to manually download the needed JSON, so we can paste it into our functions-folder.
For this, select "oauth-deleter#your-domain.iam.gserviceaccount.com" under "Service Account"
Then click on "Keys" and "Add key", which will automagically download you a service-json (SELECT Key type → JSON → Create).
Step 5 - Paste JSON file into your functions-folder
To loosen up the mood a bit, here is an easy step. Paste the downloaded JSON-File into your functions-folder.
Step 6 - Grant Access to our new created oAuth-Delelter-Account
Creating the service-account and giving it access in the normal GCP is not enough for Google, so we also have to give it access in our Firebase project. For this, go back into your "GCP via Firebase (see Step 2)" → Click on Setting → "User Access for Account" → Click on the "plus"
Then click on "Add user" and write the email we copied before into the email field (the email from Step 3, Picture FOURTH "Service-Account ID). In our case, it is "oAuth-Deleter#your-domain.iam.gserviceaccount.com". Furthermore, it needs admin-access:
Step 6 - The code
Now, after these million unnecessary but necessary steps, we get to the final part. THE DAMN CODE. I've written this in typescript with "compilerOptions" → "module": "esnext", "target": "esnext". But I am sure that you are smart enough to change the code after completing this many steps :)
import admin from "firebase-admin";
import functions from "firebase-functions";
import ky from "ky";
import docs from "#googleapis/docs";
import { UserRecord } from "firebase-functions/v1/auth";
export const dbUserOnDeleted = functions.
.auth
.user()
.onDelete((user) => doOnDeletedUser(user))
----------------------------
export asnc function doOnDeletedUser/user: UserRecord) {
try {
const googleDeletionURL = "https://www.googleapis.com/analytics/v3/userDeletion/userDeletionRequests:upsert"
// Step 1: Paste propertyID: (See Step 2)
const copiedPropertyID = "12345678"
// Step 2: Create oAuthClient
const oAuthClient = new docs.auth.GoogleAuth({
keyFile: "NAME-OF-THE-FILE-YOU-COPIED-INTO-YOUR-FUNCTIONS-FOLDER",
scopes: ["https://www.googleapis.com/auth/analytics.user.deletion"]
});
// Step 3: Get user uid you want to delete from user-analytics
const uid = user.uid
// Step 4: Generate access token
// (this is the reason why we needed the 5 steps before this)
// yup, a single line of code
const accessToken = await oAuthClient.getAccessToken() as string;
// Step 5: Make the request to google and delete the user
return ky.post(googleDeletionURL, {
body: JSON.stringify({
kind: "analytics#userDeletionRequest",
id: {
type: "USER_ID",
userid: uid
},
propertyId: copiedPropertyID
}),
headers: {
"Authorization": "Bearer " + accessToken,
}
});
} catch (err) {
functions.logger.error(`Something bad happened, ${(err as Error).message)`
}
}
Afterthoughts
This was and probably will be my longest post at stack overflow forever. I have to say that it was a pain in the a** to get this thing to working. The amount of work and setup that is needed to simply delete a data from one endpoint is just ridiculous. Google, please fix.

How to delete a large node in firebase

I have a Firebase child node with about 15,000,000 child objects with a total size of about 8 GB of data.
exampele data structure:
firebase.com/childNode/$pushKey
each $pushKey contains a small flat dictionary:
{a: 1.0, b: 2.0, c: 3.0}
I would like to delete this data as efficiently and easy as possible. How?
What i Tried:
My first try was a put request:
PUT firebase.com/childNode.json?auth=FIRE_SECRET
data-raw: null
response: {
"error": "Data requested exceeds the maximum size that can be accessed with a single request. Contact support#firebase.com for help."
}
So that didn't work, let's do a limit request:
PUT firebase.com/childNode.json?auth=FIRE_SECRET&orderBy="$key"&limitToFirst=100
data-raw: null
response: {
"error": "Querying related parameters not supported on this request type"
}
No luck so far :( What about writing a script that will get the first X number of keys and then create a patch request with each value set to null?
GET firebase.com/childNode.json?auth=FIRE_SECRET&shallow=true&orderBy="$key"&limitToLast=100
{
"error" : "Mixing 'shallow' and querying parameters is not supported"
}
It's really not going to be easy this one? I could remove the shallow requirement and get the keys, and finish the script. I was just hoping there would be a easier/more efficient way???
Another thing i tried were to create a node script that listen for childAdded and then directly tries to remove those children?
ref.authWithCustomToken(AUTH_TOKEN, function(error, authData) {
if (error) {console.log("Login Failed!", error)}
if (!error) {console.log("Login Succeeded!", authData)}
ref.child("childNode").on("child_added", function(snap) {
console.log(`found: ${snap.key()}`)
ref.child("childNode").child(snap.key()).remove( function(err) {
if (!err) {console.log(`deleted: ${snap.key()}`)}
})
})
})
This script actually hangs right now, but earlier I did receive somethings like a max stack limit warning from firebase. I know this is not a firebase problem, but I don't see any particular easy way to solve that problem.
Downloading a shallow tree, will download only the keys. So instead of asking the server to order and limit, you can download all keys.
Then you can order and limit it client-side, and send delete requests to Firebase in batches.
You can use this script for inspiration: https://gist.github.com/wilhuff/b78e7391396e09f6c614
Use firebase cli tool for this: firebase database:remove --project .
In Browser Console this is fastest way
database.ref('data').limitToFirst(10000).once('value', snap => {
var updates = {};
snap.forEach(snap => {
updates[snap.key] = null;
});
database.ref('data').update(updates);
});

GA Management API, Insert filter: lowercaseDetails.field not valid?

I am working on a web tool to insert filter through management API.
When creating lowercase function, I used strings such as 'Hostname' or 'hostname' in lowercaseDetials.field, but I get an 400 error and the detail is 'lowercaseDetails.field is required'. Seems that whatever I passed on was not in the correct format?
Anyone has an idea how I should specify this 'field'? I imagined it should just be the string in the option list in GA? I can't see anything in google developer docs
var request = gapi.client.analytics.management.filters.insert(
{
'accountId': accountId,
'resource': {
'name': filterName,
'type': 'LOWERCASE',
'lowercaseDetails': {
'field': "Hostname"
}
}
});
Any help will be very much appreciated!
It turned out it should be all capitalized, also, a space should be replaced with an underscore.

firebase Not saving data

Im using firebase and for some reason my code is not pushing data and im not seeing the data in the firebase url. My code is this
var chatref = new Firebase('https://flockedin.firebaseio.com/');
chatref.push({ ChatId: $('#conversationId').text(), User: $('#username').html(), Message: $('#txtchat').val() }, function (response) { if (response) { alert('saved'); } });
i get the alert 'saved' but i cant find the data in firebase url.
Also what is the use of API Key that is given for each url in the firebase.
UPDATE:This happens in IE10. In other browsers it works fine
IE10 seems to be working for me. Can you give more details about what version you are using (desktop / tablet / phone?).
Also, is it possible that your jquery selectors were returning null? If all of the values in the object that you set evaluate to null, Firebase treats it like a call to remove. See the note in the docs for set: https://www.firebase.com/docs/javascript-client/firebase/set.html
Try going to your FireBase Console->Database->Rules and set them to:
{
"rules": {
".read": true,
".write": true
}
}
Make sure your app is not public when you do this. Lmk if it helps.

Meteorjs - Online users

Is there a way to get a list of all currently connected users? I have checked many of the chatroom tutorials and none of them provide this methodology. Is it even possible and if so how would one go about implementing it correctly the Meteor way?
I've managed to figure out a way to do this without using inefficient keep-alives. Basically, user logins or reconnects set the profile.online to true, and logouts or disconnects set it to false. You can add it as a Meteorite smart package and try it out here:
https://github.com/mizzao/meteor-user-status
Fixes or improvements are appreciated.
We accomplished this by setting an online property when a user logs in, and then periodically pinging (every 10 seconds) and setting any inactive users to offline. Not ideal, but it works. Would love to see this feature in Meteor. Here is the pinging function.
Meteor.setInterval(function () {
var now = (new Date()).getTime();
OnlineUsers.find({'active':true,'lastActivity': {$lt: (now - 30 * 1000)}}).forEach(function (onlineActivity) {
if(onlineActivity && onlineActivity.userId) {
OnlineUsers.update({userId:onlineActivity.userId},{$set: {'active': false}});
Meteor.users.update(onlineActivity.userId,{$set: {'profile.online': false}});
}
});
}, 10000);
Old question, but for anyone looking into this there is now a meteor package that monitors client to server connections. It's called meteor user status and can be found on github.
I have ended up with this.
Server code:
Meteor.startup ->
Meteor.methods
keepalive: (params) ->
return false unless #userId
Meteor.keepalive ?= {}
Meteor.clearTimeout Meteor.keepalive[#userId] if Meteor.keepalive[#userId]
Meteor.users.update #userId, $set: {'profile.online': true}
Meteor.keepalive[#userId] = Meteor.setTimeout (=>
delete Meteor.keepalive[#userId]
Meteor.users.update #userId, $set: {'profile.online': false}
), 5000
return true
Client code:
Meteor.startup ->
Meteor.setInterval (->
Meteor.call('keepalive') if Meteor.userId()
), 3000
What I am doing is to record the user's online status when the browser tab of the meteor app has or loses the focus. Then filter the users who are online. There must be better solutions but this one works.
if Meteor.is_client
Meteor.startup ->
$(window).focus ->
Meteor.call 'online', true
$(window).blur ->
Meteor.call 'online', false
else
Meteor.methods
online: (isOnline=true) ->
Meteor.users.update Meteor.userId(), $set: online: isOnline
Then you can use
Meteor.users.find online: true
to filter online users.
BTW, don't forget to publish users with online field.
Meteor.publish null, ->
Meteor.users.find {}, fields: online: 1
My recommendation for this case is danimal:userpresence because there are some more benefits:
Track user online status over multiple servers
Contain httpHeaders so if you want to check if receiver has checked the going messages or not
Very simple code to read

Resources