Run the following LoopBack application to reproduce the problem:
var loopback = require('./');
var request = require('supertest');
var app = loopback();
var db = loopback.createDataSource({ connector: 'memory' });
loopback.User.attachTo(db);
app.model(loopback.User);
loopback.AccessToken.attachTo(db);
loopback.User.hasMany(loopback.AccessToken);
app.enableAuth();
app.use(loopback.token());
app.use(loopback.rest());
var credentials = { email: '[email protected]', password: 'password'};
loopback.User.create(credentials, function(err) {
if (err) throw err;
loopback.User.login(credentials, function(err, token) {
if (err) throw err;
console.log('passed');
process.exit();
});
});
Workaround: create a sub-class of the User model via app.model:
loopback.AccessToken.attachTo(db);
loopback.Role.attachTo(db);
loopback.ACL.attachTo(db);
app.model('User', {
options: {
base: 'User',
relations: {
accessTokens: {
model: "AccessToken",
type: "hasMany",
foreignKey: "userId"
}
}
},
dataSource: 'db'
});
app.enableAuth();
// etc.
It's a naming convention issue. Please customize the relation as follows:
loopback.User.hasMany(loopback.AccessToken, {as: 'accessTokens'});
LoopBack uses node-inflection's camelize() to derive the relation name from the model name. For example, camelize('AccessTokens', true) returns 'accesstokens' instead of 'accessTokens'.
If we think node-inflection's camelize() is flawed, we can fix it so that AccessTokens --> accessTokens.
Thanks @raymondfeng for figuring this out.
@crandmck Do we have any documentation on how to setup authentication in loopback applications? Could you please update it to include this information?
The documentation for Model.hasMany is missing description of the options, could you improve that too?
Help me please
events.js:72
throw er; // Unhandled 'error' event
^
TypeError: Cannot call method 'create' of undefined
at ModelConstructor.User.createAccessToken (/var/www/html/er/employees/main/node_modules/loopback/common/models/user.js:68:23)
The documentation for Model.hasMany is missing description of the options, could you improve that too?
@bajtos Where did you find that link--it is broken. And what is the actual article that needs to be fixed?
@crandmck The linked page was there one year ago, I guess the page has been moved since then? We should really preserve URLs when moving pages around in the docs.
Hah, I didn't notice that the date on that comment was 2014, not 2015...
We should really preserve URLs when moving pages around in the docs.
Yes, I have been making more of an effort to do that, but unfortunately it is not easily done with Confluence (a significant issue with the product IMO). In any case, in the past we made a number of sweeping changes in the organization that have broken old links, and there's no way to fix those at this point.
Also, I think relations were moved out of the Model object an into the Relation mixin -- so the new doc is http://apidocs.strongloop.com/loopback-datasource-juggler/#relationmixin-hasmany which now has documentation for the options.
I have this configuration (Admin is just a name for my real model)
{
"name": "Admin",
"base": "User",
"plural": "users",
"properties": {
...
},
"relations": {
...
"accessTokens": {
"model": "AccessToken",
"type": "hasMany",
"foreignKey": "userId"
}
},
...
}
Then when:
Admin.login(credentials,'user', function(error, token) {
return callback(null, token);
});
Output
TypeError: Cannot read property 'create' of undefined
at ModelConstructor.User.createAccessToken (PATH/node_modules/loopback/common/models/user.js:95:22)
at PATH/node_modules/loopback/common/models/user.js:251:22
at PATH/node_modules/loopback/common/models/user.js:307:9
at PATH/node_modules/loopback/node_modules/bcryptjs/dist/bcrypt.js:261:17
at PATH/node_modules/loopback/node_modules/bcryptjs/dist/bcrypt.js:1198:21
at Immediate.next (PATH/node_modules/loopback/node_modules/bcryptjs/dist/bcrypt.js:1078:21)
at Immediate._onImmediate (PATH/node_modules/loopback/node_modules/continuation-local-storage/node_modules/async-listener/glue.js:188:31)
at processImmediate [as _immediateCallback] (timers.js:358:17)
It works when POST /oauth/token.
I tried loopback.User.hasMany(loopback.AccessToken, {as: 'accessTokens'});
but it throws an error because 'hasMany' is undefined for User
I'm missing something ?
@lesterzone You should ask your question in the Google Group. It's not likely anyone will see it in a closed GH issue.
@lesterzone Please post questions at https://groups.google.com/forum/#!forum/loopbackjs. See https://github.com/strongloop/loopback/wiki/Questions for more details.
@lesterzone if you are using a model that inherits from User, try to add these lines to server/model-config.json:
"AccessToken": {
"dataSource": "db",
"public": false,
"strict": true
},
"ACL": {
"dataSource": "db",
"public": false,
"strict": true
}
@moklick Thanks that worked!
@lesterzone if you are using a model that inherits from User, try to add these lines to
server/model-config.json:"AccessToken": { "dataSource": "db", "public": false, "strict": true }, "ACL": { "dataSource": "db", "public": false, "strict": true }
This worked perfect for me, it should be added automatically when you inherit the User model
Most helpful comment
@lesterzone if you are using a model that inherits from User, try to add these lines to
server/model-config.json: