adonisjs / auth

Official Authentication package for AdonisJS
https://docs.adonisjs.com/guides/auth/introduction
MIT License
196 stars 63 forks source link

Authentication using wrong database #148

Closed Pacheco95 closed 4 years ago

Pacheco95 commented 4 years ago

I'm setting up authentication to a new Adonis 5 project and followed the doc instructions

The key problem is that I'm using two connection databases. One for an external DB connection (default) and one for app users related stuff. When I tried to register a new user with AuthController.register it has returned this error:

{
    "errors": [
        {
            "rule": "unique",
            "field": "email",
            "message": "connect ECONNREFUSED 127.0.0.1:15432"
        }
    ]
}

which is trying to connect with the wrong database. I could not find a solution to this problem in the docs, so I searched in previous Adonis versions and found this one https://forum.adonisjs.com/t/changing-database-connection-in-model/2583 but without any success.

As an additional note, I also had problems with migrations even with presence of static get connection() inside the User model, but I have noticed that node ace migration:run accepts a flag -c which selects the correct connection to use and it seems to work well. It should be nice to have the possibility to setup the connection to migrations.

Package version

5.0.0-preview-rc-1.12

Node.js and npm version

Node: v12.18.3 Npm: 6.14.8

Sample Code (to reproduce the issue)

// config/databases.ts
import Env from '@ioc:Adonis/Core/Env'
import { OrmConfig } from '@ioc:Adonis/Lucid/Orm'
import { DatabaseConfig } from '@ioc:Adonis/Lucid/Database'
import Application from '@ioc:Adonis/Core/Application'

const databaseConfig: DatabaseConfig & { orm: Partial<OrmConfig> } = {
  connection: Env.get('DB_CONNECTION', 'pg') as string,

  connections: {
    pg: {
      client: 'pg',
      connection: {
        host: Env.get('DB_HOST', '127.0.0.1') as string,
        port: Number(Env.get('DB_PORT', 5432)),
        user: Env.get('DB_USER', 'lucid') as string,
        password: Env.get(DB_PASSWORD', 'lucid') as string,
        database: Env.get('DB_NAME', 'lucid') as string,
      },
      healthCheck: false,
    },

    sqlite: {
      client: 'sqlite',
      connection: {
        filename: Application.tmpPath('db.sqlite3'),
      },
      useNullAsDefault: true,
      healthCheck: false,
    },
  },
  orm: {
  },
}

export default databaseConfig
// Model/User.ts
import { DateTime } from 'luxon'
import Hash from '@ioc:Adonis/Core/Hash'
import {
  column,
  beforeSave,
  BaseModel,
} from '@ioc:Adonis/Lucid/Orm'

export default class User extends BaseModel {
  public static get connection () {
    return 'sqlite'
  }

  @column({ isPrimary: true })
  public id: number

  @column()
  public email: string

  @column()
  public password: string

  @column()
  public rememberMeToken?: string

  @column.dateTime({ autoCreate: true })
  public createdAt: DateTime

  @column.dateTime({ autoCreate: true, autoUpdate: true })
  public updatedAt: DateTime

  @beforeSave()
  public static async hashPassword (user: User) {
    if (user.$dirty.password) {
      user.password = await Hash.make(user.password)
    }
  }
}
// database/migrations/1587988332388_users.ts
import BaseSchema from '@ioc:Adonis/Lucid/Schema'

export default class UsersSchema extends BaseSchema {
  protected tableName = 'users'

  public async up () {
    this.schema.createTable(this.tableName, (table) => {
      table.increments('id').primary()
      table.string('email', 255).notNullable()
      table.string('password', 180).notNullable()
      table.string('remember_me_token').nullable()
      table.timestamps(true)
    })
  }

  public async down () {
    this.schema.dropTable(this.tableName)
  }
}
// Controllers/Http/AuthController.ts
import User from 'App/Models/User'
import { schema, rules } from '@ioc:Adonis/Core/Validator'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export default class AuthController {
  public async register ({ request }: HttpContextContract) {
    /**
     * Validate user details
     */
    const validationSchema = schema.create({
      email: schema.string({ trim: true }, [
        rules.email(),
        rules.unique({ table: 'users', column: 'email' }),
      ]),
      password: schema.string({ trim: true }, [
        rules.confirmed(),
      ]),
    })

    const userDetails = await request.validate({
      schema: validationSchema,
    })

    /**
     * Create a new user
     */
    const user = new User()
    user.email = userDetails.email
    user.password = userDetails.password
    await user.save()

    return 'Your account has been created'
  }
}
Pacheco95 commented 4 years ago

I tried to switch the default database to be the sqlite and now this error is happening

[1600116267100] INFO  (backend-ts/44480 on redacted): started server on 0.0.0.0:3333
[1600116270904] ERROR (backend-ts/44480 on redacted): Cannot find module 'phc-argon2'
Require stack:
- redacted/backend-ts/node_modules/@adonisjs/hash/build/src/Drivers/Argon.js
- redacted/backend-ts/node_modules/@adonisjs/hash/build/src/Hash/index.js
- redacted/backend-ts/node_modules/@adonisjs/hash/build/providers/HashProvider.js
- redacted/backend-ts/node_modules/@poppinss/utils/build/src/esmRequire.js
- redacted/backend-ts/node_modules/@poppinss/utils/build/index.js
- redacted/backend-ts/node_modules/@adonisjs/core/build/src/Ignitor/Bootstrapper/index.js
- redacted/backend-ts/node_modules/@adonisjs/core/build/src/Ignitor/Ace/AppCommands.js
- redacted/backend-ts/node_modules/@adonisjs/core/build/src/Ignitor/Ace/index.js
- redacted/backend-ts/node_modules/@adonisjs/core/build/src/Ignitor/index.js
- redacted/backend-ts/build/server.js

Installing it manually solved the problem and the users are being created but when trying to await auth.login(user) it is returning this error

{
    "message": "Cannot read property 'put' of undefined",
    "stack": "TypeError: Cannot read property 'put' of undefined\n    at SessionGuard.setSession (/home/michael/repositories/cataki-dashboard/backend-ts/node_modules/@adonisjs/auth/build/src/Guards/Session/index.js:46:26)\n    at SessionGuard.login (/home/michael/repositories/cataki-dashboard/backend-ts/node_modules/@adonisjs/auth/build/src/Guards/Session/index.js:197:14)\n    at Auth.login (/home/michael/repositories/cataki-dashboard/backend-ts/node_modules/@adonisjs/auth/build/src/Auth/index.js:114:27)\n    at AuthController.register (/home/michael/repositories/cataki-dashboard/backend-ts/app/Controllers/Http/AuthController.ts:33:16)\n    at Object.PreCompiler.runRouteHandler [as fn] (/home/michael/repositories/cataki-dashboard/backend-ts/node_modules/@adonisjs/http-server/build/src/Server/PreCompiler/index.js:46:31)\n    at Server.handle (/home/michael/repositories/cataki-dashboard/backend-ts/node_modules/@adonisjs/http-server/build/src/Server/index.js:138:13)"
}

I created a naked project to reproduce this issue https://github.com/Pacheco95/adonis-auth-test

thetutlage commented 4 years ago

I see there are some mis-conceptions in the way you expect things to work.

As an additional note, I also had problems with migrations even with presence of static get connection() inside the User model

Migrations have nothing to do with the models. Migrations automates the process of creating and managing the database schema. Whereas models are used to query them. They are two dis-jointed pieces.

Regarding email validation. The validator doesn't use models, it makes a direct query to the database. You can define a custom connection to the unique rule.

rules.unique({
  connection: 'sqlite',
})

Now that we have mis-conceptions cleared. We can look into the migrations targeting different connections. I recommend keeping the migrations of different connections inside different directories, especially when each connection has different migrations all together.

Inside the config/database.ts file. Define the migration paths for your defined connections.

sqlite: {
  client: 'sqlite',
  connection: {},
  migrations: {
    paths: ['./database/migrations/sqlite']
  },
}

Now the make:migration and migration:run commands will use the paths defined inside the config file over the default path.

Make migration

CleanShot 2020-09-15 at 10 27 11@2x

Migration run

CleanShot 2020-09-15 at 10 29 11@2x
Pacheco95 commented 4 years ago

Thanks for clarification about models and migrations. But what about this error? https://github.com/adonisjs/auth/issues/148#issuecomment-692313295. Even doing your tips I still cannot login users.

RomainLanz commented 4 years ago

Since you are using the Session driver you need to ensure that the session module is installed in your application.

https://preview.adonisjs.com/guides/http/sessions/

Pacheco95 commented 4 years ago

Oh man! You saved my day. Following the docs in https://preview.adonisjs.com/guides/auth/web-guard/#when-to-use-the-web-guard I just ran npm i @adonisjs/session and npm installed ^1.0.29 version. I did not entered the configuration page and ran the invoke command. Now its everything working. Thank you again and sorry about my negligence

thetutlage commented 4 years ago

No worries. We can trying to improve error reporting in certain cases. Will surely look into it :)