Skip to content

Commit

Permalink
馃帹 one token endpoint (#7571)
Browse files Browse the repository at this point in the history
* 馃帹  one token endpoint

refs #7562
- delete /authentication/ghost
- Ghost-Admin will use /authentication/token for all use cases (password, refresh token and ghost.org authorization code)
- add new grant_type `authorization_code`

* 馃帹  update comment description and remove spamPrevention.resetCounter
  • Loading branch information
kirrg001 authored and sebgie committed Oct 17, 2016
1 parent 25b0c9e commit 4056a6d
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 89 deletions.
6 changes: 0 additions & 6 deletions core/server/api/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,12 +185,6 @@ function apiRoutes() {
auth.oauth.generateAccessToken
);

apiRouter.post('/authentication/ghost', [
auth.authenticate.authenticateClient,
auth.authenticate.authenticateGhostUser,
api.http(api.authentication.createTokens)
]);

apiRouter.post('/authentication/revoke', authenticatePrivate, api.http(api.authentication.revoke));

// ## Uploads
Expand Down
23 changes: 0 additions & 23 deletions core/server/auth/authenticate.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,29 +105,6 @@ authenticate = {
}));
}
)(req, res, next);
},

// ### Authenticate Ghost.org User
authenticateGhostUser: function authenticateGhostUser(req, res, next) {
req.query.code = req.body.authorizationCode;

if (!req.query.code) {
return next(new errors.UnauthorizedError({message: i18n.t('errors.middleware.auth.accessDenied')}));
}

passport.authenticate('ghost', {session: false, failWithError: false}, function authenticate(err, user, info) {
if (err) {
return next(err);
}

if (!user) {
return next(new errors.UnauthorizedError({message: i18n.t('errors.middleware.auth.accessDenied')}));
}

req.authInfo = info;
req.user = user;
next();
})(req, res, next);
}
};

Expand Down
54 changes: 54 additions & 0 deletions core/server/auth/oauth.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
var oauth2orize = require('oauth2orize'),
passport = require('passport'),
models = require('../models'),
utils = require('../utils'),
errors = require('../errors'),
Expand Down Expand Up @@ -62,6 +63,42 @@ function exchangePassword(client, username, password, scope, done) {
});
}

function exchangeAuthorizationCode(req, res, next) {
if (!req.body.authorizationCode) {
return next(new errors.UnauthorizedError({
message: i18n.t('errors.middleware.auth.accessDenied')
}));
}

req.query.code = req.body.authorizationCode;

passport.authenticate('ghost', {session: false, failWithError: false}, function authenticate(err, user) {
if (err) {
return next(new errors.UnauthorizedError({
err: err
}));
}

if (!user) {
return next(new errors.UnauthorizedError({
message: i18n.t('errors.middleware.auth.accessDenied')
}));
}

authenticationAPI.createTokens({}, {context: {client_id: req.client.id, user: user.id}})
.then(function then(response) {
res.json({
access_token: response.access_token,
refresh_token: response.refresh_token,
expires_in: response.expires_in
});
})
.catch(function (err) {
next(err);
});
})(req, res, next);
}

oauth = {

init: function init() {
Expand All @@ -85,6 +122,23 @@ oauth = {
// access token on behalf of the user who authorized the code.
oauthServer.exchange(oauth2orize.exchange.refreshToken({userProperty: 'client'},
exchangeRefreshToken));

/**
* Exchange authorization_code for an access token.
* We forward to authorization code to Ghost.org.
*
* oauth2orize offers a default implementation via exchange.authorizationCode, but this function
* wraps the express request and response. So no chance to get access to it.
* We use passport to communicate with Ghost.org. Passport's module design requires the express req/res.
*
* For now it's OK to not use exchange.authorizationCode. You can read through the implementation here:
* https://github.com/jaredhanson/oauth2orize/blob/master/lib/exchange/authorizationCode.js
* As you can see, it does some validation and set's some headers, not very very important,
* but it's part of the oauth2 spec.
*
* @TODO: How to use exchange.authorizationCode in combination of passport?
*/
oauthServer.exchange('authorization_code', exchangeAuthorizationCode);
},

// ### Generate access token Middleware
Expand Down
2 changes: 1 addition & 1 deletion core/server/middleware/api/spam-prevention.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ spamPrevention = {

if (req.body.username && req.body.grant_type === 'password') {
loginSecurity.push({ip: remoteAddress, time: currentTime, email: req.body.username});
} else if (req.body.grant_type === 'refresh_token') {
} else if (req.body.grant_type === 'refresh_token' || req.body.grant_type === 'authorization_code') {
return next();
} else {
return next(new errors.BadRequestError({message: i18n.t('errors.middleware.spamprevention.noUsername')}));
Expand Down
Loading

0 comments on commit 4056a6d

Please sign in to comment.