Issue with dbAuth in self-hosted: Trouble Starting Session in Custom Resolver

Hello RedwoodJS Community,

I’m having some trouble with dbAuth after generating the authentication code using the RedwoodJS CLI. Specifically, I can generate a token and return it to the user, but I’m unable to start the session from my custom signIn resolver.

Here’s a summary of my setup and the issue:

Setup:

  1. I used the RedwoodJS CLI to generate the dbAuth files.
  2. I added a custom signIn resolver to my Authentication entity.
  3. I’m able to validate the user and generate a token successfully.

Problem:

  • I cannot properly instantiate DbAuthHandler to manage the session within my custom resolver.
  • As a result, the session is not being initiated, and the session cookie is not being set.

Code Details:

Here’s my custom signIn resolver in api/src/services/authentications/authentications.ts:

import { db } from 'src/lib/db'
import bcrypt from 'bcrypt'
import { generateToken } from 'src/lib/auth'

export const signIn: MutationResolvers['signIn'] = async ({ input }) => {
  const { username, password } = input
  const authRegistry = await db.authentication.findUnique({ where: { username }})

  if (!authRegistry) {
    throw new Error('Invalid username')
  }

  const passwordValid = await bcrypt.compare(password, authRegistry.hashedPassword)
  if (!passwordValid) {
    throw new Error('Invalid password')
  }

  const token = generateToken(authRegistry)

  // Missing code to start session

  return {
    token,
  }
}

And here’s the auth.ts file generated by the RedwoodJS CLI (simplified for brevity):

import { DbAuthHandler } from '@redwoodjs/auth-dbauth-api'
import { db } from 'src/lib/db'

export const handler = async (event, context) => {
  const authHandler = new DbAuthHandler(event, context, {
    db: db,
    authModelAccessor: 'authentication',
    // Other configurations...
  })

  return await authHandler.invoke()
}

Question: How can I properly instantiate DbAuthHandler within my signIn resolver to handle the session initiation and set the session cookie?

I feel like I’m missing a crucial piece to tie everything together. Any guidance or examples would be greatly appreciated!

Thank you for your help!

Is this community dead?

Not dead, but I think there isn’t a lot of people who try to customize the dbAuth flow. I personally just use it out of the box and it works. Looking at your code, I’m not sure what you’re doing? Why is there an Authentication model in your db? I’m also not familiar with bcrypt but if you’re using your own service resolver to do this, you’re essentially exposing the password and hashed password to the client. What you should do instead is handle any kind of logic inside the auth.ts file and check for context before you make any kind of calls to your db. I may be wrong, but if you do stuff like

  const authRegistry = await db.authentication.findUnique({ where: { username }})

In a service and you’re returning the full “auth” object for the user, you’re circumventing the security implementation for getCurrentUser in the auth.ts file where dbAuth checks context before giving you the token.

I guess when I first read your post I was just confused why you’re doing this and not just implementing the logic in the auth.ts file? Password validation and invalid user checks are already done for you out of the box in dbAuth.

Adding more context on the request to avoid confusion, I need some help customizing the dbAuth flow, specifically for the SignIn mutation. Here’s a bit of background on what I’ve done so far:

  1. Generated dbAuth Files: I used the RedwoodJS CLI to generate the dbAuth files.

  2. Customized Authentication Model: I’m using an Authentication model in my Prisma schema, which looks like this:

    model Authentication {
      id                  String               @id @default(uuid())
      username            String               @unique
      hashedPassword      String               @default("")
      salt                String               @default("")
      resetToken          String?
      resetTokenExpiresAt DateTime?
      webAuthnChallenge   String?              @unique
      type                AuthenticationType
      roles               AuthenticationRole[]
      userId              String?
      user                User?                @relation(fields: [userId], references: [id])
      createdAt           DateTime             @default(now())
      updatedAt           DateTime             @updatedAt
    }
    
  3. SignIn Mutation Resolver: I’ve added a resolver for the SignIn mutation in api/src/services/authentications/authentications.ts, where I perform username and password validation.

Here’s what my SignIn resolver looks like:

import { db } from 'src/lib/db'
import bcrypt from 'bcrypt'

export const signIn: MutationResolvers['signIn'] = async ({ input }) => {
  const { username, password } = input
  const authRegistry = await db.authentication.findUnique({ where: { username } })

  if (!authRegistry) {
    throw new Error('Invalid username')
  }

  const passwordValid = await bcrypt.compare(password, authRegistry.hashedPassword)
  if (!passwordValid) {
    throw new Error('Invalid password')
  }

  const token = 'your-generated-jwt-token' // Replace with actual JWT generation logic
  return { token }
}

The Problem:

While I can generate a token and return it to the user, I cannot figure out how to properly start the session using the dbAuthHandler. I understand that dbAuth has built-in methods to handle this, but I’m unsure how to integrate them with my custom resolver.

What I’ve Tried:

  1. Custom Implementation: I tried implementing the login logic directly in the resolver, but it seems like I’m bypassing some of the built-in security features of dbAuth.
  2. Event Simulation: Attempted to simulate an event to call the dbAuthHandler, but this approach feels incorrect and cumbersome.

Question:

How can I properly integrate the dbAuth login functionality within my custom SignIn resolver to start the session and ensure security?

Here is my auth.ts file for reference:

typescript

Copy code

import type { APIGatewayProxyEvent, Context } from 'aws-lambda'
import { DbAuthHandler } from '@redwoodjs/auth-dbauth-api'
import { db } from 'src/lib/db'
import { cookieName } from 'src/lib/auth'

export const handler = async (event: APIGatewayProxyEvent, context: Context) => {
  const authHandler = new DbAuthHandler(event, context, {
    db,
    authModelAccessor: 'authentication',
    authFields: {
      id: 'id',
      username: 'username',
      hashedPassword: 'hashedPassword',
      salt: 'salt',
      resetToken: 'resetToken',
      resetTokenExpiresAt: 'resetTokenExpiresAt',
      challenge: 'webAuthnChallenge',
    },
    cookie: {
      attributes: {
        HttpOnly: true,
        Path: '/',
        SameSite: 'Strict',
        Secure: process.env.NODE_ENV !== 'development',
      },
      name: cookieName,
    },
  })

  return await authHandler.invoke()
}

I believe I might be missing something crucial about how dbAuth integrates with RedwoodJS. Any guidance on how to correctly implement this or pointers to relevant documentation would be greatly appreciated.

Thank you in advance for your help!

Please be aware that the customization is being done in the backend code nothing here is frontend code, frontend is working fine when adding a third-party oauth, but while on self-hosted dbauth is where backend is not there just yet, I need to find how to invoke dbAuthHandler from my login/signin resolver in my model service to integrate with rwjs authentication mechanism.

So, I decided to try the way I was hoping to have my authentication to work with Blitz, it worked in less than 2 hrs everything is working custom, I guess I will switch. thanks.