Monday, 25 December 2017

Sharing a user table in Loopback.io and Yii2

Problem


We wanted to implement Loopback.io as an API for our mobile app development, but needed it to play nice with our Yii2 web environment. We needed to be able to authenticate our Loopback / API users on the same user table that we authenticate our website users, using the same encrypted password.

Solution


This ended up being easier than I thought. Here are the steps that I followed:


  1. Create an AppUser class that inherits from User.
  2. Override the table that the AppUser class uses as its datastore.
  3. Create some Loopback fields in my user table.
  4. Tell AppUser to use the pre-existing primary key
  5. Turn off email validation

1. Create an AppUser Class to inherit from User

I took my cues from the documentation on how to make a class that extends User. Download the example referenced in the documentation to get the files. However, I called my class "AppUser" instead of "user", changing user.js and user.json to app-user.js and app-user.json.

I list my AppUser.json below. I left the AppUser.js as it was provided.

2. Override the table the ash AppUser class uses as its datastore.

By default, Loopback will try to use a table called AppUser to match the name of the class. In my example, I need it to reference the user table in the database, which my Yii2 application already uses. I used the documentation on this page. to figure out how to do this.

In the AppUser.json file, add the following section:

"options": {
    "mysql": {
      "table": "user"
    }
  },
Where user is the name of my table that I want the AppUser class to use as its datastore, instead of the default "AppUser" table.

3. Create some Loopback fields in my user table.

I added these columns, but perhaps I could have excluded them according to the documentation on this page.

ALTER TABLE `user` ADD `realm` varchar(512);
ALTER TABLE `user` ADD `emailVerified` tinyint(1);
ALTER TABLE `user` ADD  `verificationToken` varchar(512);

4. Tell AppUser to use the pre-existing primary key

I already had my own user_id column setup as the primary key. Loopback will try to use a column called id as the primary key by default. To prevent that, I set the field in the properties section with "id": "true" so that AppUser knows to use user_id as the primary key.

  "properties": {
    "user_id": {
      "type": "number",
      "id": true
    },
    "account_id": {
      "type": "number",
      "required": "true"
    }
  }

(I setup the account_id field as my Loopback app will need to reference that column. You would setup your other column/fields under properties)

I also set the idInjection section to false, so Loopback won't try and use the "id" field.

"idInjection": false,

5. Turn off email validation.

Insert the following in the AppUser.js file. "emailVerificationRequired": false,


Here is my entire AppUser.json file:

{
  "name": "AppUser",
  "base": "User",
  "idInjection": false,
  "restrictResetPasswordTokenScope": true,
  "emailVerificationRequired": false,
  "validations": [],
  "relations": {},
  "acls": [
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "DENY"
    },
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "ALLOW",
      "property": "login"
    },
    {
      "principalType": "ROLE",
      "principalId": "$everyone",
      "permission": "ALLOW",
      "property": "logout"
    },
    {
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW",
      "property": "findById"
    },
    {
      "principalType": "ROLE",
      "principalId": "$owner",
      "permission": "ALLOW",
      "property": "updateAttributes"
    }
  ],
  "methods": {},
  "options": {
    "mysql": {
      "table": "user"
    }
  },
  "properties": {
    "user_id": {
      "type": "number",
      "id": true
    },
    "account_id": {
      "type": "number",
      "required": "true"
    }
  }
}






Sunday, 17 December 2017

Loopback Mysql Error: Pool is Closed

Problem

You run node . to start your Loopback application; it starts fine, but on your first request to the API you get a database connection error:

{ Error: Pool is closed.    at Pool.getConnection (/home/ubuntu/code/lb3/node_modules/mysql/lib/Pool.js:25:15)    at MySQL.executeSQL (/home/ubuntu/code/cumberland/lb3/node_modules/loopback-connector-mysql/lib/mysql.js:239:12)    at /home/ubuntu/code/lb3/node_modules/loopback-connector-mysql/node_modules/loopback-connector/lib/sql.js:418:10

Shoot.

Solution

You could be disconnecting from the database in one of your startup scripts (under the boot directory). In my case, I had a script to create tables for Loopback's Role, User, AccessToken's etc and at the end I had the offending line:
module.exports = function(app)
{
  var mysqlDs = app.dataSources.mysqlDs;
  var lbTables = ['User', 'AccessToken', 'ACL', 'RoleMapping', 'Role'];
  mysqlDs.automigrate(lbTables, function(er) {
    if (er) throw er;
    console.log('Loopback tables [' - lbTables - '] created in ', mysqlDs.adapter.name);
    mysqlDs.disconnect();  });
}
I took out that line and all is well.

Sunday, 10 December 2017

Node.js connectivity fails connecting to AWS RDS

Problem:

You were able to connect to a local mysql instance with node.js (with the Loopback framework in my instance) but when you try to connect to an AWS RDS instance you get the dreaded Error: Handshake inactivity timeout  error.

And its not an issue with an older version of Node 4.2.

Solution:

You likely have a security group preventing your access to the RDS instance.

Don't worry, I felt like an idiot too; it passes.