From c684ee5b38ad72941aacab9c861a8f06e09cace2 Mon Sep 17 00:00:00 2001 From: Fabian Becker Date: Mon, 1 Sep 2014 18:02:46 +0000 Subject: [PATCH] Delete revoked tokens closes #3758 - new API method to delete access and refresh token - use new ember-simple-auth config to revoke tokens on logout - new method to delete tokens by .. token --- bower.json | 2 +- core/client/initializers/authentication.js | 1 + core/server/api/authentication.js | 19 +++++++++++++++++++ core/server/models/basetoken.js | 22 +++++++++++++++++++++- core/server/routes/api.js | 1 + 5 files changed, 43 insertions(+), 2 deletions(-) diff --git a/bower.json b/bower.json index 857e3e0367..9a7d8e76d4 100644 --- a/bower.json +++ b/bower.json @@ -8,7 +8,7 @@ "ember-data": "~1.0.0-beta.8", "ember-load-initializers": "git://github.com/stefanpenner/ember-load-initializers.git#0.0.1", "ember-resolver": "git://github.com/stefanpenner/ember-jj-abrams-resolver.git#181251821cf513bb58d3e192faa13245a816f75e", - "ember-simple-auth": "0.6.4", + "ember-simple-auth": "0.6.6", "fastclick": "1.0.0", "handlebars": "1.3.0", "ic-ajax": "1.0.1", diff --git a/core/client/initializers/authentication.js b/core/client/initializers/authentication.js index 9435e5e13c..fef1ae4ace 100644 --- a/core/client/initializers/authentication.js +++ b/core/client/initializers/authentication.js @@ -22,6 +22,7 @@ var AuthenticationInitializer = { }); SimpleAuth.Authenticators.OAuth2.reopen({ serverTokenEndpoint: Ghost.apiRoot + '/authentication/token', + serverTokenRevocationEndpoint: Ghost.apiRoot + '/authentication/revoke', refreshAccessTokens: true, makeRequest: function (url, data) { data.client_id = 'ghost-admin'; diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index 6e61b431fe..ea2599102d 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -234,6 +234,25 @@ authentication = { }).then(function () { return Promise.resolve({ users: [setupUser]}); }); + }, + + revoke: function (object) { + var token; + + if (object.token_type_hint && object.token_type_hint === 'access_token') { + token = dataProvider.Accesstoken; + } else if (object.token_type_hint && object.token_type_hint === 'refresh_token') { + token = dataProvider.Refreshtoken; + } else { + return errors.BadRequestError('Invalid token_type_hint given.'); + } + + return token.destroyByToken({ token: object.token }).then(function () { + return Promise.resolve({ token: object.token }); + }, function () { + // On error we still want a 200. See https://tools.ietf.org/html/rfc7009#page-5 + return Promise.resolve({ token: object.token, error: 'Invalid token provided' }); + }); } }; diff --git a/core/server/models/basetoken.js b/core/server/models/basetoken.js index b0efbcce60..dc5bdcd40d 100644 --- a/core/server/models/basetoken.js +++ b/core/server/models/basetoken.js @@ -57,8 +57,28 @@ Basetoken = ghostBookshelf.Model.extend({ } return Promise.reject(new errors.NotFoundError('No user found')); - } + }, + /** + * ### destroyByToken + * @param {[type]} options has token where token is the token to destroy + */ + destroyByToken: function (options) { + var token = options.token; + + options = this.filterOptions(options, 'destroyByUser'); + + if (token) { + return ghostBookshelf.Collection.forge([], {model: this}) + .query('where', 'token', '=', token) + .fetch(options) + .then(function (collection) { + collection.invokeThen('destroy', options); + }); + } + + return Promise.reject(new errors.NotFoundError('Token not found')); + }, }); module.exports = Basetoken; \ No newline at end of file diff --git a/core/server/routes/api.js b/core/server/routes/api.js index f35e4eab39..b7afc0f86e 100644 --- a/core/server/routes/api.js +++ b/core/server/routes/api.js @@ -81,6 +81,7 @@ apiRoutes = function (middleware) { middleware.authenticateClient, middleware.generateAccessToken ); + router.post('/authentication/revoke', api.http(api.authentication.revoke)); // ## Uploads router.post('/uploads', middleware.busboy, api.http(api.uploads.add));