Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
k1ng440 authored and k1ng440 committed Oct 17, 2016
1 parent 102e8da commit ce3ffad
Show file tree
Hide file tree
Showing 7 changed files with 391 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,4 @@ jspm_packages

# Optional REPL history
.node_repl_history
/typings
62 changes: 62 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,64 @@
# loopback-auth0
Loopback middleware for auth0


# Requirements
* Auth0 Secret Key from client
* [Auth0 Management API v2](https://auth0.com/docs/api/management/v2)



# Installation

* npm
```
npm install github:k1ng440/loopback-auth0
```

* Create an async boot script
```
$ slc loopback:boot-script auth0
> What type of boot script do you want to generate? async
> create server\boot\auth0.js
```

* Edit ***server\boot\auth0.js*** and paste following code

```js
const secretKey = 'AUTH0 SECRET KEY';
const domain = 'YOUR DOMAIN.auth0.com';
const password = 'SOME_RANDOM_PASSWORD_FOR_LOOPBACK_USERS';
const managementConfig = {
"APIKey": "Auth0 management API Key", // get it from https://auth0.com/docs/api/management/v2
"GlobalClientSecret": // get it from https://auth0.com/docs/api/management/v2
"token": "Auth0 management API Token" // get it from https://auth0.com/docs/api/management/v2
}


const jwt = require('loopback-auth0')({
domain: domain,
secretKey: new Buffer(secretKey, 'base64'),
userModel: app.models.User,
password: password,
managementConfig: config.management,
});

app.middleware('initial', jwt.parseTokenFromQueryString.bind(jwt));
app.middleware('initial', jwt.jwtCheck.bind(jwt));
app.middleware('initial', jwt.getUserInformation.bind(jwt));
app.middleware('initial', jwt.mapUser.bind(jwt));

```


# Issue Reporting
If you have found a bug or if you have a feature request, please report them at this repository issues section


# Auther
[***Asaduzzaman Pavel***](http://www.codegenie.co)


# License

This project is licensed under the MIT license. See the LICENSE file for more info.
117 changes: 117 additions & 0 deletions jwt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
const debug = require('debug')('loopback:jwt');
const expressJwt = require('express-jwt');
const Promise = require('bluebird');
const loopbackUser = require('./loopbackUser');
const extend = require('xtend');
const Profile = require('./profile');
const assert = require('assert');
const ManagementClient = require('auth0').ManagementClient;

class LoopbackJwt extends loopbackUser {

constructor(options) {
super(options);

assert(options, 'Options must be defined');
assert(options.secretKey, 'Options.secretKey must be defined');
assert(options.password, 'Options.password must be defined');

this.options = {
secretKey: '',
credentialsRequired: false,
algorithms: ['RS256', 'HS256'],
beforeCreate: null,
};

this.userMap = {};

this.options = extend(this.options, options);

this.jwtCheck = expressJwt({
algorithms: this.options.algorithms,
secret: this.options.secretKey,
credentialsRequired: false,
getToken: options.getToken,
});

this.management = new ManagementClient({
token: this.options.managementConfig.token,
domain: this.options.domain,
});
}

parseTokenFromQueryString(req, res, next) {
const accessToken = req.query.access_token;
if (accessToken) {
delete req.query.access_token;
req.headers.authorization = 'Bearer ' + accessToken;
debug(
'parseTokenFromQueryString',
'found token in query string. attached to headers'
);
}

next();
};

getUserInformation(req, res, next) {
if (!req.user) {
debug('no current user context found.');
return next();
}

let that = this;
this.management.users.get({
id: req.user.sub,
}, function (err, user) {
req.user = new Profile(user, that.options.userModel);
next();
});
}

mapUser(req, res, next) {
if (!req.user) {
debug('no current user context found.');
return next();
}

this.user = req.user;

debug('attempting to map user [%s]', this.user.email);

const token = this.userMap[this.user.email];

if (!token) {
this.loginUser()
.then(token => {
debug('mapped existing user [%s]', token.id);
this.userMap[this.user.email] = token;
req.accessToken = token;
next();
})
.catch((e) => {
debug('login error', e);

this.createUser(req)
.then(token => {
this.userMap[this.user.email] = token;
req.accessToken = token;
next();
})
.catch(e => {
debug('Error while creating error', e);
next();
});
});
} else {
debug('found existing token [%s]', token.id);
req.accessToken = token;
next();
}
}

}

module.exports = (options) => {
return new LoopbackJwt(options);
};
48 changes: 48 additions & 0 deletions loopbackUser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
const debug = require('debug')('loopback:jwt');

class loopbackUser {
createUser() {
debug('creating new user');

return new Promise((resolve, reject) => {
const newUserData = {
firstName: this.user.name.givenName,
lastName: this.user.name.familyName,
auth0Identifier: this.user.id,
email: this.user.email,
password: this.options.password,
};

if (typeof this.options.beforeCreate === 'function') {
this.options.beforeCreate(newUserData, user);
}

this.options.userModel.create(newUserData)
.then(newUser => {
debug('new user created [%s]', newUser.email);
this.loginUser()
.then(token => {
resolve(token);
}).catch(e => {
debug('error while try to login after creating user',
e);
reject(e);
});
})
.catch(e => {
debug('error creating user', e);
reject(e);
});
});
}

loginUser() {
debug('attempting to login user [%s]', this.user.email);
return this.options.userModel.login({
email: this.user.email,
password: this.options.password,
});
}
};

module.exports = loopbackUser;
32 changes: 32 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"name": "loopback-auth0",
"version": "0.0.5",
"description": "Auth0 middleware for auth0",
"main": "jwt.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/k1ng440/loopback-auth0.git"
},
"keywords": [
"Auth0",
"loopback",
"strongloop",
"oauth2",
"oauth"
],
"author": "Asaduzzaman Pavel",
"license": "MIT",
"bugs": {
"url": "https://github.com/k1ng440/loopback-auth0/issues"
},
"homepage": "https://github.com/k1ng440/loopback-auth0#readme",
"dependencies": {
"auth0": "^2.5.0",
"bluebird": "^3.4.6",
"express-jwt": "^5.1.0",
"xtend": "^4.0.1"
}
}
123 changes: 123 additions & 0 deletions profile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
class Profile {
constructor(data, userModel) {
this.data = data;
this.userModel = userModel;

if (data.identities) {
this.provider = data.identities[0].provider;
}

this.displayName = data.name;
this.id = this.getId();
this.email = this.getEmail();
this.isEmailVerified = this.isEmailVerified();
this.getDescription();

this.name = {
familyName: data.family_name,
givenName: data.given_name,
};

if (data.emails) {
this.emails = data.emails.map(email => ({
value: email,
}));
} else if (data.email) {
this.emails = [{
value: data.email,
}];
}


//copy these fields
let copy = ['picture',
'locale',
'nickname',
'gender',
'identities',
];

copy.filter(k => k in data).forEach(k => {
this[k] = data[k];
});
}

getId() {
return typeof this.data.user_id !== undefined ?
this.data.user_id :
this.data.sub;
}

getUserName() {
return new Promise((resolve, reject) => {
let username = '';
let available = false;

if (this.data.username) {
username = this.data.username;
} else if (this.data.screen_name) {
username = this.data.username;
}

if (_.empty(useername)) {
username = this.data.name.replace(' ', '');
}

while (available === false) {
this.userModel.count({
username: username,
}, function(err, count) {
if (err) {
debug('Error while counting user', err);
return reject(false);
}

available = count > 0;

if (available === true) {
resolve(username);
} else {
username = username + _.random(0, 5000);
}
});
}
});
}

isEmailVerified() {
return (typeof this.data.email_verified !== undefined) &&
this.data.email_verified === true;
}

getEmail() {
let id = null;
if (this.data.email) {
id = this.data.email;
} else {
const usersplit = this.getId(user).split('|');
id = usersplit[1] + '@' + 'change_this_email.com';
}

return id;
}

getDescription() {
let description;

if (this.headline) {
description = this.headline;
} else if (this.description) {
description = this.description;
} else if (this.bio) {
description = this.bio;
} else if (this.about) {
description = this.data.about;
}

this.description = description;

return description;
}
}

module.exports = Profile;
Loading

0 comments on commit ce3ffad

Please sign in to comment.