#What is the best way to restrict sign ins by email when using convex auth

14 messages · Page 1 of 1 (latest)

gaunt gust
#

What is the best way to restrict sign ins by email when using convex auth?

I only want allow listed emails to be allowed to sign in. I am using Google Auth

late mothBOT
#

Hi @gaunt gust. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

To restrict sign-ins by email when using Convex Auth with Google OAuth, you can implement a custom createOrUpdateUser callback. This callback allows you to control the user creation and account linking behavior, including the ability to restrict sign-ins based on an allow list of emails.

Here's how you can implement this:

  1. First, set up your Google OAuth provider in your convex/auth.ts file.

  2. Then, implement the createOrUpdateUser callback to check if the email is in your allow list before creating or updating a user.

Here's an example of how you might structure this:

import Google from "@auth/core/providers/google";
import { convexAuth } from "@convex-dev/auth/server";
import { MutationCtx } from "./_generated/server";

// Your allow list of emails
const allowedEmails = ["[email protected]", "[email protected]"];

export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [Google],
  callbacks: {
    async createOrUpdateUser(ctx: MutationCtx, args) {
      const email = args.profile.email;
      
      // Check if the email is in the allow list
      if (!allowedEmails.includes(email)) {
        throw new Error("Email not allowed to sign in");
      }

      // If the email is allowed, proceed with user creation or update
      if (args.existingUserId) {
        // Update existing user if needed
        return args.existingUserId;
      }

      // Create new user
      return ctx.db.insert("users", {
        email: email,
        name: args.profile.name,
        // Add any other fields you want to store
      });
    },
  },
});

This approach allows you to:

#
  1. Define an allow list of emails.
  2. Check if the signing-in user's email is in the allow list.
  3. Only create or update a user if their email is allowed.
  4. Throw an error if the email is not allowed, which will prevent the sign-in.

Remember to adjust the allowedEmails array to include all the email addresses you want to allow. You might also want to store this list in your Convex database or an environment variable for easier management.

For more details on customizing the user creation and account linking behavior, you can refer to the Convex Auth Advanced Details documentation.

Convex Auth Advanced Details

This method provides a flexible way to restrict sign-ins based on email addresses while using Google OAuth with Convex Auth.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gaunt gust Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gaunt gust
#

@late moth How can I let user know the reason they weren't allowed to sign in?

late mothBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

To let users know the reason they weren't allowed to sign in when using Convex Auth, you can customize the error message thrown in the createOrUpdateUser callback. However, it's important to note that error handling in Convex Auth, especially for client-side display, has some limitations.

Based on the knowledge sources provided, here's the best approach:

  1. Customize the error message in the createOrUpdateUser callback:
async createOrUpdateUser(ctx: MutationCtx, args) {
  const email = args.profile.email;
  
  if (!allowedEmails.includes(email)) {
    throw new ConvexError("Your email is not on the allowed list for sign-in.");
  }

  // Rest of the function...
}
  1. On the client side, you can catch this error when attempting to sign in. However, as mentioned in the Discord support thread, error messages lose their descriptions when they reach the client in production.

To work around this limitation, you can implement a custom error handling approach:

const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
  e.preventDefault();
  setSubmitting(true);
  try {
    const formDataToSend = new FormData();
    formDataToSend.append("email", formData.email);
    // ... other form data

    await signIn(provider ?? "google", formDataToSend);
  } catch (error: any) {
    console.error({ error });
    let title = "Sign-in Error";
    let description = "You are not allowed to sign in. Please contact support if you believe this is an error.";

    // You can add more specific error handling here if needed

    toast({ title, description, variant: "destructive" });
  } finally {
    setSubmitting(false);
  }
};
#

This approach provides a generic error message to the user when they're not allowed to sign in. For security reasons, it's generally better not to provide too specific information about why a sign-in was rejected.

It's worth noting that the Convex team is aware of the limitations in error handling for auth errors. As mentioned in the Discord thread, they are considering making some of these errors catchable on the client side in the future. You can follow the issue on GitHub for updates: GitHub Issue #54.

For now, this approach provides a balance between informing the user and maintaining security. If you need more detailed error handling, you might need to implement a custom authentication flow or wait for future updates to Convex Auth.

Discord support thread: Need help with handling Convex Auth signIn errors in prod

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@gaunt gust Kapa.ai is still learning and improving, please let me know how I did by reacting below.

gaunt gust
#

@late moth I am not sure this works, here is my component:

<button
            onClick={async() => {
              try {
                await signIn("google");
              } catch (error) {
                alert("Sign in failed");
              }
            }}
            className="btn btn-primary"
          >
            Sign in with Google
          </button>
late mothBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

You're right to be unsure about this approach. The code you've provided won't work as expected for handling custom error messages from the createOrUpdateUser callback in Convex Auth. There are a few reasons for this:

  1. Error Handling: As mentioned in the Discord support thread, error messages lose their descriptions when they reach the client in production. This means that even if you throw a custom ConvexError on the server, you won't be able to access its message directly on the client side.

  2. Google OAuth Flow: The signIn("google") function typically initiates the OAuth flow, which redirects the user to Google's sign-in page. This redirection happens before any server-side checks in your createOrUpdateUser callback. As a result, the try/catch block in your component won't catch errors from that callback.

  3. Callback Execution: The createOrUpdateUser callback is executed on the server after the user has authenticated with Google. By this time, the client-side code has already completed the OAuth flow.

To handle this situation more effectively, you could consider the following approach:

  1. Allow the Google sign-in to proceed as normal.
  2. Implement a client-side check after the sign-in process to verify if the user was successfully authenticated.

Here's an example of how you might modify your component:

import { useAuth } from "@convex-dev/auth/react";
import { useEffect } from "react";

function SignInButton() {
  const { signIn, isAuthenticated } = useAuth();

  useEffect(() => {
    if (isAuthenticated === false) {
      // This will run if authentication failed
      alert("Sign in failed. Your email may not be on the allowed list.");
    }
  }, [isAuthenticated]);
#

return (
<button
onClick={() => signIn("google")}
className="btn btn-primary"
>
Sign in with Google
</button>
);
}


This approach:

1. Uses the `useAuth` hook to access authentication functions and state.
2. Initiates the Google sign-in process when the button is clicked.
3. Uses an `useEffect` hook to check the `isAuthenticated` state after the sign-in process completes.
4. Displays an alert if authentication failed, which could be due to the email not being on the allowed list.

Remember, this is still a generic error message. For security reasons, it's generally better not to provide too specific information about why a sign-in was rejected.

For more detailed error handling or user-specific messages, you might need to implement a custom authentication flow or wait for future updates to Convex Auth that may provide better client-side error handling.

[Discord support thread: Need help with handling Convex Auth signIn errors in prod](<https://discord.com/channels/1019350475847499849/1278048435513069660>)
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------