How do you get the _id of the user that logged in. I have tried the following combinations and I get errors, or undefined
Upon user creation, the user is automatically signed into the application. Is the user that is returned by the Accounts.onCreateUser function occurring after the user is logged in?
Accounts.onLogin(function(){
var user = this.userId / Meteor.user() / Meteor.user()._id
console.log(user)
})
http://docs.meteor.com/#/full/accounts_onlogin
The Accounts.onLogin(function(){}), come with 1 parameter user.
When it is known which user was attempting to login, the Meteor user
object. This will always be present for successful logins.
from the docs.
So this should work.
Accounts.onLogin(function(user){
console.log(user.user._id)
});
And you should see, all the user document into the server console,check this meteorpad for an example.
NOTE: this feature is only available on server side check this Hooks Accounts.onLogin/onLoginFailure should be available on client
You can always get the _id of the logged-in user via Meteor.userId(). This also works inside the Accounts.onLogin callback
Accounts.onLogin(function() {
console.log(Meteor.userId());
})
Related
Precondition
$npm install --save firebase#4.11.0
issue
I'm using firebase authentication on my web application.
In my app, I implemented onAuthStateChanged for client side js like below.
firebase.auth().onAuthStateChanged((user) => {
if(user) {
//logged in
} else {
//do sth
}
});
After login, I confirmed this method will return actual user obj, but if I refresh the page, then user might be null.
Curiously, sometimes user won't be null.
I'm afraid there are some limitation of calling onAuthStateChanged, but currently I have no idea.
How should I deal with this issue?
update
Let me share my minimal example.
My app is working with express.js.
There are two URLs like below.
/login
/main
In the login page, I implemented authentication method.
If the login is successfully finished, then user will be redirected to '/main'.
//login.js
import firebase from 'firebase';
var config = {...};
firebase.initializeApp(config);
var provider = new firebase.auth.GoogleAuthProvider();
firebase.auth().signInWithPopup(provider)
.then((result) => {
return result.user.getIdToken(true);
}).then((idToken) => {
if(idToken) {
location.href = '/main';
}
});
In the main page, there is no login method.
main.js is only checking whether user is logged in.
//main.js
import firebase from 'firebase';
var config = {...};
firebase.initializeApp(config);
firebase.auth().onAuthStateChanged((user) => {
if (user) {
//initialize main page.
} else {
location.href = '/login';
}
}
I think login status is stored on LocalStorage of web browser.
This means that, after finishing loading of main.js, onAuthStateChanged will be automatically fired with user information, but not working as I expected.
I'm sure that persistence of login information is correct because official document says the default setting is LOCAL for web client.
https://firebase.google.com/docs/auth/web/auth-state-persistence
my question
Should I implement onAuthStateChanged with another way?
How can I ensure user is logged in after reload?
e.g.
import $ from 'jquery';
$(document).on('ready', () => {
onAuthStateChanged((user) => {...});
});
Or could you show me the correct way?
Workaround
I decided to remove session and set redirection to login page if null is returned. This is not a solution, but a workaround currently...
You're not calling onAuthStateChanged. Instead you're telling Firebase to call you when the authentication state changes, which may happen a few times when the page is being re-loaded
When a page is getting loaded and there was previously a user signed in, the auth state may change a few times, while the client is figuring out if the user's authentication state it still valid. For that reason, you may see a call with no user before seeing the final call with the actual signed in user.
The fact it's sometimes null and sometimes not null likely points to an async problem. Are you making the check in the if statement above? All references to the user should be within the callback. If that all checks out, maybe check that authentication is being properly initiated.
onAuthStateChanged is an observer as stated in firebase docs, which gets triggered when the auth state is changed like user signed in, signed out, pwd change. To check if user is logged in or not you should use firebase.auth().currentUser which will give you the current logged in user. As you said your state is local firebase.auth().currentUser will always give you user unless user is signed out.
So, maybe I missed this somewhere in the docs but I couldn't find anything of the sort.
I wan't my users to have to type in their current password to be able to create a new one. From what I understand if the user is authenticated he is able to update his password without providing his current one.
Even if this might be somewhat secure I would rather have him type his old one to prevent people from going on already authenticated sessions from say family members or so and changing the pw.
Is there any way to do this?
(I have no problem using the Admin SDK since I already set up a server for these kind of things)
UPDATE: (Use - reauthenticateWithCredential)
var user = firebaseApp.auth().currentUser;
var credential = firebase.auth.EmailAuthProvider.credential(
firebase.auth().currentUser.email,
providedPassword
);
// Prompt the user to re-provide their sign-in credentials
user.reauthenticateWithCredential(credential).then(function() {
// User re-authenticated.
}).catch(function(error) {
// An error happened.
});
PREVIOUS VERSION
you can use reauthenticate API to do so. I am assuming you want to verify a current user's password before allowing the user to update it. So in web you do something like the following:
reauthenticateAndRetrieveDataWithCredential- DEPRECATED
firebase.auth().currentUser.reauthenticateAndRetrieveDataWithCredential(
firebase.auth.EmailAuthProvider.credential(
firebase.auth().currentUser.email,
providedPassword
)
);
If this succeeds, then you can call
firebase.auth().currentUser.updatePassword(newPassword);
So I'm creating a new user as my template is created. The user is being created successfully, and automatically is logged in however if I sign out and then try to sign in , I get the 'user not found'. Here is my code
Template.hello.onCreated(function helloOnCreated() {
// counter starts at 0
this.counter = new ReactiveVar(0);
var userObject = {
username: "anotherTest",
mail: "anotherTest#me.com",
password: "testingME"
};
Accounts.createUser(userObject, function(error){
console.log('User created');
console.log(error);
});
});
And here is the full project in case it is needed.
https://github.com/hayk94/UbMvp/tree/accountsTEST
Do you know what is the problem?
You're trying to use client side accounts management to perform a task it hasn't been designed for.
Client side accounts package purpose is to specifically allow new users to create their account and expect to be logged in immediately.
You have to remember that certain functions can be run on the client and/or on the server with different behaviors, Accounts.createUser docs specify that : "On the client, this function logs in as the newly created user on successful completion."
On the contrary, "On the server, it returns the newly created user id." (it doesn't mess with the currently logged in user on the client).
In order to solve your problem, you should write a server side method creating a new user and be able to call it from your client side admin panel, after filling correctly a user creation form of your own design.
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
How can I make a variable for the current user's username as they log in to create a database document for them to store their info?
You need to setup a custom function to configure user creation in server side, see Accounts.onCreateUser on docs.meteor.com
In this function you can initialize your user database document, either in user.field or user.profile.field.
The username is automatically stored in user.username, you do not need to create it.
Then to modify the user record client side, simply call a server method that will update the Meteor.users collection, ie
server/users.js
Meteor.methods({
updateUser:function(fields){
if(!this.userId){
// error : no user logged in
}
check(fields,{/* fields verification */});
Meteor.users.update(this.userId,{
$set:fields
});
}
});
client/main.js
Meteor.call("updateUser",{
"username":"foo",
"profile.bar":"bar"
});
Note that Meteor built-in user accounts greatly simplify all this process : it is well documented so I encourage you re-read that particular section in the docs.