Inside my onCreate auth cloud function for firebase handler, how can I tell if the user is anonymous, e.g. the isAnonymous property I get on the client in the onAuthStateChanged or onIdTokenChanged response?
exports.createHandler = functions.auth.user().onCreate((user, context) => {
let uid = user.uid,
isAnonymous = ????;
});
It seems that you cannot obtain this information from the Admin SDK. There is no method in the SDK which allows to get it.
The reason is that "isAnonymous is only a client side construct" as explained here: https://github.com/firebase/firebase-admin-node/issues/127.
isAnonymous is only a client side construct. If a user is created with
signInAnonymously(), this flag is set to true (client side only). For
example you can use createUser and just create a user without any
profile info (no email, phone, password, etc). Would that be
considered anonymous? How about if you create a user with just an
email? What about a custom auth user? You could also unlink all
providers from a non-anonymous account. Would that user become
anonymous?
Basically, you need to apply your own criteria of what an anonymous
user constitutes. When you listUsers you can perhaps check that no
providers are linked and no email/phoneNumber is set on the user, etc.
I know I am late but maybe this can help someone.
For me getting provider info from auth token is working fine:
context.auth.token.firebase.sign_in_provider
I ve tested in Emulators and found out that anonymous user has none providerData info.
A password user onCreate trigger in functino returns
providerData: [
{
providerId: 'password',
email: '********************',
federatedId: '************',
rawId: '***************',
toJSON: [Function]
}
]
A ANONYMOUS user return
providerData: [],
Related
How do I use the userId property outside the cases where I would use the special value 'me'?
From what I understand, when you use 'me', is when you've authenticated as the end user.
But when you're not authenticated as the end user, you can't use 'me' nor can you use any other value because you don't have permissions to perform a given request.
I'm trying to understand how can I perform an action on behalf of an authenticated user on my app without using the value 'me' in the userId field?
Example using Gmail nodeJs client:
const googleClient = new google.auth.OAuth2(client_id, client_secret, redirect_uri);
async function getUnreadEmailsFrom(userID) {
let gmail = google.gmail({ version: 'v1', auth: googleClient });
/* Here on the line below, I'd like to use the email or ID of an already authenticated user in order to perform a given action.
But the only way I understand that I am able to perform any action is by loading the token of the user and saving it via
googleClient.setCredentials(userToken)
and then using the special value 'me' inside the userId propery.
*/
let response = await gmail.users.messages.list({userId: 'me', q: 'is:unread', maxResults: 500 });
return response.data;
}
Short answer:
The only allowed values for userId are me and the authenticated (or impersonated) user email address.
Explanation:
The only way to access other users' data is to grant domain-wide authority to a service account and use that to impersonate the target user whose data you want to access (this user should be in the same domain).
Nevertheless, the target user email is specified when impersonating it (see Preparing to make an authorized API call), that is, before calling the API.
Later, when calling the API, the special value me would correspond to the impersonated user, and the only accepted values would be me and the impersonated user email. Any other value would result in error (since the service account would be acting as the impersonated user, and would only be able to access the impersonated user's data).
Therefore, the only alternative to me is the authenticated user email address, but of course you'd get the same data.
Domain-wide delegation example:
creds = service_account.Credentials.from_service_account_file('credentials.json', scopes=SCOPES)
creds = creds.with_subject('your_target_user#email.com')
service = build('gmail', 'v1', credentials=creds)
messages = service.users().messages().list(userId="me").execute()
I have been trying to get emails from another user in my domail in order to migrate it, using the google nodejs library. My account is superadmin and my API has wide-domain delegation permission enable with the following scopes:
"https://www.googleapis.com/auth/apps.groups.migration",
"https://mail.google.com/",
"https://www.googleapis.com/auth/gmail.settings.sharing"
I have logged into the API usin an impersonate access
async function initializeGmail(emailToImpersonate) {
const auth = await getAuth(emailToImpersonate);
return google.gmail({
version: 'v1',
auth
});
}
async function getAuth(emailToImpersonate) {
const jwtClient = new google.auth.JWT(
client_email,
null,
private_key,
SCOPES,
emailToImpersonate
);
await jwtClient.authorize();
return jwtClient;
}
I have added delegation permitions in the account I want to migrate so I can access its emails from my main account
const requestBody = {
delegateEmail: user.email,
verificationStatus: 'accepted'
}
await gmail.users.settings.delegates.create({userId, requestBody})
I request the emails of the account to migrate and I get the following error
const gmail = await initializeGmail(userEmail);
const messages = (await gmail.users.messages.list({auth, userId:userEmail})).data.messages;
code: 403,
errors: [
{
domain: 'global',
reason: 'forbidden',
message: 'Delegation denied for sysadmin#boatjump.com'
}
]
Any help or ideas will be highly appreciated
1st step - Create a delegate
To create a delegate, the parameter delegateEmail must specify the primary email of the delegate, that is your main account, I assume sysadmin#boatjump.com
As specified in the documentation, the delegate creation must performed by a service account that impersonates the delegator (your secondary account, I assume user.email)
2nd step - list the delegator's emails
Authenticate as sysadmin#boatjump.co' (no need for service account)
Specify as userId user.email
The reason you obtained a 403 error is the fact you specified
user.email instead of sysadmin#boatjump.com as the delegate and
thus did not pass to sysadmin#boatjump.com the authority to list
user.email's emails.
UPDATE:
If after a correct set-up of a delegate you continue experiencing issues with retrieving the delegator's emails, this issue is likely related to this behavior already reported on Google's Issue Tracker.
I recommend you to give it a "star" to increase visibility and the chances that it will be fixed in the future.
Workaround for the meantime:
When using the service account with domain-wide delegation, authenticate as the delegator and retrieve the emails on his behalf (userId:"me").
Inside my onCreate auth cloud function for firebase handler, how can I tell if the user is anonymous, e.g. the isAnonymous property I get on the client in the onAuthStateChanged or onIdTokenChanged response?
exports.createHandler = functions.auth.user().onCreate((user, context) => {
let uid = user.uid,
isAnonymous = ????;
});
It seems that you cannot obtain this information from the Admin SDK. There is no method in the SDK which allows to get it.
The reason is that "isAnonymous is only a client side construct" as explained here: https://github.com/firebase/firebase-admin-node/issues/127.
isAnonymous is only a client side construct. If a user is created with
signInAnonymously(), this flag is set to true (client side only). For
example you can use createUser and just create a user without any
profile info (no email, phone, password, etc). Would that be
considered anonymous? How about if you create a user with just an
email? What about a custom auth user? You could also unlink all
providers from a non-anonymous account. Would that user become
anonymous?
Basically, you need to apply your own criteria of what an anonymous
user constitutes. When you listUsers you can perhaps check that no
providers are linked and no email/phoneNumber is set on the user, etc.
I know I am late but maybe this can help someone.
For me getting provider info from auth token is working fine:
context.auth.token.firebase.sign_in_provider
I ve tested in Emulators and found out that anonymous user has none providerData info.
A password user onCreate trigger in functino returns
providerData: [
{
providerId: 'password',
email: '********************',
federatedId: '************',
rawId: '***************',
toJSON: [Function]
}
]
A ANONYMOUS user return
providerData: [],
The docs from Firebase suggest that the API offers the same features as the console:
It is not always convenient to have to visit the Firebase console in order to manage your Firebase users. The admin user management API provides programmatic access to those same users. It even allows you to do things the Firebase console cannot, such as retrieving a user's full data and changing a user's password, email address or phone number.
But the reference docs don't list a function to reset a user's password. Am I missing something?
EDIT: This answer is now out of date, see Andrea's answer below for how to send a password reset link through the Firebase SDK.
It depends on which definition of 'reset' you're using.
If you mean reset as in 'change', then yes - the updateUser function allows you to provide a new password. See the following example from the docs:
admin.auth().updateUser(uid, {
email: "modifiedUser#example.com",
phoneNumber: "+11234567890",
emailVerified: true,
password: "newPassword",
displayName: "Jane Doe",
photoURL: "http://www.example.com/12345678/photo.png",
disabled: true
})
.then(function(userRecord) {
// See the UserRecord reference doc for the contents of userRecord.
console.log("Successfully updated user", userRecord.toJSON());
})
.catch(function(error) {
console.log("Error updating user:", error);
});
If, on the other hand, you mean reset as in 'send a password reset email', then no, there doesn't seem to be a simple way of doing so via the Admin SDK.
Yes, you can. To generate a password reset link, you provide the existing user's email. Then you can use any email service you like to send the actual email. Link to documentation.
// Admin SDK API to generate the password reset link.
const userEmail = 'user#example.com';
admin.auth().generatePasswordResetLink(userEmail, actionCodeSettings)
.then((link) => {
// Construct password reset email template, embed the link and send
// using custom SMTP server.
return sendCustomPasswordResetEmail(email, displayName, link);
})
.catch((error) => {
// Some error occurred.
});
I'm using Firebase in a node.js backend for authentication.
After authentication, the data retrieved by Firebase are like this :
{
auth: {
provider: 'password',
uid: '1234'
},
expires: 1452072779,
token:'token',
uid: '4567',
provider: 'password'
}
However I cannot get the username. Do you know if there is a way to get it ?
In Android API, there is such a method called getProviderData() and you can do the following getProviderData().getEmail() but it seems there is no such thing in Javascript API... Can someone confirm this ?
Since you are using email and password with the first login and after that a token for login you can store the email of the user in your firebase itself so you can get it using the uid even when you login in with a token.
Take a look at https://www.firebase.com/docs/web/guide/user-auth.html#section-storing or How do I link each user to their data in Firebase? for an example.