derby-auth
module (wrappers over passportjs
) for registration / authorization in a derby application using social networks as well.derby-starter
as the server part. This module hid from us the details of the server settings. Let me remind you that on the server, a derby application is built on top of the standard expressjs application, in particular, the database is set up there, the express middlware we need is connected. For our example today, it is necessary to deal more deeply with the server part - we will not use derby-starter
.expressjs
or doesn’t know nodejs
badly, nodejs
advise Ilya Kantor’s excellent course .derby-boilerplate
. In fact, this is a minimal application, similar to what we had in the first example, but the server part is no longer in derby-starter
, but in the project itself (the last 4th express is also used here, and redis
is not used at redis
- now it is possible without it). Copy the command: git clone https://github.com/derbyparty/derby-boilerplate.git
src/ app/ server/ styles/ views/ index.js
app
folder will be our derby app. Schematically, the “analogue” of the derby-starter
will be located in the server folder (I really took the derby-starter
, copied it there and slightly combed it), we will analyze its contents in more detail.styles
and views
folders - styles and templates, respectively. The index.js file is similar to what it was in the previous examples - a few lines that run the whole thing.expressjs
application, so if you do not know what it is, what express-middleware
, how they work, how express sessions work, routing - here you need to stop and eliminate deficiencies in knowledge. error/ index.js server.js
error
daddy, I brought an error handler, I will not here. I can only say that it is expressjs-middlware
, which is triggered when there is no handler for the dialed url
, or errors occurred during the operation of the application. If you want to sort it out yourself.index.js
file is to pick up express
(which is configured in the server.js
file) and raise the server on the specified port in the settings: var derby = require('derby'); exports.run = function (app, options, cb) { options = options || {}; var port = options.port || process.env.PORT || 3000; derby.run(createServer); function createServer() { if (typeof app === 'string') app = require(app); var expressApp = require('./server.js').setup(app, options); var server = require('http').createServer(expressApp); server.listen(port, function (err) { console.log('%d listening. Go to: http://localhost:%d/', process.pid, port); cb && cb(err); }); } }
derby
. Parsing this moment is the key to understanding how the derby works.express
distribution and must be installed separately. var express = require('express'); // 4- middleware // var session = require('express-session'); // var MongoStore = require('connect-mongo')(session); // - , // var midError = require('./error'); var MongoClient = require('mongodb').MongoClient; exports.setup = function setup(app, options) { var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app'; // MongoClient.connect(mongoUrl); var expressApp = express() if (options && options.static) { expressApp.use(require('serve-static')(options.static)); } expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); // - // - 404 expressApp.all('*', function(req, res, next) { next('404: ' + req.url); }); // expressApp.use(midError()); return expressApp; }
middleware
(via expressApp.use
). In essence, these middlware
are simple functions that will be in the same order as registered, called for each request that arrives at the server. Each of these middleware
can either respond to the request by completing the processing chain (the control will not be transferred to the remaining middlware), or perform some intermediate actions with the request (for example, parsing cookies
, determine the session, cookies
etc.) and transfer control down the chain. The order of connecting middlware
very important - it is in this sequence that the functions will be called for each request. // 4- var express = require('express'); // 4- middleware // var session = require('express-session'); // var MongoStore = require('connect-mongo')(session); // - , // var midError = require('./error'); var derby = require('derby'); // BrowserChannel - socket.io - , // , // liveDbMongo - - var racerBrowserChannel = require('racer-browserchannel'); var liveDbMongo = require('livedb-mongo'); // browserify derby.use(require('racer-bundle')); exports.setup = function setup(app, options) { var mongoUrl = process.env.MONGO_URL || process.env.MONGOHQ_URL || 'mongodb://localhost:27017/derby-app'; // ( redis) var store = derby.createStore({ db: liveDbMongo(mongoUrl + '?auto_reconnect', {safe: true}) }); var expressApp = express() // "" // (.. /derby/...) expressApp.use(app.scripts(store)); if (options && options.static) { expressApp.use(require('serve-static')(options.static)); } // browserchannel, // middleware, // (browserchannel longpooling - .. // /channel) expressApp.use(racerBrowserChannel(store)); // req getModel, // express- // . createUserId expressApp.use(store.modelMiddleware()); expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); expressApp.use(createUserId); // -, // , // expressApp.use(app.router()); // - // // - 404 expressApp.all('*', function(req, res, next) { next('404: ' + req.url); }); // expressApp.use(midError()); return expressApp; } // id- , // id - function createUserId(req, res, next) { var model = req.getModel(); var userId = req.session.userId; if (!userId) userId = req.session.userId = model.id(); model.set('_session.userId', userId); next(); }
browserify
is used - it is needed in order to give the client a so-called “bundle” to the client - a data block containing all javascript
derby application files, as well as templates (css scripts will not be contained here) . It is necessary to understand that with the usual request of any page of the site by the client - a bundle is not immediately sent to the browser, it would be too costly. A rendered page with styles and with some initial data has already been presented. This is logical because speed is very important here. Then this page itself loads the “bundle” - a request is made to the address "/ derby /: bandle-name". expressApp.use(clientApp.scripts(store)); expressApp.use(adminApp.scripts(store));
expressApp.use(app.router());
expressApp.use(clientApp.router()); expressApp.use(adminApp.router());
expressjs
handlers, organizing any RESTFull API we need, as they say, with blackjack and young ladies. This all works due to the fact that the client router of derby applications, not finding the right handler in their application, simply sends a request to the server for processing.browserchannel
- this is Google's analog socket.io
. In essence, this is a transport, thanks to which our application will synchronize data with the server in real time. Under the hood is the fact that at some point the client browser browsernel script is added to the bundle, as well as processing requests from this module (it is based on longpooling - therefore, processing is required, in this case at the /channel
address)redis
is not used. The derby update has recently been released, and now, redis
not mandatory (it is still needed if we do horizontal scaling — that is, to increase performance, run several derby servers at once).expressjs-middlware
, derby plugins, individual derby applications, express handlers - whatever. All this will be easy to live together.express
new ones. That is, everything happens according to the classical scheme: a cookie is used for identification (which will be stored in the client’s browser), the sessions themselves are stored in mongodb
in the sessions
collection.createUserId
function: function createUserId(req, res, next) { var model = req.getModel(); var userId = req.session.userId; if (!userId) userId = req.session.userId = model.id(); model.set('_session.userId', userId); next(); }
model.id()
) and written to the model along the path _session.userId
(this data will be serialized and transmitted to the client) and into the session itself .userId
) - the main page will be displayed in the browser and if we look at the model on the client, then on the way '_session.userId' - We will see our newly formed ID. Now wandering through the pages of our application (there will be no requests to the server) - the client always has his Id, and if he makes any requests to the server, we will always be able to recognize him by the session.userId
- everything is fine._session.userId
.derby-auth
module, which we will consider, is still can not. It uses the division of user data into 2 parts: the first is the auths
collection (here only private data), the second users
are open public data. So derby-auth
derby-auth
and derby-passport
. Both of them are wrappers over passportjs
, (one of them, in my opinion, fork the other). Actual versions of both that and that are intended for use with the fifth version of the derby (remember the 6th is still in alpha), have not yet been updated for the 6th, but this does not prevent us from using derby-auth
in all our projects on 6-ke (not available only html form templates, but we have them all the same custom).derby-auth
just because I worked with him.users
collection, and closed in auths
. Recently, an opportunity has appeared in the derby to make “projections” —that is, to subscribe only to certain collection fields — I think when these modules are updated to the 6th version of the derby, the restriction will go away.PassportJS
is the middleware
for authorization under node.js
This is an abstracting module that allows your application to use both regular login and password authorization and authorization through almost any service that supports Oauth, Oauth 2, etc. authorization Authorization methods are called strategies here. For example, login / password authorization is called LocalStrategy
(“local authorization strategy”), while GitHub authorization is GithubStrategy
. Each strategy is put into a separate module, so connecting the whole thing to a node.js application will look something like this: var passport = require('passport'); var LocalStrategy = require('passport-local').Strategy; var GithubStrategy = require('passport-github').Strategy;
appId
(or clientId
is a unique id-schnick of our application in this social network) and Secret (a secret key that nobody can say, it will be used to enable our application to confirm that we are us). When registering, you will also need to enter the so-called redirectURL
- that is, the url-address, where social. the network will redirect the client browser after authorization. In my example it will be: localhost : 3000 / auth / github / callback - but more on that later.github
, registration of the application there is very simple (for other providers it is not more difficult). First of all, you need to have a regular user account, an application is easily created in its settings.derby-auth
directly from the git-repository, for some reason, they have the master version under the previous derby
:Derby-auth
automatically puts passport
and passport-local
, it remains to install passport-github
: npm install passport-github -S
// derby-auth var auth = require('derby-auth'); var strategies = { github: { strategy: require("passport-github").Strategy, conf: { clientID: 'eeb00e8fa12f5119e5e9', clientSecret: '61631bdef37fce808334c83f1336320846647115' } } }
var options = { passport: { failureRedirect: '/login', successRedirect: '/' }, site: { domain: 'http://localhost:3000', name: 'Derby-auth example', email: 'admin@mysite.com' }, smtp: { service: 'Gmail', user: 'zag2art@gmail.com', pass: 'blahblahblah' } }
failureRedirect
, successRedirect
- url-address where the client will be redirected in case of not successful / successful authorization, respectively. Site information is needed to be sent to a strategy (for example, social networks can use it if the corresponding fields were not filled in when registering the application). The mail settings are needed in order for the “forgotten password” feature to work - reset it and send me a new one for soap. auth.store(store, false, strategies);
derby-auth
adds data access control, the user will not be able to get other people's records from the auths collection — he will only see his account. The second parameter is currently not used.body-parser
(we do not have it yet - we will install npm i body-parser -S
and connect it): expressApp.use(require('cookie-parser')()); expressApp.use(session({ secret: process.env.SESSION_SECRET || 'YOUR SECRET HERE', store: new MongoStore({url: mongoUrl}) })); expressApp.use(require('body-parser')()) // method-override , // , PUT // expressApp.use(require('method-override')())
derby-auth middeware
- the heap of all about it is connected here further: expressApp.use(auth.middleware(strategies, options));
expressjs
process requests to specific url. So it is - here is a summary of the controllers:Route | Method | Options | Purpose |
---|---|---|---|
/ login | post | username password | Login to the system by login and password |
/ register | post | username, email, password | Serves for user registration by login and password |
/ auth /: provider | get | absent | Passing here we will be redirected to the authorization page of the corresponding social. network. Example path: / auth / github |
/ auth /: provider / callback | get | absent | . — derby-auth failureRedirect successRedirect url. : /auth/github/callback — url , . network. |
/logout | get | «» '/' | |
/password-reset | post | email ( AJAX) | |
/password-change | post | uid, oldPassword, newPassword | ( AJAX) |
Derby-auth
It sends certain data to the client, which allows you to find out whether the user has logged in, his id, as well as supporting information describing problems with logging and registration:Let be | Description |
---|---|
_session.loggedIn | Boolean variable - authorized / not authorized user |
_session.userId | User ID |
_session.flash.error | Array of strings - auxiliary error messages (various authorization / registration errors) |
auth. {userId} .local | Local Registration Information |
auth. {userId}. {provider} | Registration data through the corresponding social. network |
derby-auth
would fit the 6th version of the derby, the application would be written in two accounts, without them more difficult. To simplify development, we connect bootstrap
, for this we enter in src / app / index.js app.use(require('d-bootstrap'));
npm i d-bootstrap -S
auths
.src/app/index.js
does not require special comments, the only thing that we did not discuss is the controller "*", but this is the standard thing for the express: var derby = require('derby'); var app = module.exports = derby.createApp('auth', __filename); global.app = app; app.use(require('d-bootstrap')); app.loadViews (__dirname+'/../../views'); app.loadStyles(__dirname+'/../../styles'); app.get('*', function(page, model, params, next){ var user = 'auths.' + model.get('_session.userId'); model.subscribe(user, function(){ model.ref('_page.user', user); next(); }); }); app.get('/', function (page, model){ page.render('home'); }); app.get('/login', function (page, model){ page.render('login'); });
layout
with the menu in the file index.html
, home.html
and login.html
we will put in the files , respectively, a page with information about the user’s status (entered / not entered, whether the github account is linked), and a page with authorization / registration. <import: src="./home"> <import: src="./login"> <Title:> Derby-auth example <Body:> <div class="navbar navbar-inverse" role="navigation"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="/">Derby-auth Example</a> </div> <div class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <view name="nav-link" href="/"></view> <view name="nav-link" href="/login"></view> </ul> </div> </div> </div> {{if _session.flash.error}} <div class="container"> {{each _session.flash.error as #error}} <div class="alert alert-warning">{{#error}}</div> {{/}} </div> {{/}} <view name="{{$render.ns}}"></view> <nav-link: element="nav-link"> <li class="{{if $render.url === @href}}active{{/}}"> <a href="{{@href}}">{{@content}}</a> </li>
<view name="{{$render.ns}}"></view>
home.html
, or login.html
, depending on what we have registered in the controller: page.render('home')
or The page.render('login')
home.html
: <index:> <div class="container"> <h1>:</h1> {{if _session.loggedIn}} <p> </p> <h2> :</h2> {{if _page.user.local}} <p> : <b>{{_page.user.local.username}}</b></p> {{else}} <p> </p> {{/}} <h2>GitHub:</h2> {{if _page.user.github}} <p> : <b>{{_page.user.github.username}}</b></p> {{else}} <p> </p> {{/}} <a class="btn btn-danger" href="/logout"></a> {{else}} <p> </p> <a href="/login">/</a> {{/}} </div>
GET /logout derby-auth
.form-signin { max-width: 330px; padding: 15px; margin: 0 auto; } .form-signin .form-signin-heading, .form-signin .checkbox { margin-bottom: 10px; } .form-signin .checkbox { font-weight: normal; } .form-signin .form-control { position: relative; height: auto; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 10px; font-size: 16px; } .form-signin .form-control:focus { z-index: 2; } .form-signin input[type="email"] { margin-bottom: -1px; border-bottom-right-radius: 0; border-bottom-left-radius: 0; } .form-signin input[type="password"] { margin-bottom: 10px; border-top-left-radius: 0; border-top-right-radius: 0; }
<index:> <view name="signin"></view> <hr/> <view name="signup"></view> <signin:> <div class="container"> <form class="form-signin" role="form" action='/login' method='post'> <h3 class="form-signin-heading"></h3> <input name="username" type="text" class="form-control" placeholder="" required="" autofocus=""> <input name="password" type="password" class="form-control" placeholder="" required=""> <button class="btn btn-lg btn-primary btn-block" type="submit"></button> <br/> <a class="btn btn-lg btn-danger btn-block" href="/auth/github"> GitHub</a> </form> </div> <signup:> <div class="container"> <form class="form-signin" role="form" action='/register' method='post'> <h3 class="form-signin-heading"></h3> <input name="username" type="text" class="form-control" placeholder="" required="" autofocus=""> <input name="email" type="email" class="form-control" placeholder="" required=""> <input name="password" type="password" class="form-control" placeholder="" required=""> <button class="btn btn-lg btn-primary btn-block" type="submit"></button> </form> </div>
derby-auth
(resetting and changing the password) - this lesson came out pretty big because of the need to explain the server part of the derby.Source: https://habr.com/ru/post/222399/
All Articles