How to prevent user from logging in with unverified email?

I’m using the firebase login and I have it all working properly. Now I would like to prevent the user from gaining access to the system until after they have verified their email address.

api/src/lib/auth.js

^ seems like a good place to do this in the getCurrentUser method. I came up with something like

export const getCurrentUser = async (
  decoded
  // { _token, _type },
  // { _event, _context }
) => {
  if (decoded?.email_verified) {
    return {
      ...decoded,
      roles: parseJWT({ decoded }).roles,
    };
  } else {
    throw new AuthenticationError(
      'translations:server.errors.not-verified-error'
    );
  }
}

But on in the browser I only get the “Something went wrong” error rather than my string “translations:server.errors.not-verified-error”. I’ve even tried RedwoodError without any luck.

Is this a reasonable way to accomplish this? If so how can I get back a custom error?

Don’t you need to load the current user session first so you have access to the User record? I think the first parameter to getCurrentUser is an object with a single key (id or whatever the primary key of your User model is).

Don’t you need to load the current user session first so you have access to the User record?

The problem isn’t with the user access, that part is working fine. The problem is that when I check email_verified and it’s false I want to throw an error that I can catch in the browser and determine that it was an unverified user. I’ve tried throwing AuthenticationError as well as RedwoodError but each one just gives me the “something went wrong” error which isn’t very helpful :frowning:

Sorry, I missed that you were pulling the role from a JWT.

I think your error is being masked server side for security reasons. You could add your own createGraphQLHandler instance in api/src/functions/graphql.ts to handle your not-verified error.

Yes I agree it is being masked but unless I’m misunderstanding something from the documentation, I thought using one of the Redwood error handlers would prevent that.

To use a Redwood Error, import each from @redwoodjs/graphql-server.

  • SyntaxError - An unspecified error occurred
  • ValidationError - Invalid input to a service
  • AuthenticationError - Failed to authenticate
  • ForbiddenError - Unauthorized to access
  • UserInputError - Missing input to a service

If you use one of the errors, then the message provided will not be masked and will be shared in the GraphQL response

The emphasis above is mine. I’ve tried AuthenticationError and it is still being masked.

I’m not sure why, but the import of AuthenticationError is commented out in redwood/packages/graphql-server/src/plugins/useRedwoodAuthContext.ts.

Are you able to use the base class error (RedwoodGraphQLError) successfully? That might narrow the problem down.

This is the file that converts Redwood errors to Yoga errors. If you’re using VS Code, you should get debugger configuration already set up for you. You might try setting a breakpoint inside the useRedwoodError.ts file in your node_modules and see if there’s any useful information available there to troubleshoot.

Are you able to use the base class error (RedwoodGraphQLError) successfully? That might narrow the problem down.

I tried it, but it doesn’t seem to make a difference what error type I use :frowning:

This is the file that converts Redwood errors to Yoga errors. If you’re using VS Code, you should get debugger configuration already set up for you.

I put breakpoints all over this file, this code is never fired.

I’ve traced down what is happening

  1. It executes yoga.fetch redwood/packages/graphql-server/src/functions/graphql.ts at v4.5.0 · redwoodjs/redwood · GitHub
  2. This results in catching an exception that I’ve thrown in getCurrentUser where I’m trying to figure out whether or not the user has been verified, it is then re-thrown as just a generic exception redwood/packages/graphql-server/src/plugins/useRedwoodAuthContext.ts at v4.5.0 · redwoodjs/redwood · GitHub
  3. lambdaRespose is returned with the defaultError (something went wrong) redwood/packages/graphql-server/src/functions/graphql.ts at v4.5.0 · redwoodjs/redwood · GitHub

I’m wondering if useRedwoodAuthContext should not throw a new error but rather use the AuthenticationError I’m throwing? I’m not sure it would make a difference. I’m not sure how Redwood determines not to mask an error.

I think the actual error masking is taking place within Yoga (their docs on it here). The reason for the useRedwoodError.ts file I linked (from the notes in the file) is to convert to a Redwood exception class to one that Yoga will recognize as over-riding its built-in error masking behavior (since Yoga doesn’t allow custom exception classes).

I think you’re right, the problem seems to be in useRedwoodAuthContext.ts - the fact that the AuthenticationError import is commented out is suspicious. You should probably open an issue on the repo; I’m not sure what the history is on it.

^ It looks like they we’re trying something but never got it working, then commented it out and just got left in there. It never was used, it’s just an orphaned comment.

I may play around here and see if I can get something working and make a PR if I’m successful.

1 Like