How do I set displayName in Firebase when using Apple authentication? - javascript

I am trying to use Apple OAuth on the web with the Firebase Javascript SDK, and when I do, the returned user object is fine, except for displayName, which is null.
let provider = new firebase.auth.OAuthProvider("apple.com");
provider.addScope("email");
provider.addScope("name");
let userCred = await firebase.auth().signInWithPopup(provider);
However when I look at the userCred I get:
displayName: null
I tried the scope fullName to no luck.
Is there any way to get the display name? I use it so that my users can see their name on the site, to personalize the experience.

You get this right on the response to appleAuth signin response
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: appleAuth.Operation.LOGIN,
requestedScopes: [appleAuth.Scope.EMAIL, appleAuth.Scope.FULL_NAME]
});
appleAuthRequestResponse will hold the name details.
Please note that, if you had already used your account for testing, you will have to login to your appleid and remove the apple sign in for your app, to test this.[ Also delete this user from firebase auth ]

Related

How to add new members in Group using Firebase

I am developing the app which has grouping function.
Now I have the problem about adding new member in group.
Like Slack, in group creating onboarding flow user can decide group name and add members which don't use the app yet.
As adding members function, I want to use inviting mail link using firebase dynamic links.
Data structure is below
User
- id
- name
- email
Group
- id
- groupName
- members[]
Group's members array has user id.
But when someone creates new group, it is possible that new users don't register the app yet.
So they don't have user id property in the app.
How do I fix this problem?
When someone creates new group, should I develop sign up functions new user using Firebase auth? This means then new member has automatically user id, and adding those to members property.
Or should group member array have mail address instead of user id.
Please tell me. I'm happy with Swift or JavaScript you will teach me.
Thank you.
UPDATE
After reading your comment I would propose another approach.
When the Group creator user adds users to a group, if a user does not already exists you could, from the front-end, call a Callable Cloud Function (CF) that creates a temporary Firestore document in a specific collection. The ID of this document will be the (future) userId.
Then, still in this same Cloud Function, you send an email to the email address (you need to generate yourself the email, for example with the email extension) with a link containing the userId as query string value.
Example of code for this first CF:
exports.provisionNewAccount = functions
.https.onCall(async (data, context) => {
try {
// You can check that the caller is authenticated
// if (context.auth.uid) {execute the rest of the code} else {throw an error}
// New user email
const userEmail = data.email;
// Generate the new user docID
const fakeDocRef = admin.firestore().collection('_').doc();
const requestId = fakeDocRef.id;
// Create the doc in a specific collection
await admin.firestore().collection('usersCreationRequests').doc(requestId).set({ email: userEmail, treated: false });
// Generate the link to include in the email
const linkURL = 'https://your.app/register.html?requestId=' + requestId
// Send the email by creating a doc in the Extension collection
await db
.collection("emails")
.add({
to: userEmail,
message: {
subject: "....",
html: `Click to create your account` // adapt the html to add some text
},
});
return {result: 'OK'}
} catch (error) {
console.log(JSON.stringify(error));
throw new functions.https.HttpsError('internal', JSON.stringify(error));
}
});
You call it as explained here, by passing the future user's email.
When the email recipient clicks on the link, you open a specific page of your web app that shows a set of fields for the future user to enter his password, display name etc. Then on clicking on a sign-in button in this page you call a Callable Cloud Function passing it the Firestore document ID plus the field values (you get the document ID from the query string).
As shown below, this Cloud Function creates the user in the Authentication service (using the Admin SDK) and flag the Firestore document as treated. Upon getting back the Cloud Function result in the web app you authenticate the user (you have his email and password, since he/she entered it in the form).
exports.createNewAccount = functions
.https.onCall(async (data, context) => {
try {
const userEmail = data.email;
const userId = data.userId;
const userPassword = data.password;
const userDisplayName = data.displayName;
// Fetch the user doc created in the first CF
const snapshot = await admin.firestore().collection('usersCreationRequests').doc(userId).get();
const treated = snapshot.get('treated');
const email = snapshot.get('email');
if (!treated && userEmail === email) {
const createUserPayload = {
email,
emailVerified: false,
password: userPassword,
displayName: userDisplayName
};
const userRecord = await admin.auth().createUser(createUserPayload);
return { result: 'OK' }
} else {
return { result: 'User already created' }
}
} catch (error) {
console.log(JSON.stringify(error));
throw new functions.https.HttpsError('internal', JSON.stringify(error));
}
});
I’m actually using this exact approach for a B2B collaborative web app in which users can invite new users by email.
INITIAL ANSWER
(Totally different from the update)
So they don't have user id property in the app… How do I fix this
problem? When someone creates new group, should I develop sign up
functions new user using Firebase auth?
You can use the Anonymous Authentication mode, it exactly corresponds to your needs:
You can use Firebase Authentication to create and use temporary
anonymous accounts to authenticate with Firebase. These temporary
anonymous accounts can be used to allow users who haven't yet signed
up to your app to work with data protected by security rules. If an
anonymous user decides to sign up to your app, you can link their
sign-in credentials to the anonymous account so that they can continue
to work with their protected data in future sessions.
When signing-in with Anonymous Authentication a userId (uid) will be created and later you will be able to convert an anonymous account to a permanent account
I always use userId to achive this kind of feature, you can use anonymous authentication to get userId after user click invite link, Then if needed unlock more feature with furter authentication(add more provider).
If you only using mail address without authentication, It's hard to write rules for prevent user access unwanted data, Like anyone knew your email are able to access your account.

How to allow users to deploy NEAR protocol smart contract on the frontend?

Getting close to being able to have individual users deploy their own smart contracts from the frontend witn NEAR, but I'm running into an error. First, the code:
const account = await near.account('polluterofminds.testnet');
const res = await axios.get("/api/contract");
const contractRaw = res.data;
const uint8 = new Uint8Array(Buffer.from(contractRaw))
const result = await account.deployContract(uint8);
console.log(result);
When doing this, I get the following error:
Error: The transaction contains more then one action, but it was signed with an access key which allows transaction to apply only one specific action. To apply more then one actions TX must be signed with a full access key
No idea how to solve this one. As far as I know my polluterofminds.testnet account has a full access key and I have signed in with that account.
Anyone successfully done this?
Here it is, from an example created a while ago that demonstrates deploying contracts to user accounts as part of a communication protocol
https://github.com/metanear/metanear-web/blob/master/src/Home.js#L96
UPDATE
(addressing comment to this answer)
#JustinHunter there are 2 types of access keys: FullAccess that lets you sign transactions with all 8 supported actions and FunctionCall access keys that only let you sign transactions with one type, the FunctionCall action.
you can see a little more about transactions and actions, keys, etc here:
https://docs.near.org/docs/concepts/transaction
https://docs.near.org/docs/concepts/account#access-keys
and a few relevant words about this restriction in this video:
https://youtu.be/ZM7TCONx9BI?t=2127
It seems that you use a function-call-only access key to sign the transaction. To (re-)deploy a contract in NEAR Protocol, you have to use a full-access key. The account in question has full-access and function-call-only keys, but you need to check which one is, ultimately, gets used.

React native firebase authentication using different providers

Firstly I create new user in firebase using function createUserWithEmailAndPassword(jamesdmurphy51#gmail.com, password).
I then sign in using Facebook using the code below
const result = await LoginManager.logInWithPermissions(['public_profile', 'email']);
const data = await AccessToken.getCurrentAccessToken();
const firebaseCredential = await auth.FacebookAuthProvider.credential(data.accessToken);
const fbUserObj = await auth().signInWithCredential(firebaseCredential);
Problem is that it creates a completely seperate user account with seperate UID....even though the email address linked to the facebook account is the same one that was used to createUserWithEmailAndPassword.
How do I make it so user can log into exising account using Facebook?
When you call auth().signInWithCredential(firebaseCredential), you're indeed creating a new account in Firebase Authentication.
If you instead want to link the Facebook credentials to an existing account, you need to call linkWithPopup or linkWithRedirect as shown in the Firebase documentation on linking accounts.

How can I make Dialogflow agent greet user if they have used the action before?

I'm using Actions On Google / Dialogflow, and I'm trying to make a function that will greet a user by their name if they've used the action before, and if not, will ask for their name. I have tried to map this to the "Welcome intent" through fulfillment, but whenever I try to run the action on the simulator, I get this error:
Error 206: Webhook Error
Which Initially would make sense if this was mapped to another intent, but I'm wondering if I'm getting this error because you can't have a fulfillment with the welcome intent?
Here's the code I'm using in the inline editor which may be the problem:
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request,response) => {
const agent = new WebhookClient({ request, response });
function welcome(conv) {
if (conv.user.last.seen) {
conv.ask(`Welcome back ${name}!`);
} else {
conv.ask('Welcome to The app! My name is Atlas, could I get your name?');
}}
let intentMap = new Map();
intentMap.set('Welcome Intent', welcome);
agent.handleRequest(intentMap);
How come this isn't working? Do I need to implement user login? Do I need to use a function that would write to a firestore databbase?
Thanks for the help or suggestions!
Let's clear a few things up to start:
You can have fulfillment with your welcome intent.
You do not need user login. Although using Google Sign In for Assistant can certainly be used, it doesn't fundamentally change your problem here.
You do not need to use a function that writes to the firestore database. Again, you could use it, but this doesn't change your problems.
The specific reason this isn't working is because the conv parameter in this case contains a Dialogflow WebhookClient rather than an actions-on-google Conversation object.
To get the Conversation object with the parameter you have, you can call conv.getConv(), which will give you an object that has a user parameter. So this may look something like
function welcome(conv) {
let aog = conv.getConv();
if (aog.user.last.seen) {
conv.ask(`Welcome back ${name}!`);
} else {
conv.ask('Welcome to The app! My name is Atlas, could I get your name?');
}}
There are, however, still some issues with this. Most notably, it isn't clear where name will come from. I assume you will get it out of the user store object, but you don't seem to have done this.
For anyone who comes across this question in the future and just wants a straight forward answer without having to search through ambiguous answers / documentation, here is what to do step by step:
note: I ended up using the Google Sign in method, but even if this isn't your goal, i'll post the link to the alternative method.
1) Import the actions on google module. What people / tutorials don't to show is you have to import the library like this (for user login):
const {
dialogflow,
Permission,
SignIn
} = require('actions-on-google')
instead of
const dialogflow = require('actions-on-google')
2) Use this code:
const app = dialogflow({
clientId: '<YOUR CLIENT ID from Actions on Google>',
});
app.intent('Start Signin', conv => {
conv.ask(new SignIn('To get your account details'));
});
app.intent('Get Signin', (conv, params, signin) => {
if (signin.status === 'OK') {
const payload = conv.user.profile.payload;
conv.ask(`Welcome back ${payload.name}. What do you want to do next?`);
} else {
conv.ask(`I won't be able to save your data, but what do you want to do next?`);
}
});
This function will ask the user for a login, and next time you invoke the intent, it will say "Welcome back name", because google automatically saves it.
Here's the link to the alternative method:

Is there any way to get Firebase Auth User UID?

I am looking to fetch Auth User(s) UID from Firebase via NodeJS or Javascript API.
I have attached screenshot for it so that you will have idea what I am looking for.
Hope, you guys help me out with this.
Auth data is asynchronous in Firebase 3. So you need to wait for the event and then you have access to the current logged in user's UID. You won't be able to get the others. It will get called when the app opens too.
You can also render your app only once receiving the event if you prefer, to avoid extra logic in there to determine if the event has fired yet.
You could also trigger route changes from here based on the presence of user, this combined with a check before loading a route is a solid way to ensure only the right people are viewing publicOnly or privateOnly pages.
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User logged in already or has just logged in.
console.log(user.uid);
} else {
// User not logged in or has just logged out.
}
});
Within your app you can either save this user object, or get the current user at any time with firebase.auth().currentUser.
https://firebase.google.com/docs/reference/js/firebase.auth.Auth#onAuthStateChanged
if a user is logged in then the console.log will print out:
if (firebase.auth().currentUser !== null)
console.log("user id: " + firebase.auth().currentUser.uid);
on server side you can use firebase admin sdk to get all user information :
const admin = require('firebase-admin')
var serviceAccount = require("./serviceAccountKey.json");
admin.initializeApp({
credential: admin.credential.cert(serviceAccount),
databaseURL: "https://yourprojecturl.firebaseio.com",
});
admin.auth().listUsers().then(data=>{
console.log(data.users)
})
This is an old question but I believe the accepted answer provides a correct answer to a different question; and although the answer from Dipanjan Panja seems to answer the original question, the original poster clarified later in a reply with a different question:
Basically, I need to generate token from UID by Firebase.auth().createCustomToken(UID) to sign in user on firebase with the following function firebase.auth().signInWithCustomToken(token).
Because the original question was clarified that the intent is to use
createCustomToken and signInWithCustomToken, I believe this is a question about using the Firebase Admin SDK or Firebase Functions (both server-side) to provide custom authentication, probably based on a username and password combination, rather than using an email address and password.
I also think there's some confusion over "uid" here, where in the code example below, it does NOT refer to the user's Firebase uid, but rather the uid indicated in the doc for createCustomToken, which shows:
admin
.auth()
.createCustomToken(uid)
.then((customToken) => {
...
In this case, the uid parameter on the createCustomToken call is not the Firebase uid field (which would not yet be known), thus providing a series of frustrating replies to the coder asking this question.
Instead, the uid here refers to any arbitrary basis for logging in for which this custom auth will support. (For example, it could also be an email address, social security number, employee number, anything...)
If you look above that short code block from the documentation page, you'll see that in this case uid was defined as:
const uid = 'some-uid';
Again, this could represent anything that the custom auth wanted it to be, but in this case, let's assume it's username/userid to be paired with a password. So it could have a value of 'admin' or 'appurist' or '123456' or something else.
Answer: So in this case, this particular uid (misnamed) is probably coming from user input, on a login form, which is why it is available at (before) login time. If you know who is trying to log in, some Admin SDK code code then search all users for a matching field (stored on new user registration).
It seems all of this is to get around the fact that Firebase does not support a signInWithUsernameAndPassword (arbitrary userid/username) or even a signInWithUidAndPassword (Firebase UID). So we need Admin SDK workarounds, or Firebase Functions, and the serverless aspect of Firebase is seriously weakened.
For a 6-minute video on the topic of custom auth tokens, I strongly recommend Jen Person's YouTube video for Firebase here:
Minting Custom Tokens with the Admin SDK for Node.js - Firecasts
As of now in Firebase console, there is no direct API to get a list of users, Auth User(s) UID.
But inside your Firebase database, you can maintain the User UID at user level. As below,
"users": {
"user-1": {
"uid": "abcd..",
....
},
"user-2": {
"uid": "abcd..",
....
},
"user-3": {
"uid": "abcd..",
....
}
}
Then you can make a query and retrieve it whenever you need uid's.
Hope this simple solution could help you!
From Firebase docs, use Firebase.getAuth():
var ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
var authData = ref.getAuth();
if (authData) {
console.log("Authenticated user with uid:", authData.uid);
}
Source:
Firebase.getAuth()

Categories

Resources