Limit sign-in to portal by role

Hi, i have multiple roles for the user, we're just using basic email password & oAuth sign in for certain roles,
(so 4 roles, 3 of which require only email password sign-in while the last user can have either email password or oAuth)

user1 - email + password
user2 - email + password
user3 - email + password
user4 - email + password or oAuth sign-in

now we'd like to limit what portal they can login from, sign-up is already limited to the portal as a role is assigned client side as a signup argument (or gets defaulted to user4 if not provided - for oAuth sign-up where we cant provide args), however on sign in, we'd want a way to tell the user their acc is invalid to login from the incorrect portal (for context continuity)

one aspect was using hooks to check the url however this wouldn't entirely work as we are using tabs for the prior 3 user types, so they share the same URL, another would be to have our own api endpoint, and return a generic incorrect user error (so as to not inform a malicious user if that email exists)

then there's the case of oAuth sign-in ensuring it is limited to the specific user4 role type when they sign back in, as after they have made a account, we have auto-link enabled, we'd like to prevent if the prior 3 users can sign-in with oAuth.

however, i'd like to ask what the best idiomatic way to handle this case is with better auth? maybe we're missing something that would make this whole thing easier?
Solution
oh forgot i posted this but if anyone else is strugglign with this, use the following:

 databaseHooks: {
    session: {
      create: {
        async before(session, ctx) {
          if (!ctx) return

          // check if role is specified in query
          if (!(ctx.query && 'role' in ctx.query && typeof ctx.query.role === 'string')) return

          // get user
          const user = await ctx.context.internalAdapter.findUserById(
              session.userId
          ) as UserWithRole

          if (user.role !== ctx.query.role) throw new APIError("FORBIDDEN", {
            message: `Please use ${user.role} portal to login!`,
            code: "ROLE_MISMATCH",
          });
        }
      }
    }
  },


something of that sort, where we pass down a query param within the login code:

async function onSubmit(values: z.infer<typeof loginSchema>) {
    const { data, error } = await authClient.signIn.email({
      email: values.email,
      password: values.password,
      rememberMe: true,
      callbackURL: "/",
      fetchOptions: { 
        query: {
          role: "user"
        }
      }
    });
  
   // ... 
}


with oAuth you will have to hard code the role you want, for me its only one role so its fine,
just check if ctx.path.startswith('/callback/')
  • do the checks you want, then redirect them to your desired error page (gotta return a api error, so i created a new apiError then overwrote the status and status code to 302 (for redirect) then set the Location header to what i wanted the redirect to go to!
Was this page helpful?