aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorChocobozzz <florian.bigard@gmail.com>2016-07-20 16:23:58 +0200
committerChocobozzz <florian.bigard@gmail.com>2016-07-20 16:23:58 +0200
commit2f372a865487427ff97ad17edd0e6adfbb478c80 (patch)
tree84e8b2ba2240e83dcdeeb057579327b0a663974b
parent66698b833f77806045b36fdeba612acda43dd7f7 (diff)
downloadPeerTube-2f372a865487427ff97ad17edd0e6adfbb478c80.tar.gz
PeerTube-2f372a865487427ff97ad17edd0e6adfbb478c80.tar.zst
PeerTube-2f372a865487427ff97ad17edd0e6adfbb478c80.zip
Server: implement refresh token
-rw-r--r--server/controllers/api/v1/users.js1
-rw-r--r--server/initializers/constants.js6
-rw-r--r--server/initializers/installer.js2
-rw-r--r--server/lib/oauth-model.js32
-rw-r--r--server/middlewares/oauth.js3
-rw-r--r--server/models/oauth-client.js4
-rw-r--r--server/models/oauth-token.js37
-rw-r--r--server/models/user.js8
-rw-r--r--server/tests/api/users.js8
9 files changed, 78 insertions, 23 deletions
diff --git a/server/controllers/api/v1/users.js b/server/controllers/api/v1/users.js
index 3f8d8ad92..fbbe6e472 100644
--- a/server/controllers/api/v1/users.js
+++ b/server/controllers/api/v1/users.js
@@ -12,6 +12,7 @@ const router = express.Router()
12 12
13router.get('/client', getAngularClient) 13router.get('/client', getAngularClient)
14router.post('/token', oAuth.token, success) 14router.post('/token', oAuth.token, success)
15// TODO: Once https://github.com/oauthjs/node-oauth2-server/pull/289 is merged,, implement revoke token route
15 16
16// --------------------------------------------------------------------------- 17// ---------------------------------------------------------------------------
17 18
diff --git a/server/initializers/constants.js b/server/initializers/constants.js
index 32fe1645f..7fcf5b01b 100644
--- a/server/initializers/constants.js
+++ b/server/initializers/constants.js
@@ -12,6 +12,11 @@ const FRIEND_SCORE = {
12// Time to wait between requests to the friends (10 min) 12// Time to wait between requests to the friends (10 min)
13let INTERVAL = 600000 13let INTERVAL = 600000
14 14
15const OAUTH_LIFETIME = {
16 ACCESS_TOKEN: 3600 * 4, // 4 hours
17 REFRESH_TOKEN: 1209600 // 2 weeks
18}
19
15// Number of results by default for the pagination 20// Number of results by default for the pagination
16const PAGINATION_COUNT_DEFAULT = 15 21const PAGINATION_COUNT_DEFAULT = 15
17 22
@@ -71,6 +76,7 @@ module.exports = {
71 API_VERSION: API_VERSION, 76 API_VERSION: API_VERSION,
72 FRIEND_SCORE: FRIEND_SCORE, 77 FRIEND_SCORE: FRIEND_SCORE,
73 INTERVAL: INTERVAL, 78 INTERVAL: INTERVAL,
79 OAUTH_LIFETIME: OAUTH_LIFETIME,
74 PAGINATION_COUNT_DEFAULT: PAGINATION_COUNT_DEFAULT, 80 PAGINATION_COUNT_DEFAULT: PAGINATION_COUNT_DEFAULT,
75 PODS_SCORE: PODS_SCORE, 81 PODS_SCORE: PODS_SCORE,
76 REQUESTS_IN_PARALLEL: REQUESTS_IN_PARALLEL, 82 REQUESTS_IN_PARALLEL: REQUESTS_IN_PARALLEL,
diff --git a/server/initializers/installer.js b/server/initializers/installer.js
index 490084104..32830d4da 100644
--- a/server/initializers/installer.js
+++ b/server/initializers/installer.js
@@ -66,7 +66,7 @@ function createOAuthClientIfNotExist (callback) {
66 const secret = passwordGenerator(32, false) 66 const secret = passwordGenerator(32, false)
67 const client = new Client({ 67 const client = new Client({
68 clientSecret: secret, 68 clientSecret: secret,
69 grants: [ 'password' ] 69 grants: [ 'password', 'refresh_token' ]
70 }) 70 })
71 71
72 client.save(function (err, createdClient) { 72 client.save(function (err, createdClient) {
diff --git a/server/lib/oauth-model.js b/server/lib/oauth-model.js
index f4fd9805a..555a54e90 100644
--- a/server/lib/oauth-model.js
+++ b/server/lib/oauth-model.js
@@ -12,6 +12,7 @@ const OAuthModel = {
12 getClient: getClient, 12 getClient: getClient,
13 getRefreshToken: getRefreshToken, 13 getRefreshToken: getRefreshToken,
14 getUser: getUser, 14 getUser: getUser,
15 revokeToken: revokeToken,
15 saveToken: saveToken 16 saveToken: saveToken
16} 17}
17 18
@@ -20,7 +21,7 @@ const OAuthModel = {
20function getAccessToken (bearerToken) { 21function getAccessToken (bearerToken) {
21 logger.debug('Getting access token (bearerToken: ' + bearerToken + ').') 22 logger.debug('Getting access token (bearerToken: ' + bearerToken + ').')
22 23
23 return OAuthToken.loadByTokenAndPopulateUser(bearerToken) 24 return OAuthToken.getByTokenAndPopulateUser(bearerToken)
24} 25}
25 26
26function getClient (clientId, clientSecret) { 27function getClient (clientId, clientSecret) {
@@ -28,19 +29,36 @@ function getClient (clientId, clientSecret) {
28 29
29 // TODO req validator 30 // TODO req validator
30 const mongoId = new mongoose.mongo.ObjectID(clientId) 31 const mongoId = new mongoose.mongo.ObjectID(clientId)
31 return OAuthClient.loadByIdAndSecret(mongoId, clientSecret) 32 return OAuthClient.getByIdAndSecret(mongoId, clientSecret)
32} 33}
33 34
34function getRefreshToken (refreshToken) { 35function getRefreshToken (refreshToken, callback) {
35 logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').') 36 logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').')
36 37
37 return OAuthToken.loadByRefreshToken(refreshToken) 38 return OAuthToken.getByRefreshTokenAndPopulateClient(refreshToken)
38} 39}
39 40
40function getUser (username, password) { 41function getUser (username, password) {
41 logger.debug('Getting User (username: ' + username + ', password: ' + password + ').') 42 logger.debug('Getting User (username: ' + username + ', password: ' + password + ').')
42 43
43 return User.loadByUsernameAndPassword(username, password) 44 return User.getByUsernameAndPassword(username, password)
45}
46
47function revokeToken (token) {
48 return OAuthToken.getByRefreshToken(token.refreshToken).then(function (tokenDB) {
49 if (tokenDB) tokenDB.remove()
50
51 /*
52 * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js
53 * "As per the discussion we need set older date
54 * revokeToken will expected return a boolean in future version
55 * https://github.com/oauthjs/node-oauth2-server/pull/274
56 * https://github.com/oauthjs/node-oauth2-server/issues/290"
57 */
58 const expiredToken = tokenDB
59 expiredToken.refreshTokenExpiresAt = new Date('2015-05-28T06:59:53.000Z')
60 return expiredToken
61 })
44} 62}
45 63
46function saveToken (token, client, user) { 64function saveToken (token, client, user) {
@@ -48,10 +66,10 @@ function saveToken (token, client, user) {
48 66
49 const tokenObj = new OAuthToken({ 67 const tokenObj = new OAuthToken({
50 accessToken: token.accessToken, 68 accessToken: token.accessToken,
51 accessTokenExpiresOn: token.accessTokenExpiresOn, 69 accessTokenExpiresAt: token.accessTokenExpiresAt,
52 client: client.id, 70 client: client.id,
53 refreshToken: token.refreshToken, 71 refreshToken: token.refreshToken,
54 refreshTokenExpiresOn: token.refreshTokenExpiresOn, 72 refreshTokenExpiresAt: token.refreshTokenExpiresAt,
55 user: user.id 73 user: user.id
56 }) 74 })
57 75
diff --git a/server/middlewares/oauth.js b/server/middlewares/oauth.js
index 3d7429f1d..91a990509 100644
--- a/server/middlewares/oauth.js
+++ b/server/middlewares/oauth.js
@@ -2,9 +2,12 @@
2 2
3const OAuthServer = require('express-oauth-server') 3const OAuthServer = require('express-oauth-server')
4 4
5const constants = require('../initializers/constants')
5const logger = require('../helpers/logger') 6const logger = require('../helpers/logger')
6 7
7const oAuthServer = new OAuthServer({ 8const oAuthServer = new OAuthServer({
9 accessTokenLifetime: constants.OAUTH_LIFETIME.ACCESS_TOKEN,
10 refreshTokenLifetime: constants.OAUTH_LIFETIME.REFRESH_TOKEN,
8 model: require('../lib/oauth-model') 11 model: require('../lib/oauth-model')
9}) 12})
10 13
diff --git a/server/models/oauth-client.js b/server/models/oauth-client.js
index 048e5af48..830f68857 100644
--- a/server/models/oauth-client.js
+++ b/server/models/oauth-client.js
@@ -11,8 +11,8 @@ const OAuthClientSchema = mongoose.Schema({
11OAuthClientSchema.path('clientSecret').required(true) 11OAuthClientSchema.path('clientSecret').required(true)
12 12
13OAuthClientSchema.statics = { 13OAuthClientSchema.statics = {
14 getByIdAndSecret: getByIdAndSecret,
14 list: list, 15 list: list,
15 loadByIdAndSecret: loadByIdAndSecret,
16 loadFirstClient: loadFirstClient 16 loadFirstClient: loadFirstClient
17} 17}
18 18
@@ -28,6 +28,6 @@ function loadFirstClient (callback) {
28 return this.findOne({}, callback) 28 return this.findOne({}, callback)
29} 29}
30 30
31function loadByIdAndSecret (id, clientSecret) { 31function getByIdAndSecret (id, clientSecret) {
32 return this.findOne({ _id: id, clientSecret: clientSecret }) 32 return this.findOne({ _id: id, clientSecret: clientSecret })
33} 33}
diff --git a/server/models/oauth-token.js b/server/models/oauth-token.js
index 5da5da417..23c698732 100644
--- a/server/models/oauth-token.js
+++ b/server/models/oauth-token.js
@@ -1,13 +1,15 @@
1const mongoose = require('mongoose') 1const mongoose = require('mongoose')
2 2
3const logger = require('../helpers/logger')
4
3// --------------------------------------------------------------------------- 5// ---------------------------------------------------------------------------
4 6
5const OAuthTokenSchema = mongoose.Schema({ 7const OAuthTokenSchema = mongoose.Schema({
6 accessToken: String, 8 accessToken: String,
7 accessTokenExpiresOn: Date, 9 accessTokenExpiresAt: Date,
8 client: { type: mongoose.Schema.Types.ObjectId, ref: 'OAuthClient' }, 10 client: { type: mongoose.Schema.Types.ObjectId, ref: 'OAuthClient' },
9 refreshToken: String, 11 refreshToken: String,
10 refreshTokenExpiresOn: Date, 12 refreshTokenExpiresAt: Date,
11 user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' } 13 user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' }
12}) 14})
13 15
@@ -16,19 +18,38 @@ OAuthTokenSchema.path('client').required(true)
16OAuthTokenSchema.path('user').required(true) 18OAuthTokenSchema.path('user').required(true)
17 19
18OAuthTokenSchema.statics = { 20OAuthTokenSchema.statics = {
19 loadByRefreshToken: loadByRefreshToken, 21 getByRefreshTokenAndPopulateClient: getByRefreshTokenAndPopulateClient,
20 loadByTokenAndPopulateUser: loadByTokenAndPopulateUser 22 getByTokenAndPopulateUser: getByTokenAndPopulateUser,
23 getByRefreshToken: getByRefreshToken
21} 24}
22 25
23mongoose.model('OAuthToken', OAuthTokenSchema) 26mongoose.model('OAuthToken', OAuthTokenSchema)
24 27
25// --------------------------------------------------------------------------- 28// ---------------------------------------------------------------------------
26 29
27function loadByRefreshToken (refreshToken, callback) { 30function getByRefreshTokenAndPopulateClient (refreshToken) {
28 return this.findOne({ refreshToken: refreshToken }, callback) 31 return this.findOne({ refreshToken: refreshToken }).populate('client').then(function (token) {
32 if (!token) return token
33
34 const tokenInfos = {
35 refreshToken: token.refreshToken,
36 refreshTokenExpiresAt: token.refreshTokenExpiresAt,
37 client: {
38 id: token.client._id.toString()
39 },
40 user: token.user
41 }
42
43 return tokenInfos
44 }).catch(function (err) {
45 logger.info('getRefreshToken error.', { error: err })
46 })
29} 47}
30 48
31function loadByTokenAndPopulateUser (bearerToken, callback) { 49function getByTokenAndPopulateUser (bearerToken) {
32 // FIXME: allow to use callback
33 return this.findOne({ accessToken: bearerToken }).populate('user') 50 return this.findOne({ accessToken: bearerToken }).populate('user')
34} 51}
52
53function getByRefreshToken (refreshToken) {
54 return this.findOne({ refreshToken: refreshToken })
55}
diff --git a/server/models/user.js b/server/models/user.js
index 130b49b55..14ffecbff 100644
--- a/server/models/user.js
+++ b/server/models/user.js
@@ -11,8 +11,8 @@ UserSchema.path('password').required(true)
11UserSchema.path('username').required(true) 11UserSchema.path('username').required(true)
12 12
13UserSchema.statics = { 13UserSchema.statics = {
14 list: list, 14 getByUsernameAndPassword: getByUsernameAndPassword,
15 loadByUsernameAndPassword: loadByUsernameAndPassword 15 list: list
16} 16}
17 17
18mongoose.model('User', UserSchema) 18mongoose.model('User', UserSchema)
@@ -23,6 +23,6 @@ function list (callback) {
23 return this.find(callback) 23 return this.find(callback)
24} 24}
25 25
26function loadByUsernameAndPassword (username, password, callback) { 26function getByUsernameAndPassword (username, password) {
27 return this.findOne({ username: username, password: password }, callback) 27 return this.findOne({ username: username, password: password })
28} 28}
diff --git a/server/tests/api/users.js b/server/tests/api/users.js
index 749aa8af8..68ba9de33 100644
--- a/server/tests/api/users.js
+++ b/server/tests/api/users.js
@@ -144,7 +144,7 @@ describe('Test users', function () {
144 utils.removeVideo(server.url, accessToken, videoId, done) 144 utils.removeVideo(server.url, accessToken, videoId, done)
145 }) 145 })
146 146
147 it('Should logout') 147 it('Should logout (revoke token)')
148 148
149 it('Should not be able to upload a video') 149 it('Should not be able to upload a video')
150 150
@@ -152,6 +152,12 @@ describe('Test users', function () {
152 152
153 it('Should be able to login again') 153 it('Should be able to login again')
154 154
155 it('Should have an expired access token')
156
157 it('Should refresh the token')
158
159 it('Should be able to upload a video again')
160
155 after(function (done) { 161 after(function (done) {
156 process.kill(-server.app.pid) 162 process.kill(-server.app.pid)
157 163