nodeAPI


imagine ou are github and want to allow thirdparty apps to provide services to your users without knowing their user/pass so that if later on a user wants to revoke the app's access they can.
the app gets a client secret.
the app forwards the user to github login page with a call_back route + its client id.
once user logis in to github and grants access, github sends back a code to the app
the app sends client_id, client_secret, code and handles user's verbs.
--if hte user wants to revoke the code is removed. 

When you create your application, you register it using the Google Developers Console. Google then provides information you'll need later, such as a client ID and a client secret.

client id & secret
Client ID and Secret
After registering your app, you will receive a client ID and a client secret. The client ID is considered public information, and is used to build login URLs, or included in Javascript source code on a page. The client secret must be kept confidential. If a deployed app cannot keep the secret confidential, such as Javascript or native apps, then the secret is not used.
Browser-Based Apps
Browser-based apps run entirely in the browser after loading the source code from a web page. Since the entire source code is available to the browser, they cannot maintain the confidentiality of their client secret, so the secret is not used in this case.
Mobile Apps
Like browser-based apps, mobile apps also cannot maintain the confidentiality of their client secret. Because of this, mobile apps must also use an OAuth flow that does not require a client secret

What's the benefit of the client secret in OAuth2?

so client secret is used when you e.g. salesforce wants to authenticate a third party webserver that accesses sales force for their data. client secret is securely stored inside the thirdparty server. users can acess via username/password but it should be done through the thirdparty websrver to avoid corrupted dns or man-in-the-middle attacks. like attacks where a website spoofs the actual theird party's website and tries to catch people's data in the middle.
 (proves to the authentication server that the client app is authorized to make a request on behalf of the user.)  http://salesforce.stackexchange.com/a/14013
 


http://aleksandrov.ws/2013/09/12/restful-api-with-nodejs-plus-mongodb/

$ express nodeAPI
$ # delete all the directories and files except app.js and package.json
$ npm install
$ mv app.js server.js
$ vim server.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));   // public directory e.g. to put index.html

app.get('/api', function (req, res) {
    res.send('API is running');
});

app.listen(3000, function(){
    console.log('Express server listening on port 3000');
});

$ node server.js  # '/' and '/api' are recognized
$ mkdir public
$ vim public/index.html
HELLOOOOOO
$ # now index.html also works.


$ npm i winston  # a cool logger
$ mkdir libs
$ vim libs/log.js
var winston = require('winston');

function getLogger(module) {
    var path = module.filename.split('/').slice(-2).join('/'); //using filename in log statements
    
    return new winston.Logger({
        transports : [
            new winston.transports.Console({   // now we only have one transport (console) you can separately sort and store logs in different transports, such as a database or file.  
                colorize:   true,
                level:      'debug',
                label:      path
            })
        ]
    });
}

module.exports = getLogger;

$ vim server.js
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var log = require('./libs/log')(module);    //winston logger

var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.get('/api', function (req, res) {
    res.send('API is running');
});

app.listen(3000, function(){
    log.info('Express server listening on port 3000');  //log
});

$ cat package.json
{
  "name": "nodeAPI",
  "version": "0.0.0",
  "private": true,
  "scripts": {
    "start": "node ./server.js"
  },
  "dependencies": {
    "body-parser": "~1.12.0",
    "cookie-parser": "~1.3.4",
    "debug": "~2.1.1",
    "express": "~4.12.2",
    "serve-favicon": "~2.2.0",
    "winston": "^1.0.0"
  }
}

Now that we have logger let's do error handling
app.get('/ErrorExample', function(req, res, next){
    next(new Error('Random error!'));   // an error caused. 
});

app.use(function(req, res, next){ // 404
    res.status(404);
    log.debug('Not found URL: %s',req.url);
    res.send({ error: 'Not found' });
    return;
});

app.use(function(err, req, res, next){ // capture internal errors
    res.status(err.status || 500);
    log.error('Internal error(%d): %s',res.statusCode,err.message);
    res.send({ error: err.message });
    return;
});

RESTful API endpoints, CRUD

app.get('/api/articles', function(req, res) {
    res.send('This is not implemented now');
});

app.post('/api/articles', function(req, res) {
    res.send('This is not implemented now');
});

app.get('/api/articles/:id', function(req, res) {
    res.send('This is not implemented now');
});

app.put('/api/articles/:id', function (req, res){
    res.send('This is not implemented now');    
});

app.delete('/api/articles/:id', function (req, res){
    res.send('This is not implemented now');
});

To test post/put/delete I am advising a wonderful wrapper over cURL - httpie
$ http http://localhost:3000/api/articles

Database:     $ ./bin/mongod --dbpath ./data/db/

$ npm i mongoose --save
================================================== config manager
$ npm i nconf --save 
$ vim libs/config.js
var nconf = require('nconf');

// hierarchical configuration manager
// Setup nconf to use (in-order):
  //   1. Command-line arguments
  //   2. Environment variables
  //   3. A file located at 'path/to/config.json'
nconf.argv()
.env()
.file({
file: process.cwd() + '/config.json'   // All the settings will be stored in config.json at the project's root
});

module.exports = nconf;

$ vim config.json
{
"port" : 1337,
"mongoose": {
"uri": "mongodb://localhost/test1"
}}

mongoose.js changes:
var config = require(process.cwd() + '/libs/config');

mongoose.connect(config.get('mongoose:uri'));

server.js changes:
var config = require(process.cwd() + '/libs/config');

app.listen(config.get('port'), function(){
    log.info('Express server listening on port ' + config.get('port'));
});

=============================================================  CRUD actions


var ArticleModel    = require('./libs/mongoose').ArticleModel;

app.get('/api/articles', function(req, res) {
    return ArticleModel.find(function (err, articles) {
        if (!err) {
            return res.send(articles);
        } else {
            res.statusCode = 500;
            log.error('Internal error(%d): %s',res.statusCode,err.message);
            return res.send({ error: 'Server error' });
        }
    });
});

app.post('/api/articles', function(req, res) {
    var article
 = new ArticleModel({
        title: req.body.title,
        author: req.body.author,
        description: req.body.description,
        images: req.body.images
    });

    article.save(function (err) {
        if (!err) {
            log.info("article created");
            return res.send({ status: 'OK', article:article });
        } else {
            console.log(err);
            if(err.name == 'ValidationError') {
                res.statusCode = 400;
                res.send({ error: 'Validation error' });
            } else {
                res.statusCode = 500;
                res.send({ error: 'Server error' });
            }
            log.error('Internal error(%d): %s',res.statusCode,err.message);
        }
    });
});

app.get('/api/articles/:id', function(req, res) {
    return ArticleModel.findById(req.params.id, function (err, article) {
        if(!article) {
            res.statusCode = 404;
            return res.send({ error: 'Not found' });
        }
        if (!err) {
            return res.send({ status: 'OK', article:article });
        } else {
            res.statusCode = 500;
            log.error('Internal error(%d): %s',res.statusCode,err.message);
            return res.send({ error: 'Server error' });
        }
    });
});

app.put('/api/articles/:id', function (req, res){
    return ArticleModel.findById(req.params.id, function (err, article) {
        if(!article) {
            res.statusCode = 404;
            return res.send({ error: 'Not found' });
        }

        article.title = req.body.title;
        article.description = req.body.description;
        article.author = req.body.author;
        article.images = req.body.images;
        return article.save(function (err) {
            if (!err) {
                log.info("article updated");
                return res.send({ status: 'OK', article:article });
            } else {
                if(err.name == 'ValidationError') {
                    res.statusCode = 400;
                    res.send({ error: 'Validation error' });
                } else {
                    res.statusCode = 500;
                    res.send({ error: 'Server error' });
                }
                log.error('Internal error(%d): %s',res.statusCode,err.message);
            }
        });
    });
});

app.delete('/api/articles/:id', function (req, res){
    return ArticleModel.findById(req.params.id, function (err, article) {
        if(!article) {
            res.statusCode = 404;
            return res.send({ error: 'Not found' });
        }
        return article.remove(function (err) {
            if (!err) {
                log.info("article removed");
                return res.send({ status: 'OK' });
            } else {
                res.statusCode = 500;
                log.error('Internal error(%d): %s',res.statusCode,err.message);
                return res.send({ error: 'Server error' });
            }
        });
    });
});











 Access control — OAuth 2.0, Passport.js

We will use OAuth 2. Perhaps this is redundant, but in the future, this approach facilitates an integration with other OAuth-providers. 
Module Passport.js will be responsible for access control. For OAuth2 server, I will use handy solution from the same author - OAuth2orize. Access tokens will be stored in MongoDB.

First you need to install all the required modules:

   $ npm i Faker     oauth2orize     passport     passport-http     passport-http-bearer     passport-oauth2-client-password  --save

Then, you need to add mongoose.js scheme for users and tokens:

var crypto = require('crypto');

////////////////////////////////////////////////////////////// User
var User = new Schema({
    username: {
        type: String,
        unique: true,
        required: true
    },
    hashedPassword: {
        type: String,
        required: true
    },
    salt: {
        type: String,
        required: true
    },
    created: {
        type: Date,
        default: Date.now
    }
});

User.methods.encryptPassword = function(password) {
    return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
    //more secure – return crypto.pbkdf2Sync(password, this.salt, 10000, 512);
};

User.virtual('userId')
    .get(function () {
        return this.id;
    });

User.virtual('password')
    .set(function(password) {
        this._plainPassword = password;
        this.salt = crypto.randomBytes(32).toString('hex');
        //more secure - this.salt = crypto.randomBytes(128).toString('hex');
        this.hashedPassword = this.encryptPassword(password);
    })
    .get(function() { return this._plainPassword; });


User.methods.checkPassword = function(password) {
    return this.encryptPassword(password) === this.hashedPassword;
};

var UserModel = mongoose.model('User', User);

//////////////////////////////////////////////////////////////////// Client
var Client = new Schema({
    name: {
        type: String,
        unique: true,
        required: true
    },
    clientId: {
        type: String,
        unique: true,
        required: true
    },
    clientSecret: {
        type: String,
        required: true
    }
});

var ClientModel = mongoose.model('Client', Client);

/////////////////////////////////////////////////////////////////// AccessToken
var AccessToken = new Schema({
    userId: {
        type: String,
        required: true
    },
    clientId: {
        type: String,
        required: true
    },
    token: {
        type: String,
        unique: true,
        required: true
    },
    created: {
        type: Date,
        default: Date.now
    }
});

var AccessTokenModel = mongoose.model('AccessToken', AccessToken);

////////////////////////////////////////////////////////////////////// RefreshToken
var RefreshToken = new Schema({
    userId: {
        type: String,
        required: true
    },
    clientId: {
        type: String,
        required: true
    },
    token: {
        type: String,
        unique: true,
        required: true
    },
    created: {
        type: Date,
        default: Date.now
    }
});

var RefreshTokenModel = mongoose.model('RefreshToken', RefreshToken);

module.exports.UserModel = UserModel;
module.exports.ClientModel = ClientModel;
module.exports.AccessTokenModel = AccessTokenModel;
module.exports.RefreshTokenModel = RefreshTokenModel;


Virtual property password is an example of how mongoose model can embed convenient logic.

DB objects:

    User – a user who has a name, password hash and a salt.
    Client – a client application which requests access on behalf of a user, has a name and a secret code.
    AccessToken – token (type of bearer), issued to the client application, limited by time.
    RefreshToken – another type of token allows you to request a new bearer-token without re-request a password from the user.
 
Add token lifetime to config.json:
{
    "port" : 1337,
    "security": {
        "tokenLife" : 3600
    },
    "mongoose": {
        "uri": "mongodb://localhost/testAPI"
    }
}

I implemented OAuth2 server and authorization logic in separate modules. In auth.js passport.js strategies are described. We connect 3 strategies – 2 for OAuth2 username-password flow and one to check the token.
$ mkdir auth && cd auth
$ vim auth.js
/*
I implemented OAuth2 server and authorization logic in separate modules. In auth.js passport.js strategies are described. 
We connect 3 strategies – 2 for OAuth2 username-password flow and one to check the token.

Check these out:
  http://blog.jeroenpelgrims.be/token-based-sessionless-auth-using-express-and-passport/  "Token based, sessionless auth using express and passport" 
  http://scottksmith.com/blog/2014/07/02/beer-locker-building-a-restful-api-with-node-oauth2-server/ goes for session. This strategy creates a session on the server and passes back the session id in a cookie. Subsequent requests would pass the session id in the cookie so you know who the authorized user is.
  This is exact same thing: https://github.com/reneweb/oauth2orize_resource_owner_password_example/blob/master/auth.js
 */
var passport = require('passport');

//strategies
var BasicStrategy = require('passport-http').BasicStrategy;
var ClientPasswordStrategy = require('passport-oauth2-client-password').Strategy;
var BearerStrategy = require('passport-http-bearer').Strategy;

var libs = process.cwd() + '/libs/';
var config = require(libs + 'config');

// db models
var User = require(libs + 'model/user');
var Client = require(libs + 'model/client');
var AccessToken = require(libs + 'model/accessToken');
var RefreshToken = require(libs + 'model/refreshToken');

/*
The Basic strategy implemented by Passport looks nearly identical to the local strategy, with one subtle difference. The basic strategy is to be used with API endpoints where the architecture is stateless. As a result, sessions are not required but can be used. This strategy should also use SSL/TLS. The session flag can be set like so:

app.get('/private', passport.authenticate('basic', { session: false }), function(req, res) {
  res.json(req.user);
});

If you a using the Passport Local strategy: - a session is established, so you don't have to send creds on each request; - username and password are provided in "username" and "password" headers by default;
If you a using the Passport Basic/Digest strategies: - a session is not used, so you must provide the credentials at every API call; - username and password/hash are contained within "Authorization" header;

 */
passport.use(new BasicStrategy(
    function(username, password, done) {
        Client.findOne({ clientId: username }, function(err, client) {
            if (err) { 
                return done(err); 
            }

            if (!client) { 
                return done(null, false); 
            }

            if (client.clientSecret !== password) { 
                return done(null, false); 
            }

            return done(null, client);
        });
    }
));

/*
authenticate requests containing client credentials in the request body,
The OAuth 2.0 client password authentication strategy authenticates clients using a client ID and client secret. The strategy requires a verify callback, which accepts those credentials and calls done providing a client.

passport.use(new ClientPasswordStrategy(
  function(clientId, clientSecret, done) {
    Clients.findOne({ clientId: clientId }, function (err, client) {
      if (err) { return done(err); }
      if (!client) { return done(null, false); }
      if (client.clientSecret != clientSecret) { return done(null, false); }
      return done(null, client);
    });
  }
));

ClientPasswordStrategy is used to authenticate clients as they request access tokens from the token endpoint.
https://github.com/jaredhanson/passport-oauth2-client-password/blob/master/lib/strategy.js
 */
passport.use(new ClientPasswordStrategy(
    function(clientId, clientSecret, done) {
        Client.findOne({ clientId: clientId }, function(err, client) {
            if (err) { 
                return done(err); 
            }

            if (!client) { 
                return done(null, false); 
            }

            if (client.clientSecret !== clientSecret) { 
                return done(null, false); 
            }

            return done(null, client);
        });
    }
));

/*
https://github.com/jaredhanson/passport-http-bearer/blob/master/lib/strategy.js
Bearer tokens are typically used protect API endpoints, and are often issued using OAuth 2.0.

The HTTP Bearer authentication strategy authenticates requests based on a bearer token contained in the `Authorization` header field, `access_token` body parameter, or `access_token` query parameter.

 Applications must supply a `verify` callback, for which the function signature is:

     function(token, done) { ... }

 `token` is the bearer token provided as a credential.  The verify callback is responsible for finding the user who posesses the token, and invoking `done` with the following arguments:

     done(err, user, info);

 If the token is not valid, `user` should be set to `false` to indicate an authentication failure.  Additional token `info` can optionally be passed as
 a third argument, which will be set by Passport at `req.authInfo`, where it can be used by later middleware for access control.  This is typically used
 to pass any scope associated with the token.

 Options:

   - `realm`  authentication realm, defaults to "Users"
   - `scope`  list of scope values indicating the required scope of the access
              token for accessing the requested resource

 Examples:

     passport.use(new BearerStrategy(
       function(token, done) {
         User.findByToken({ token: token }, function (err, user) {
           if (err) { return done(err); }
           if (!user) { return done(null, false); }
           return done(null, user, { scope: 'read' });
         });
       }
      ));

Optional info can be passed, typically including associated scope, which will be set by Passport at req.authInfo to be used by later middleware for authorization and access control.

passport.use(new BearerStrategy(
  function(token, done) {
    User.findOne({ token: token }, function (err, user) {
      if (err) { return done(err); }
      if (!user) { return done(null, false); }
      return done(null, user, { scope: 'all' });
    });
  }
));

 */
passport.use(new BearerStrategy(
    function(accessToken, done) {
        AccessToken.findOne({ token: accessToken }, function(err, token) {

            if (err) { 
                return done(err); 
            }

            if (!token) {   // token not found
                return done(null, false); 
            }

            if( Math.round((Date.now()-token.created)/1000) > config.get('security:tokenLife') ) {  //check for expired token

                AccessToken.remove({ token: accessToken }, function (err) {
                    if (err) {
                        return done(err);
                    } 
                });

                return done(null, false, { message: 'Token expired' });
            }

            User.findById(token.userId, function(err, user) {
            
                if (err) { 
                    return done(err); 
                }

                if (!user) { 
                    return done(null, false, { message: 'Unknown user' }); 
                }

                var info = { scope: '*' };
                done(null, user, info);
            });
        });
    }
));





$ vim oauth2.js
/*
oauth2.js is responsible for the issuance and renewal of the token: One token exchange strategy is for username-password flow, another is to refresh tokens.
 */

var oauth2orize = require('oauth2orize');
var passport = require('passport');
var crypto = require('crypto');

var libs = process.cwd() + '/libs/';

var config = require(libs + 'config');
var log = require(libs + 'log')(module);

var db = require(libs + 'db/mongoose');
var User = require(libs + 'model/user');
var AccessToken = require(libs + 'model/accessToken');
var RefreshToken = require(libs + 'model/refreshToken');

// create OAuth 2.0 server
var aserver = oauth2orize.createServer();

// Generic error handler
var errFn = function (cb, err) {
    if (err) { 
        return cb(err); 
    }
};

/* 
  Destroys any old tokens and generates a new access and refresh token

Data is used in AccessToken: 
    {
        userId: user.userId, 
        clientId: client.clientId 
        tokenvalue 
    }
*/
var generateTokens = function (data, done) {    

// function.bind(undefined, arg1)  // Create a function with a preset leading argument    
    var errorHandler = errFn.bind(undefined, done), // curries in `done` callback so we don't need to pass it
        refreshToken,
        refreshTokenValue,
        token,
        tokenValue;

    RefreshToken.remove(data, errorHandler);
    AccessToken.remove(data, errorHandler);

    tokenValue = crypto.randomBytes(32).toString('hex');  //access token
    refreshTokenValue = crypto.randomBytes(32).toString('hex');  //refresh token

    data.token = tokenValue;
    token = new AccessToken(data);

    data.token = refreshTokenValue;
    refreshToken = new RefreshToken(data);

    refreshToken.save(errorHandler);

    token.save(function (err) {
        if (err) {
            
            log.error(err);
            return done(err); 
        }
        done(null, tokenValue, refreshTokenValue, { 
            'expires_in': config.get('security:tokenLife') 
        });
    });
};

/*
    Exchange username & password for access token.
 */ 
aserver.exchange(oauth2orize.exchange.password(function(client, username, password, scope, done) {
    
    User.findOne({ username: username }, function(err, user) {
        
        if (err) { 
            return done(err); 
        }
        
        if (!user || !user.checkPassword(password)) {
            return done(null, false);
        }

        var model = { 
            userId: user.userId, 
            clientId: client.clientId 
        };

        generateTokens(model, done);
    });

}));

/*
    Exchange refreshToken for access token.
 */ 
aserver.exchange(oauth2orize.exchange.refreshToken(function(client, refreshToken, scope, done) {

    RefreshToken.findOne({ token: refreshToken, clientId: client.clientId }, function(err, token) {
        if (err) { 
            return done(err); 
        }

        if (!token) { 
            return done(null, false); 
        }

        User.findById(token.userId, function(err, user) {
            if (err) { return done(err); }
            if (!user) { return done(null, false); }

            var model = { 
                userId: user.userId, 
                clientId: client.clientId 
            };

            generateTokens(model, done);
        });
    });
}));

// token endpoint
//
// `token` middleware handles client requests to exchange authorization grants for access tokens.  
// Based on the grant type being exchanged, the above exchange middleware will be invoked to handle the request.  
// Clients must authenticate when making requests to this endpoint.

exports.token = [
    passport.authenticate(['basic', 'oauth2-client-password'], { session: false }),    //state-less
    aserver.token(),
    aserver.errorHandler()
];


Connect these modules with server.js:

var oauth2 = require('./libs/oauth2');

app.use(passport.initialize());

require('./libs/auth');

app.post('/oauth/token', oauth2.token);

app.get('/api/userInfo',
    passport.authenticate('bearer', { session: false }),
        function(req, res) {
            // req.authInfo is set using the `info` argument supplied by
            // `BearerStrategy`.  It is typically used to indicate a scope of the token,
            // and used in access control checks.  For illustrative purposes, this
            // example simply returns the scope in the response.
            res.json({ user_id: req.user.userId, name: req.user.username, scope: req.authInfo.scope })
        }
);



For example, the access is restricted on localhost:1337/api/userInfo.

To check the auth logic, we should create a user and a client in our database. Use this node application, which will create the necessary objects and remove redundant from collections. It helps quickly clean the tokens and users for testing.
$ vim dataGen.js
var log                 = require('./libs/log')(module);
var mongoose            = require('./libs/mongoose').mongoose;
var UserModel           = require('./libs/mongoose').UserModel;
var ClientModel         = require('./libs/mongoose').ClientModel;
var AccessTokenModel    = require('./libs/mongoose').AccessTokenModel;
var RefreshTokenModel   = require('./libs/mongoose').RefreshTokenModel;
var faker               = require('Faker');

UserModel.remove({}, function(err) {
    //generate a preset user
    var user = new UserModel({ username: "andrey", password: "simplepassword" });
    user.save(function(err, user) {
        if(err) return log.error(err);
        else log.info("New user - %s:%s",user.username,user.password);
    });

    for(i=0; i<4; i++) {  // use faker to generate 4 fake users
        var user = new UserModel({ username: faker.random.first_name().toLowerCase(), password: faker.Lorem.words(1)[0] });
        user.save(function(err, user) {
            if(err) return log.error(err);
            else log.info("New user - %s:%s",user.username,user.password);
        });
    }
});

ClientModel.remove({}, function(err) { // generate a client
    var client = new ClientModel({ name: "OurService iOS client v1", clientId: "mobileV1", clientSecret:"abc123456" }); // this is not used in simulation calls at all. a new client is accessing
    client.save(function(err, client) {
        if(err) return log.error(err);
        else log.info("New client - %s:%s",client.clientId,client.clientSecret);
    });
});

AccessTokenModel.remove({}, function (err) {  //clear AccessTokens
    if (err) return log.error(err);
});
RefreshTokenModel.remove({}, function (err) { // clear RefreshTokens
    if (err) return log.error(err);
});

setTimeout(function() {
    mongoose.disconnect();
}, 3000);


$ node dataGen.js



=======================
=======================
======================


Make Requests


Creating and refreshing access tokens:

http POST http://localhost:3000/api/oauth/token grant_type=password client_id=android client_secret=SomeRandomCharsAndNumbers username=myapi password=abc1234             #user authenticates and gets a set of tokens
http POST http://localhost:3000/api/oauth/token grant_type=refresh_token client_id=android client_secret=SomeRandomCharsAndNumbers refresh_token=[TOKEN]                   # use refresh token to access the same resource
Creating your article data:

http POST http://localhost:3000/api/articles title=NewArticle author='John Doe' description='Lorem ipsum dolar sit amet' images:='[{"kind":"thumbnail", "url":"http://habrahabr.ru/images/write-topic.png"}, {"kind":"detail", "url":"http://habrahabr.ru/images/write-topic.png"}]' Authorization:'Bearer PUT_YOUR_TOKEN_HERE'     #access token
Updating your article data:

http PUT http://localhost:3000/api/articles/YOUR_ARTICLE_ID_HERE title=NewArticleUpdated author='John Doe' description='Lorem ipsum dolar sit amet' images:='[{"kind":"thumbnail", "url":"http://habrahabr.ru/images/write-topic.png"}, {"kind":"detail", "url":"http://habrahabr.ru/images/write-topic.png"}]' Authorization:'Bearer PUT_YOUR_TOKEN_HERE'      #bearer means access token
Getting your data

http http://localhost:3000/api/users/info Authorization:'Bearer PUT_YOUR_TOKEN_HERE'
http http://localhost:3000/api/articles Authorization:'Bearer PUT_YOUR_TOKEN_HERE'


"access_token": "276e7898597c28b8b75d51020bcb3ba3b4319471e72d8ba164e350652dcac465",
 "refresh_token": "34626e6a32e4b28cbd96b43e9c98a4ca7a8c65f6575cd77f64f07bcf865d5279",








Comments