]> git.immae.eu Git - github/Chocobozzz/PeerTube.git/blame - server/lib/oauth-model.ts
Merge branch 'release/1.4.0' into develop
[github/Chocobozzz/PeerTube.git] / server / lib / oauth-model.ts
CommitLineData
cef534ed 1import * as Bluebird from 'bluebird'
141b177d 2import { AccessDeniedError } from 'oauth2-server'
da854ddd 3import { logger } from '../helpers/logger'
3fd3ab2d
C
4import { UserModel } from '../models/account/user'
5import { OAuthClientModel } from '../models/oauth/oauth-client'
6import { OAuthTokenModel } from '../models/oauth/oauth-token'
557b13ae 7import { LRU_CACHE } from '../initializers/constants'
f201a749 8import { Transaction } from 'sequelize'
6dd9de95 9import { CONFIG } from '../initializers/config'
557b13ae 10import * as LRUCache from 'lru-cache'
453e83ea 11import { MOAuthTokenUser } from '@server/typings/models/oauth/oauth-token'
69b0a27c 12
69818c93 13type TokenInfo = { accessToken: string, refreshToken: string, accessTokenExpiresAt: Date, refreshTokenExpiresAt: Date }
557b13ae 14
453e83ea 15const accessTokenCache = new LRUCache<string, MOAuthTokenUser>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
557b13ae 16const userHavingToken = new LRUCache<number, string>({ max: LRU_CACHE.USER_TOKENS.MAX_SIZE })
69818c93 17
69b0a27c
C
18// ---------------------------------------------------------------------------
19
f201a749
C
20function deleteUserToken (userId: number, t?: Transaction) {
21 clearCacheByUserId(userId)
22
23 return OAuthTokenModel.deleteUserToken(userId, t)
24}
25
26function clearCacheByUserId (userId: number) {
557b13ae
C
27 const token = userHavingToken.get(userId)
28
f201a749 29 if (token !== undefined) {
557b13ae
C
30 accessTokenCache.del(token)
31 userHavingToken.del(userId)
f201a749
C
32 }
33}
34
35function clearCacheByToken (token: string) {
557b13ae
C
36 const tokenModel = accessTokenCache.get(token)
37
f201a749 38 if (tokenModel !== undefined) {
557b13ae
C
39 userHavingToken.del(tokenModel.userId)
40 accessTokenCache.del(token)
f201a749
C
41 }
42}
43
69818c93 44function getAccessToken (bearerToken: string) {
69b0a27c
C
45 logger.debug('Getting access token (bearerToken: ' + bearerToken + ').')
46
3acc5084
C
47 if (!bearerToken) return Bluebird.resolve(undefined)
48
557b13ae 49 if (accessTokenCache.has(bearerToken)) return Bluebird.resolve(accessTokenCache.get(bearerToken))
f201a749 50
3fd3ab2d 51 return OAuthTokenModel.getByTokenAndPopulateUser(bearerToken)
f201a749
C
52 .then(tokenModel => {
53 if (tokenModel) {
557b13ae
C
54 accessTokenCache.set(bearerToken, tokenModel)
55 userHavingToken.set(tokenModel.userId, tokenModel.accessToken)
f201a749
C
56 }
57
58 return tokenModel
59 })
69b0a27c
C
60}
61
69818c93 62function getClient (clientId: string, clientSecret: string) {
69b0a27c
C
63 logger.debug('Getting Client (clientId: ' + clientId + ', clientSecret: ' + clientSecret + ').')
64
3fd3ab2d 65 return OAuthClientModel.getByIdAndSecret(clientId, clientSecret)
69b0a27c
C
66}
67
69818c93 68function getRefreshToken (refreshToken: string) {
69b0a27c
C
69 logger.debug('Getting RefreshToken (refreshToken: ' + refreshToken + ').')
70
3fd3ab2d 71 return OAuthTokenModel.getByRefreshTokenAndPopulateClient(refreshToken)
69b0a27c
C
72}
73
ba12e8b3
C
74async function getUser (usernameOrEmail: string, password: string) {
75 logger.debug('Getting User (username/email: ' + usernameOrEmail + ', password: ******).')
69b0a27c 76
ba12e8b3 77 const user = await UserModel.loadByUsernameOrEmail(usernameOrEmail)
f5028693 78 if (!user) return null
26d7d31b 79
f5028693
C
80 const passwordMatch = await user.isPasswordMatch(password)
81 if (passwordMatch === false) return null
26d7d31b 82
e6921918
C
83 if (user.blocked) throw new AccessDeniedError('User is blocked.')
84
d9eaee39
JM
85 if (CONFIG.SIGNUP.REQUIRES_EMAIL_VERIFICATION && user.emailVerified === false) {
86 throw new AccessDeniedError('User email is not verified.')
87 }
88
f5028693 89 return user
2f372a86
C
90}
91
f5028693 92async function revokeToken (tokenInfo: TokenInfo) {
3fd3ab2d 93 const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)
bfcef50d 94 if (token) {
f201a749
C
95 clearCacheByToken(token.accessToken)
96
bfcef50d
C
97 token.destroy()
98 .catch(err => logger.error('Cannot destroy token when revoking token.', { err }))
99 }
f5028693
C
100
101 /*
102 * Thanks to https://github.com/manjeshpv/node-oauth2-server-implementation/blob/master/components/oauth/mongo-models.js
103 * "As per the discussion we need set older date
104 * revokeToken will expected return a boolean in future version
105 * https://github.com/oauthjs/node-oauth2-server/pull/274
106 * https://github.com/oauthjs/node-oauth2-server/issues/290"
107 */
108 const expiredToken = token
109 expiredToken.refreshTokenExpiresAt = new Date('2015-05-28T06:59:53.000Z')
110
111 return expiredToken
69b0a27c
C
112}
113
3fd3ab2d 114async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {
32bb4156 115 logger.debug('Saving token ' + token.accessToken + ' for client ' + client.id + ' and user ' + user.id + '.')
69b0a27c 116
feb4bdfd 117 const tokenToCreate = {
69b0a27c 118 accessToken: token.accessToken,
2f372a86 119 accessTokenExpiresAt: token.accessTokenExpiresAt,
69b0a27c 120 refreshToken: token.refreshToken,
2f372a86 121 refreshTokenExpiresAt: token.refreshTokenExpiresAt,
feb4bdfd
C
122 oAuthClientId: client.id,
123 userId: user.id
124 }
69b0a27c 125
3fd3ab2d 126 const tokenCreated = await OAuthTokenModel.create(tokenToCreate)
e6921918 127 return Object.assign(tokenCreated, { client, user })
69b0a27c
C
128}
129
130// ---------------------------------------------------------------------------
131
65fcc311
C
132// See https://github.com/oauthjs/node-oauth2-server/wiki/Model-specification for the model specifications
133export {
f201a749
C
134 deleteUserToken,
135 clearCacheByUserId,
136 clearCacheByToken,
65fcc311
C
137 getAccessToken,
138 getClient,
139 getRefreshToken,
140 getUser,
141 revokeToken,
142 saveToken
143}