Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom User Schema/Model ignoring custom fields #891

Closed
2 of 5 tasks
carlobolla opened this issue Nov 28, 2020 · 2 comments
Closed
2 of 5 tasks

Custom User Schema/Model ignoring custom fields #891

carlobolla opened this issue Nov 28, 2020 · 2 comments
Labels
question Ask how to do something or how something works

Comments

@carlobolla
Copy link

carlobolla commented Nov 28, 2020

Your question
I can't understand why my custom fields for a User are not stored in the database (MongoDB). I'm surely missing a step, but cannot find it in the documentation.

What are you trying to do
These are my relevant snippets. I have added a 'country' field to the default User schema for testing.
I read about the createUser event, but it doesn't access the profile information returned from the provider.

// pages/api/auth/[...nextauth].js

import NextAuth from 'next-auth'
import Models from '../../../models'
import Adapters from 'next-auth/adapters'

const options = {
  providers: [
    {
      id: 'vatsim',
      name: 'VATSIM Connect',
      type: 'oauth',
      version: '2.0',
      scope: 'full_name vatsim_details email country',
      params: { grant_type: 'authorization_code' },
      authorizationUrl: process.env.VATSIM_AUTH_URL,
      accessTokenUrl: process.env.VATSIM_TOKEN_URL,
      profileUrl: process.env.VATSIM_PROFILE_URL,
      profile: (profile) => {
        return {
          id: profile.data.cid,
          name: profile.data.personal.name_full,
          email: profile.data.personal.email,
          image: null,
          verifiedEmail: null,
          country: profile.data.personal.country.id
        }
      },
      clientId: process.env.VATSIM_CLIENT_ID,
      clientSecret: process.env.VATSIM_CLIENT_SECRET
    }
  ],
  database: process.env.AUTH_DB,
  adapter: Adapters.TypeORM.Adapter(
    process.env.AUTH_DB,
    {
      models: {
        User: Models.User
      }
    }
  ),
  debug: true
}
export default (req, res) => NextAuth(req, res, options)
// models/user.js

import Adapters from 'next-auth/adapters'

export default class User extends Adapters.TypeORM.Models.User.model {
  constructor (name, email, image, emailVerified, country) {
    super(name, email, image, emailVerified)
    if (country) { this.country = country }
  }
}

export const UserSchema = {
  name: 'User',
  target: User,
  columns: {
    ...Adapters.TypeORM.Models.User.schema.columns,
    country: { type: String }
  }
}
// models/index.js

import User, { UserSchema } from './User'

export default {
  User: {
    model: User,
    schema: UserSchema
  }
}

This is the resulting User:

[next-auth][debug][typeorm_create_session] User {
  name: 'Web One',
  email: [REDACTED],
  createdAt: 2020-11-28T15:25:08.457Z,
  updatedAt: 2020-11-28T15:25:08.457Z,
  emailVerified: null,
  image: null,
  id: 5fc26bd4a4ad16177c2300ee
}

Feedback
Documentation refers to searching through online documentation, code comments and issue history. The example project refers to next-auth-example.

  • Found the documentation helpful
  • Found documentation but was incomplete
  • Could not find relevant documentation
  • Found the example project helpful
  • Did not find the example project helpful

Edit: syntax '^^

@carlobolla carlobolla added the question Ask how to do something or how something works label Nov 28, 2020
@rjblopes
Copy link

Crossed this issue while trying to solve another one.

I'm not expert but I just found in the source that the built-in default adapter calls the User constructor with exact args (name, email, image, emailVerified); therefore your custom property will never be seen by your class constructor.

The only way I see to workaround this is to create a custom Adapter (https://next-auth.js.org/tutorials/creating-a-database-adapter). In the createUser method you have access to the exact profile returned by the provider.

@luizwhite
Copy link

luizwhite commented Mar 4, 2021

I can't get it to work too..
when the session/jwt is "revalidated", it gets a user from the database without the custom props (even if the database users has those props and i defined the new User model)

Should i create a new issue to raise this problem?

So the problem was that we can't use an oauth profile data to initialize the user object, because of that built-in User constructor in the default TypeORM Adapter like @rjblopes mentioned

async function createUser (profile) {
debug('CREATE_USER', profile)
try {
// Create user account
const user = new User(profile.name, profile.email, profile.image, profile.emailVerified)
return await manager.save(user)
} catch (error) {
logger.error('CREATE_USER_ERROR', error)
return Promise.reject(new CreateUserError(error))
}
}

But we can initialize a custom prop with a constant, with a profile prop or with a user prop (that comes from a database) value, all in the jwt callback when user or profile is truthy

But to the value persist to the session, you also have to pass that token.user value returned from the jwt callback to the session callback like this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Ask how to do something or how something works
Projects
None yet
Development

No branches or pull requests

3 participants