1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
import express from 'express'
import { logger } from '@server/helpers/logger'
import { CONFIG } from '@server/initializers/config'
import { OTP } from '@server/initializers/constants'
import { getAuthNameFromRefreshGrant, getBypassFromExternalAuth, getBypassFromPasswordGrant } from '@server/lib/auth/external-auth'
import { handleOAuthToken, MissingTwoFactorError } from '@server/lib/auth/oauth'
import { BypassLogin, revokeToken } from '@server/lib/auth/oauth-model'
import { Hooks } from '@server/lib/plugins/hooks'
import { asyncMiddleware, authenticate, buildRateLimiter, openapiOperationDoc } from '@server/middlewares'
import { buildUUID } from '@shared/extra-utils'
import { ScopedToken } from '@shared/models/users/user-scoped-token'
const tokensRouter = express.Router()
const loginRateLimiter = buildRateLimiter({
windowMs: CONFIG.RATES_LIMIT.LOGIN.WINDOW_MS,
max: CONFIG.RATES_LIMIT.LOGIN.MAX
})
tokensRouter.post('/token',
loginRateLimiter,
openapiOperationDoc({ operationId: 'getOAuthToken' }),
asyncMiddleware(handleToken)
)
tokensRouter.post('/revoke-token',
openapiOperationDoc({ operationId: 'revokeOAuthToken' }),
authenticate,
asyncMiddleware(handleTokenRevocation)
)
tokensRouter.get('/scoped-tokens',
authenticate,
getScopedTokens
)
tokensRouter.post('/scoped-tokens',
authenticate,
asyncMiddleware(renewScopedTokens)
)
// ---------------------------------------------------------------------------
export {
tokensRouter
}
// ---------------------------------------------------------------------------
async function handleToken (req: express.Request, res: express.Response, next: express.NextFunction) {
const grantType = req.body.grant_type
try {
const bypassLogin = await buildByPassLogin(req, grantType)
const refreshTokenAuthName = grantType === 'refresh_token'
? await getAuthNameFromRefreshGrant(req.body.refresh_token)
: undefined
const options = {
refreshTokenAuthName,
bypassLogin
}
const token = await handleOAuthToken(req, options)
res.set('Cache-Control', 'no-store')
res.set('Pragma', 'no-cache')
Hooks.runAction('action:api.user.oauth2-got-token', { username: token.user.username, ip: req.ip, req, res })
return res.json({
token_type: 'Bearer',
access_token: token.accessToken,
refresh_token: token.refreshToken,
expires_in: token.accessTokenExpiresIn,
refresh_token_expires_in: token.refreshTokenExpiresIn
})
} catch (err) {
logger.warn('Login error', { err })
if (err instanceof MissingTwoFactorError) {
res.set(OTP.HEADER_NAME, OTP.HEADER_REQUIRED_VALUE)
}
return res.fail({
status: err.code,
message: err.message,
type: err.name
})
}
}
async function handleTokenRevocation (req: express.Request, res: express.Response) {
const token = res.locals.oauth.token
const result = await revokeToken(token, { req, explicitLogout: true })
return res.json(result)
}
function getScopedTokens (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.user
return res.json({
feedToken: user.feedToken
} as ScopedToken)
}
async function renewScopedTokens (req: express.Request, res: express.Response) {
const user = res.locals.oauth.token.user
user.feedToken = buildUUID()
await user.save()
return res.json({
feedToken: user.feedToken
} as ScopedToken)
}
async function buildByPassLogin (req: express.Request, grantType: string): Promise<BypassLogin> {
if (grantType !== 'password') return undefined
if (req.body.externalAuthToken) {
// Consistency with the getBypassFromPasswordGrant promise
return getBypassFromExternalAuth(req.body.username, req.body.externalAuthToken)
}
return getBypassFromPasswordGrant(req.body.username, req.body.password)
}
|