How can I send a verification email after I create a user with firebase admin SDK?
I am trying to combine createUser function and sendEmailVerification function
could somebody indicate a hint or an answer? thanks
update:
the user creation is being done by an admin user who is already signed in in the app, so the admin user is just creating users on the dashboad. This is completely different from the registeration methods.
update 2:
I tried to follow bojeil's answer, I am still stuck with the step that user signs in with the custom token. It gets into conflict with my current admin user session, the admin users gets kicked out and instead the new user is signed in and even when I sign out the new user, the admin user is still out and needs to sign in to get back into the app.
here is my code inside the app after I get the custom token:
$http.post('/.custom-token', {uid: $scope.data.data.uid})
.then(function (response) {
console.log("custom token here:", response.data.token);
firebase.auth().signInWithCustomToken(response.data.token)
.then(function (firebaseUser) {
firebaseUser.sendEmailVerification();
firebase.auth().signOut().then(function() {
// Sign-out successful.
console.log("signed out success");
}, function(error) {
// An error happened.
});
})
.catch(function(error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
// ...
});
});
so, I get the token, sign in the new user, send the email verification link, and then, sign out the new user.
But my admin user who is doing all of this gets signed out as well.
what am I missing here?
OK this is what you can do but you may hit quota limitations:
Include the firebase-admin module.
Include the firebase client module.
using admin sdk, create the new user via createUser
when that promise resolves, get the uid of the user created.
using admin sdk, create custom token for that uid.
using client sdk, signInWithCustom token using that custom token.
A user is returned in the process, call user.sendEmailVerification()
signOut that user from the client SDK.
According to firebase, the admin-sdk does not currently support this feature. See their response here:
https://stackoverflow.com/a/44557980/8177355
Whenever an email / password authentication user is logged in, and attempts to use a feature that requires authentication, I call onAuthStateChanged() and then check the user's record for email verification.
If the email is not verified, and I have not sent a verification email before, I send it automatically. I return an error, asking the user to verify their email. (I store a variable in a profile setup for the user in firestore, to indicate whether it has been sent previously).
On future attempts to use the app, if the email is still not verified, I return the same error, and also include a button in the error labeled "re-send verification email" that triggers sending of the verification email when pressed. (This way I am not automatically sending tons of verification emails every time the user tries to do something.)
A rather clean solution would be to actually use the REST APIs.
curl 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/getOobConfirmationCode?key=[API_KEY]' \
-H 'Content-Type: application/json' \
--data-binary '{"requestType":"PASSWORD_RESET","email":"[user#example.com]"}'
The [API KEY] is a web client api key that can be retrieved from Project Settings > add an app >> click web and you will get the configuration with a JSON, within the JSON there is an APIKey that is the one that you need to use.
You don't even need to use the Firebase Admin SDK for this. You can just use the regular Firebase client-side SDK:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function(user) {
console.log("User successfully created:", user.uid);
return user.sendEmailVerification();
})
.then(function() {
console.log("Email verification email successfully sent!");
})
.catch(function(error) {
console.log("Error:", error);
});
Here's bojeil's steps in code, which work in a NodeJS Firebase Cloud Function. Assuming that 'admin' is your Firebase Admin SDK instance, and 'firebase' is your Firebase Client SDK instance.
var userId = "YOURUSERIDHERE";
admin.auth()
.createCustomToken(userId)
.then((customToken) => {
return firebase.auth().signInWithCustomToken(customToken)
})
.then((data) => {
return data.user.sendEmailVerification({url: "http://YOURREDIRECTURL"});
}).then((result) => {
console.log("success");
}).catch((error) => {
console.log("faillure");
});
Make sure that you've setup the Admin SDK properly, by creating a service account and initialising it in the Admin SDK configuration snippet.
I think we can try the following combinations:
createUser()
generateEmailVerificationLink
Send the email verification link using Firebase Trigger Email Extension.
What I did is very simple:
Create a user in the server
In the client await the server creation and after that sign-in using the client SDK,
in the background listening to firebase auth user change event and than use the user and call sendEmailVerification
This is my React code:
await Api('signupUser', data) // server call
await firebase
.auth()
.signInWithEmailAndPassword(email, password); //client login
// send email on login
useEffect(() => {
return firebase.auth().onIdTokenChanged((user) =>
user?.sendEmailVerification());
}, []);
Related
I am implementing authentication via Auth0 for Cypress.
Currently, I am using the solution from this question
// Pseudocode
cy.postRequestToAuth0(username, password, applicationClientId, applicationSecret)
.then(({ body: { access_token, expires_in, id_token, token_type } }) => {
cy.window().then((win) => {
win.localStorage.setItem(
`##auth0spajs##::${client_id}::${audience}::${scope}`,
JSON.stringifyReceivedToken()
);
});
});
For Application A (Auth0 term), It returns POST 200 for auth0/token endpoint and authorizes a user (No login button, actual user profile instead).
For Application B, which is an absolute copy of application A, it returns POST 200 from auth0/token but it does not authorize a user (Login button is visible).
The scope of token response is the same: "scope":"openid profile email offline_access".
Connections and Grant types from the setting are the same.
Logs say that authentication is successful for both applications.
Any ideas why the stored token for Application A is valid, but the token for Application B is not?
I have two reactjs websites, one for client user and one for admin. I have graphql api for data access. Currently, admin is using JWT and we will change to firebase authentication.
I already implement both login and registration using firebase package.
here is the create user function. So it worked and can login successfully. But my question is that how can I distinguish admin user and customer user. I saw that setCustomUserClaims is only for firebase-admin. May I know how to set the role in firebase and what is the correct way to set?
function register(name, email, password) {
return createUserWithEmailAndPassword(auth, email, password).then((res) => {
const auth = getAuth();
updateProfile(auth.currentUser, {
displayName: name
}).then(() => {
// Profile updated!
// ...
}).catch((error) => {
// An error occurred
// ...
console.log(error.message);
});
});
}
Custom claims are definitely the easiest way to add roles in Firebase Auth but you'll need to use Admin SDK in a Cloud function or any secure server environment.
I saw that setCustomUserClaims is only for firebase-admin
setCustomUserClaims() is used to set the custom claims but you can always read them using getIdTokenResult() function in client SDK.
const { claims } = await getIdTokenResult(auth.currentUser)
So if your GraphQL API runs on your own server you can install Firebase Admin there and add custom claims to Firebase auth users.
Alternatively, you can store user role in a database like Firestore or your own database and read user's role from there. One catch of store roles in a database is that you cannot access them from security rules of other services like Firebase storage if required but custom claims can be.
For a small app catering to a very small set of users, we are planning to implement magic link authentication. The user would come to the application, enter their email address, get a magic link on the email address, after the user clicks on the link, they are logged in to the app.
I am not able to find enough resources to figure out how to do it in a SPA. Here are some helpful links:
Magic Link with Node
https://medium.com/#aleksandrasays/sending-magic-links-with-nodejs-765a8686996
https://medium.com/one-more-thing-studio/how-to-make-magic-links-with-node-1d164c036e29
Magic Link with Auth0
https://auth0.com/passwordless
This is the SPA workflow that I have in mind:
User comes to the SPA
The SPA takes the user to the login page where they can provide their email address.
The SPA sends the email address to the backend api
The api decides whether or not the user is registered, and sends them an email with a short lived jwt.
Clicking on this link takes user to a SPA route with the jwt in query params.
The Frontend forwards this jwt to the api backend, and the api backend verifies the jwt and sets a cookie
This cookie can then be used to maintain the user session.
I want to verify this workflow, but I am not able to find enough resources.
Specifically, I want to clarify whether the magic link should send the user to the SPA and the SPA should be responsible for extracting the jwt and sending it to the API backend, or is there another way to do it?
Is this how this should be implemented? What are the security implications?
I am using react and react-router.
Cotter co-founder here.
We have a super easy magic link integration for React. Here's a guide to set up a Simple Magic Link Login for your React App.
You can integrate magic link login in 2 steps:
1. Add dependencies
yarn add cotter
2. Show the log in form
(step 2-4 in your flow)
import React, { useEffect } from "react";
import Cotter from "cotter"; // 1️⃣ Import Cotter
function App() {
useEffect(() => {
// 2️⃣ Initialize and show the form
var cotter = new Cotter(API_KEY_ID); // 👈 Specify your API KEY ID here
cotter
.signInWithLink() // use Magic link
.showEmailForm() // show email login form
.then(resp => console.log(resp))
.catch(err => console.log(err));
}, []);
return (
// 3️⃣ Put a <div> with id "cotter-form-container"
// that will contain the form
<div id="cotter-form-container" style={{ width: 300, height: 300 }} />
);
}
export default App;
You can create your API_KEY_ID here.
Done! Now you should have an email Login Form that sends a magic link to your users. Here's a working example.
The response
After the user click the link (step 5), you'll receive the following response in then((resp) => console.log(resp)):
{
"email": "youremail#gmail.com",
"oauth_token": {
"access_token": "eyJhbGciONiIsImtiJFUzI1pZCI6...",
// you'll also get a refresh token and an id token
},
"user": {
"ID": "abcdefgh-1234-5678-1234-f17786ed499e", // Cotter's User ID
// More user information
}
}
You can then send this response to your backend server and do the following steps: (step 6-7 in your flow)
Verify if the access_token (a JWT token) is valid.
If it's valid, you can register the user if the email is not recognized (you should also associate the email with Cotter's user id).
You can use the access_token for all your API endpoints, or you can generate your own session and set a cookie
Checkout this Reack Hook use-magic-link to integration Magic Link very quickly into a React app.
Read this article for more info: Simple Auth Setup for Your React App
This is the workflow for magic link:
When a user enters the email address, Magic sends verification link to the email address to verify that email. When you click on "Confirm your email", a modal will show up to log in to the app. When the user click on the "Log in to app", Public+Private keys are generated and stored on the browser. That means users own their own identity. This key pair is in embedded iframe and inaccessible to the developer in order to protect the user's private key.
Since those keys are generated on the user's client instead of magic servers, Magic will not be able to see those secrets.
Magic sdk will use that private key to generate auth token. This auth token is called DID token (Decentralized Identifier). When you decode this token, you can see the user's email address and when it was issued. Essentially, DID token is your digital signature. If we store this token in our database and if our database gets hacked, malicious users will not be able to access our private key. Then we pass this DID token to the server to check the user
Magic stores the user's email and id in indexedDb. It also stores some cookies for itself, to function properly
to work with magic, you use magic-sdk. You set the magic client ✨
import { Magic } from "magic-sdk";
const createMagic = () => {
return (
new Magic(process.env.API_KEY)
);
};
export const magic = createMagic();
then using this client:
// you have input and captured the email
if (email) {
try {
// this store public/private key on browser and returns the DID token
const didToken = await magic.auth.loginWithMagicLink({
email,
});
if (didToken) {
// once you have the token, using metadata, you can add another propertis
// iat, exp, roles etc
// then sign this with jwt
// store the token in browser
}
I want to have a login button in my website so when a user clicks on it, the user can use their Google credentials. I'd like to ideally perform the authentication server side using Express.js and Passport.js.
I implemented authentication server-side but the problem is that I can't make an AJAX request from the website to the server to start authentication because Google or Oauth don't support CORS. So I need to use a href element in my website which would call the server authentication endpoint. However, I can't catch server response in this way.
If I perform the authentication client-side (I'm using React) I could store login state in Redux and allow the user to access the website's resources. However, when the user logs out I need to make sure that server endpoints stop serving the same user which feels like implementing authentication twice: client-side and server-side.
In addition when authenticating client-side, Google opens a popup for the user to authenticate which I think is worse user experience then just a redirect when authenticating server-side.
I'm wondering what the best practice in terms of authenticating using Oauth2/Google. For example, stackoverflow.com also has Google button but just makes a redirect, without any popup, so I guess they figured out a way to perform server-side authentication and to bypass CORS issue.
I faced the same issue. This article is Gold link
1.In auth route File I had following code
const CLIENT_HOME_PAGE_URL = "http://localhost:3000";
// GET /auth/google
// called to authenticate using Google-oauth2.0
router.get('/google', passport.authenticate('google',{scope : ['email','profile']}));
// GET /auth/google/callback
// Callback route (same as from google console)
router.get(
'/google/callback',
passport.authenticate("google", {
successRedirect: CLIENT_HOME_PAGE_URL,
failureRedirect: "/auth/login/failed"
}));
// GET /auth/google/callback
// Rest Point for React to call for user object From google APi
router.get('/login/success', (req,res)=>{
if (req.user) {
res.json({
message : "User Authenticated",
user : req.user
})
}
else res.status(400).json({
message : "User Not Authenticated",
user : null
})
});
2.On React Side After when user click on button which call the above /auth/google api
loginWithGoogle = (ev) => {
ev.preventDefault();
window.open("http://localhost:5000/auth/google", "_self");
}
3.This will redirect to Google authentication screen and redirect to /auth/google/callback which again redirect to react app home page CLIENT_HOME_PAGE_URL
4.On home page call rest end point for user object
(async () => {
const request = await fetch("http://localhost:5000/auth/login/success", {
method: "GET",
credentials: "include",
headers: {
Accept: "application/json",
"Content-Type": "application/json",
"Access-Control-Allow-Credentials": true,
},
});
const res = await request.json();
//In my case I stored user object in redux store
if(request.status == 200){
//Set User in Store
store.dispatch({
type: LOGIN_USER,
payload : {
user : res.user
}
});
}
})();
5.last thing add cors package and following code in server.js/index.js in node module
// Cors
app.use(
cors({
origin: "http://localhost:3000", // allow to server to accept request from different origin
methods: "GET,HEAD,PUT,PATCH,POST,DELETE",
credentials: true // allow session cookie from browser to pass through
})
);
Your authentication should be done server side. Here is how it works.
You make a fetch or axios call to your authentication route.
Your authentication route sends a request to Google's Authentication servers. This is important to have on the backend because you will need to provide your clientSecret. If you were to store this on the frontend, it would make it really easy for someone to find that value and compromise your website.
Google authenticates the user and then sends you a set of tokens to your callback url to use for that user (refresh, auth, etc...). Then you would use the auth token for any additional authorization until it expires.
Once that expires, you would use the refresh token to get a new authorization token for that client. That is a whole other process though.
Here is an example of what that looks like with Passport.js: https://github.com/jaredhanson/passport-google-oauth2
EDIT #1:
Here is an example with comments of the process in use with Facebook, which is the same OAuth codebase:
https://github.com/passport/express-4.x-facebook-example/blob/master/server.js
Redux can really help with achieving this and this follows the same logic as Nick B already explained...
You set up oauth on the server side and provide an endpoint that makes that call
You set up the button on you react frontend and wire that through an action to the endpoint you already setup
The endpoint supplies a token back which you can dispatch via a reducer to the central redux store.
That token can now be used to set a user to authenticated
There you have it.
I'm new to nodejs, and I'm trying to create and REST_API server, the thing is:
1- I have user registration and login currently working using passport
What i am a bit confuse is:
When a user get access to they're dashboard, i want to have and button that says (Generate your API Key). Then the user will use this key in header requests to my REST_API. That's where i'm not understanding, what should i use to get the above flow?
I tried using JWT to generate a token, but that was only creating the token when user login, and i don't want the user to have to login everytime.
Here's my login route:
app.post('/api/login', passport.authenticate('local-login'), function (req, res) {
res.json({ message: req.authInfo, user_id: req.user.id, user_email: req.user.local.email, loggedin: true });
});
If there's something else i have to provide, please ask ok?
Thanks!
When a user get access to they're dashboard...
Make a post request to an endpoint, pass the userid, that way you will know which user the key it’s been generated . Generate the key and return it. You can store it in cache or session to use during the session
i don't want the user to have to login overtime...
If you don’t want to make your user login everytime. You could store user and pass in browser cache(encrypt the pass). so, when user heads to the app you have to check if user and pass are in cache. if so, log the user in.