Next-auth: How to persist custom fields in database?

Created on 27 Aug 2020  路  7Comments  路  Source: nextauthjs/next-auth

Your question
I have followed https://next-auth.js.org/tutorials/typeorm-custom-models to understand how to add custom fields. I have successfully used this to override users table to use uuid instead of incrementing ID. I have tried adding new fields as well:

import Adapters from 'next-auth/adapters'

// Extend the built-in models using class inheritance
export default class User extends Adapters.TypeORM.Models.User.model {
  // You can extend the options in a model but you should not remove the base
  // properties or change the order of the built-in options on the constructor
  constructor(
    name,
    email,
    image,
    emailVerified,
    firstName,
    lastName,
    userType,
  ) {
    super(name, email, image, emailVerified)

    if (firstName) {
      console.log('First Name Set')
      this.firstName = firstName
    }
    if (lastName) {
      console.log('Last Name Set')
      this.lastName = lastName
    }
    if (userType) {
      console.log('User Type Set')
      this.userType = userType
    }
  }
}

export const UserSchema = {
  name: 'User',
  target: User,
  columns: {
    ...Adapters.TypeORM.Models.User.schema.columns,
    // Override to use uuid
    id: {
      primary: true,
      type: 'uuid',
      generated: true,
    },
    firstName: {
      type: 'varchar',
      nullable: true,
    },
    lastName: {
      type: 'varchar',
      nullable: true,
    },
    userType: {
      type: 'varchar',
      nullable: true,
    },
  },
}

In the hopes that the extra details that are present in the profile from my own oauth2 server using hydra (and I've confirmed they are in the profile). However, it seems that the constructor never receives the new firstName, lastName, and userType fields.

Am I right in thinking that there should be a way to configure next-auth to persist these extra profile values to the user in the database? Or do I need to manually update the database myself?

Essentially, I had been expecting that if I expanded the profile to include the new fields, then next-auth would persist those in the database thanks to my customised User model. That didn't happen.

For completeness, here's the options I'm using:

import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import Adapters from 'next-auth/adapters'

import Models from '../../../models'

const options = {
  // Configure one or more authentication providers
  providers: [
    {
      id: 'hydra',
      name: 'Hydra',
      type: 'oauth',
      version: '2.0',
      scope: 'openid offline',
      params: { grant_type: 'authorization_code' },
      accessTokenUrl: 'http://127.0.0.1:4444/oauth2/token',
      requestTokenUrl: 'http://127.0.0.1:4444/oauth2/auth',
      authorizationUrl: 'http://127.0.0.1:4444/oauth2/auth?response_type=code',
      profileUrl: 'http://localhost:4444/userinfo',
      // Profile:  {
      //   email: '[email protected]',
      //   firstName: 'John',
      //   id: '2201ae71-92f0-4d46-b106-b84038547d09',
      //   lastName: 'Smith',
      //   sid: '3a93ac49-e03e-43a2-a182-ea41a5bb3a04',
      //   sub: '2201ae71-92f0-4d46-b106-b84038547d09',
      //   userType: 'CLIENT'
      // }
      profile: (profile) => {
        return {
          id: profile.id,
          email: profile.email,
          firstName: profile.firstName,
          lastName: profile.lastName,
          userType: profile.userType,
        }
      },
      clientId: 'hydra-client',
      clientSecret: 'secret',
    },
  ],
  adapter: Adapters.TypeORM.Adapter(
    // The first argument should be a database connection string or TypeORM config object
    {
      type: 'postgres',
      host: process.env.PG_HOST || 'localhost',
      port: process.env.PG_PORT || 5432,
      username: process.env.PG_USER || 'postgres',
      password: process.env.PG_PASS || 'postgres',
      database: process.env.PG_NAME || 'postgres',
      schema: process.env.PG_SCHEMA || 'auth',
    },
    // The second argument can be used to pass custom models and schemas
    {
      models: {
        User: Models.User,
      },
    },
  ),
}

export default (req, res) => NextAuth(req, res, options)

Feedback

  • [x] Found the documentation helpful
  • [x] Found documentation but was incomplete
  • [x] Could not find relevant documentation
  • [ ] Found the example project helpful
  • [ ] Did not find the example project helpful
enhancement help wanted

Most helpful comment

@iaincollins (sorry if you're the wrong person) I made a commit in our fork that appears to resolve this issue for me -- https://github.com/episub/next-auth/commit/aacbf88609fc97b103e2af30c493a5e8f9edda5b

I'm wondering if you see any problems with that, consequences I may not have considered, or if you think it could form the basis of a PR? It would also need documentation updating. Here's an example of my custom user model that makes use of this change:

import Adapters from 'next-auth/adapters'

// Extend the built-in models using class inheritance
export default class User extends Adapters.TypeORM.Models.User.model {
  // You can extend the options in a model but you should not remove the base
  // properties or change the order of the built-in options on the constructor
  constructor(profile) {
    super(profile)

    if (profile) {
      if (profile.raw.firstName) {
        this.firstName = profile.raw.firstName
      }
      if (profile.raw.lastName) {
        this.lastName = profile.raw.lastName
      }
      if (profile.raw.userType) {
        this.userType = profile.raw.userType
      }
    }
  }
}

export const UserSchema = {
  name: 'User',
  target: User,
  columns: {
    ...Adapters.TypeORM.Models.User.schema.columns,
    // Override to use uuid
    id: {
      primary: true,
      type: 'uuid',
      generated: true,
    },
    firstName: {
      type: 'varchar',
      nullable: true,
    },
    lastName: {
      type: 'varchar',
      nullable: true,
    },
    userType: {
      type: 'varchar',
      nullable: true,
    },
  },
}

All 7 comments

@iaincollins (sorry if you're the wrong person) I made a commit in our fork that appears to resolve this issue for me -- https://github.com/episub/next-auth/commit/aacbf88609fc97b103e2af30c493a5e8f9edda5b

I'm wondering if you see any problems with that, consequences I may not have considered, or if you think it could form the basis of a PR? It would also need documentation updating. Here's an example of my custom user model that makes use of this change:

import Adapters from 'next-auth/adapters'

// Extend the built-in models using class inheritance
export default class User extends Adapters.TypeORM.Models.User.model {
  // You can extend the options in a model but you should not remove the base
  // properties or change the order of the built-in options on the constructor
  constructor(profile) {
    super(profile)

    if (profile) {
      if (profile.raw.firstName) {
        this.firstName = profile.raw.firstName
      }
      if (profile.raw.lastName) {
        this.lastName = profile.raw.lastName
      }
      if (profile.raw.userType) {
        this.userType = profile.raw.userType
      }
    }
  }
}

export const UserSchema = {
  name: 'User',
  target: User,
  columns: {
    ...Adapters.TypeORM.Models.User.schema.columns,
    // Override to use uuid
    id: {
      primary: true,
      type: 'uuid',
      generated: true,
    },
    firstName: {
      type: 'varchar',
      nullable: true,
    },
    lastName: {
      type: 'varchar',
      nullable: true,
    },
    userType: {
      type: 'varchar',
      nullable: true,
    },
  },
}

@saward +1 that some kind of a PR should be made to address this issue. My organization is experiencing a definite need for it!

Unintended consequences aside, making the changes to the instantiation & constructor of the User model that you made in your fork would not introduce any breaking changes to the next-auth API, constituting only a MINOR semver bump.

However, it would definitely create a behavioral double-standard for the other models, given that some models (the User model) would permit custom field extension, but not others. It would have to be addressed at some point, likely through a few small but nonetheless breaking changes to the API.

@iaincollins Any input on this? I believe that the issues raised here are fair, given that this missing functionality is counter-productive to the support for custom models described in the documentation. Let us know 馃槃

Hey @karpawich any movement here? Have you found a solution for this?

@kamerondotcom not currently. I'm waiting on word from a maintainer (@iaincollins) that they're open to a PR of this sort.

For the time being, you can use the fork from @saward or make a fork of your own.

Our fork is also available here: https://www.npmjs.com/package/@episub/next-auth

Currently it's just including this one change.

Hi there! It looks like this issue hasn't had any activity for a while. It will be closed if no further activity occurs. If you think your issue is still relevant, feel free to comment on it to keep it open. (Read more at #912) Thanks!

hi there! I am going to try to discuss this with Iain the next time we speak.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

jimmiejackson414 picture jimmiejackson414  路  3Comments

alephart picture alephart  路  3Comments

MelMacaluso picture MelMacaluso  路  3Comments

ryanditjia picture ryanditjia  路  3Comments

alex-cory picture alex-cory  路  3Comments