Muhammad Faizan Khan
Muhammad Faizan Khan

Reputation: 10551

Redirecting user to specific URL after Firebase Authentication

I have web portal single page login form where on user account I want to redirect users to different URLs, for example. I have enabled email/password in firebase authentication: Now

I mean I want to allow or restrict different domains to different users. one way is to right js code with if-else and reidrect user to a specific domain. But this is not safe as the code exists in JS. enter image description here

Edit (Dharmaraj Answer): I have received Dharmaraj answer but there is a confusion or a clarification required. Currently I am not allowing publically to register any user for security reason. I am just creating user on firebase console. So only the sign in required. Therefore the code snippet is slightly changed like this (Please correct me if it is secure).

firebase.auth().signInWithEmailAndPassword(username, password)
          .then((userCredential) =>
          {
            
            var user = userCredential.user;
            console.log(userCredential);
            console.log(user);
            
            
            //This code is not working
            /*const userDocRef  = firebase.firestore().collection("users").doc(userCredential.uid);
            const domain        = userDocRef.get().data();
            //const domain      = (await userDocRef.get()).data()
            if(domain)
            {
                console.log(domain + " domain");
            }*/
            
            console.log(userCredential.uid + " uid");
                console.log(userCredential.user.uid + " uid");
            
            
            //getting user specic domain! Is this safe
            firebase.firestore().collection("users").where('email','==', username).get().then((snapshot) =>{
            console.log("user specifc data");
                console.log(snapshot);
                snapshot.docs.forEach(doc => {
                    console.log(doc.data());
                    window.location.href = doc.data(),domain;
                    break;
                });
                
                
            })
            
            
            //unsecure way to redirect users to specific page
           // window.location = "TrenoxPilot/index.html"; //redirect user to specific path
          })

Further, the given security rule is not allowing me to get the records from firestore so i have changed like this but it is not save.

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow read, write;
    }
  }

}

Edit 2: Securtiy Rule Issue enter image description here

Upvotes: 1

Views: 2379

Answers (2)

Dharmaraj
Dharmaraj

Reputation: 50850

One way is to right JS code with if-else and redirect user to a specific domain. But this is not safe as the code exists in JS.

That sounds like you don't want to hardcode all the domains in your code and allow users to check their domain only. In such cases, you should store user-specific data in a database or you can even use Custom Claims if it's just a single URL. You would have to use a Cloud function to set that claim whenever user sets their domain.

exports.setDomain = functions.https.onCall((data, context) => {
  const {domain} = data;
  const {uid} = context.auth

  return admin
    .auth()
    .setCustomUserClaims(uid, { domain })
    .then(() => {
      // The new custom claims will propagate to the user's ID token the
      // next time a new one is issued.
    
      return {data: "Domain updated"}
    });
});

Now whenever a user signs in, just check for this claim and redirect them.

firebase.auth().createUserWithEmailAndPassword(email, password)
  .then(async ({user}) => {
    // Signed in
    const {claims} = await user.getIdTokenResult()
    if (claims.domain) {
      console.log("Domain present")
      window.location.href = claims.domain
    } else {
      console.log("Domain is not present")
    }
  })

Do note that you can also store user's domain in a database like Firestore and read the value from it instead of custom claims. However, I'd prefer using custom claims if it's just a single URL to save some Firestore reads.

If you do not wish to use Cloud function then you can use Firestore (or realtime db). Whenever a user sets their domain, update it in their documents. Then you can read the domain when the user logs in.

firebase.auth().createUserWithEmailAndPassword(email, password)
  .then(async ({user}) => {
    // Signed in
    const userDocRef = firebase.firestore().collection("users").doc(user.uid)
    const snap = await userDocRef.get()
    if (snap.exists) {
      console.log("User Doc present")
      const {domain} = snap.data()
      if (domain) {
        window.location.href = domain
      } else {
        console.log("Domain not present")
      }
    } else {
      console.log("User doc is not present")
    }
  })

Make sure your security rules allow users to read their own document only by setting the following rule:

match /users/{userId} {
  allow read, write: if request.auth != null && request.auth.uid == userId;
}

Edit:

The code in updated question does not work as intended since you are not handling promises correct here: const domain = userDocRef.get().data();

Try changing the security rules as in my answer above. Users will be able to read and write their own documentation only so I don't think there's any security risk involved.

Upvotes: 2

Leftium
Leftium

Reputation: 17903

It's OK to redirect with JS as long as the destination does a server-side check to make sure that user is allowed on the domain. (Similar to how you repeat server-side form validation even if you have client-side form validation.) So the flow would be:

  1. User submits login form on example.com.
  2. Firebase handles user auth and redirects to example.com/authRedirectHandler/
  3. Then example.com/authRedirectHandler/ redirects again to the final destination (abc.com, xyc.com, ...) based on the user.
  4. The destination domain then verifies that user is allowed on the domain. If not, you can return 403 not authorized instead of the protected content. (Or redirect back to the login page.)

I think you have to handle the Firebase auth redirect response from client-side JS (the access token is returned after a # to prevent it from reaching the server), but after getting the token, you can just do the redirect from the server.

Some notes:

  • It may not even be possible to have Firebase auth redirect directly to a different domain from the login form domain. I think the purpose of the 'authorized domains' setting is to limit the domains the login form can be called from.
  • Even if it is possible, the main use case is to use the same domain for the login form and the redirect URL.

If the domains are different you will probably run into some problems:

  1. The Firebase authentication is stored in a browser cookie, which is probably tied to a single domain, the one with the signup form. You will have to figure out how to marshal this authentication cookie to the other domain.
  2. It is bad UX to switch domains after submitting a login form. The user may get confused and/or not trust the site.

Upvotes: 1

Related Questions