]>
Commit | Line | Data |
---|---|---|
7fed6375 C |
1 | import * as express from 'express' |
2 | import { OAUTH_LIFETIME } from '@server/initializers/constants' | |
3 | import * as OAuthServer from 'express-oauth-server' | |
4 | import { PluginManager } from '@server/lib/plugins/plugin-manager' | |
5 | import { RegisterServerAuthPassOptions } from '@shared/models/plugins/register-server-auth.model' | |
6 | import { logger } from '@server/helpers/logger' | |
7 | import { UserRole } from '@shared/models' | |
e1c55031 | 8 | import { revokeToken } from '@server/lib/oauth-model' |
e307e4fc | 9 | import { OAuthTokenModel } from '@server/models/oauth/oauth-token' |
7fed6375 C |
10 | |
11 | const oAuthServer = new OAuthServer({ | |
12 | useErrorHandler: true, | |
13 | accessTokenLifetime: OAUTH_LIFETIME.ACCESS_TOKEN, | |
14 | refreshTokenLifetime: OAUTH_LIFETIME.REFRESH_TOKEN, | |
15 | continueMiddleware: true, | |
16 | model: require('./oauth-model') | |
17 | }) | |
18 | ||
19 | function onExternalAuthPlugin (npmName: string, username: string, email: string) { | |
20 | ||
21 | } | |
22 | ||
23 | async function handleIdAndPassLogin (req: express.Request, res: express.Response, next: express.NextFunction) { | |
e307e4fc C |
24 | const grantType = req.body.grant_type |
25 | ||
26 | if (grantType === 'password') await proxifyPasswordGrant(req, res) | |
27 | else if (grantType === 'refresh_token') await proxifyRefreshGrant(req, res) | |
28 | ||
29 | return forwardTokenReq(req, res, next) | |
30 | } | |
31 | ||
32 | async function handleTokenRevocation (req: express.Request, res: express.Response) { | |
33 | const token = res.locals.oauth.token | |
34 | ||
35 | res.locals.explicitLogout = true | |
36 | await revokeToken(token) | |
37 | ||
38 | // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released | |
39 | // oAuthServer.revoke(req, res, err => { | |
40 | // if (err) { | |
41 | // logger.warn('Error in revoke token handler.', { err }) | |
42 | // | |
43 | // return res.status(err.status) | |
44 | // .json({ | |
45 | // error: err.message, | |
46 | // code: err.name | |
47 | // }) | |
48 | // .end() | |
49 | // } | |
50 | // }) | |
51 | ||
52 | return res.sendStatus(200) | |
53 | } | |
54 | ||
55 | // --------------------------------------------------------------------------- | |
56 | ||
57 | export { | |
58 | oAuthServer, | |
59 | handleIdAndPassLogin, | |
60 | onExternalAuthPlugin, | |
61 | handleTokenRevocation | |
62 | } | |
63 | ||
64 | // --------------------------------------------------------------------------- | |
65 | ||
66 | function forwardTokenReq (req: express.Request, res: express.Response, next: express.NextFunction) { | |
67 | return oAuthServer.token()(req, res, err => { | |
68 | if (err) { | |
69 | logger.warn('Login error.', { err }) | |
70 | ||
71 | return res.status(err.status) | |
72 | .json({ | |
73 | error: err.message, | |
74 | code: err.name | |
75 | }) | |
76 | .end() | |
77 | } | |
78 | ||
79 | return next() | |
80 | }) | |
81 | } | |
82 | ||
83 | async function proxifyRefreshGrant (req: express.Request, res: express.Response) { | |
84 | const refreshToken = req.body.refresh_token | |
85 | if (!refreshToken) return | |
86 | ||
87 | const tokenModel = await OAuthTokenModel.loadByRefreshToken(refreshToken) | |
88 | if (tokenModel?.authName) res.locals.refreshTokenAuthName = tokenModel.authName | |
89 | } | |
90 | ||
91 | async function proxifyPasswordGrant (req: express.Request, res: express.Response) { | |
7fed6375 C |
92 | const plugins = PluginManager.Instance.getIdAndPassAuths() |
93 | const pluginAuths: { npmName?: string, registerAuthOptions: RegisterServerAuthPassOptions }[] = [] | |
94 | ||
95 | for (const plugin of plugins) { | |
96 | const auths = plugin.idAndPassAuths | |
97 | ||
98 | for (const auth of auths) { | |
99 | pluginAuths.push({ | |
100 | npmName: plugin.npmName, | |
101 | registerAuthOptions: auth | |
102 | }) | |
103 | } | |
104 | } | |
105 | ||
106 | pluginAuths.sort((a, b) => { | |
107 | const aWeight = a.registerAuthOptions.getWeight() | |
108 | const bWeight = b.registerAuthOptions.getWeight() | |
109 | ||
e1c55031 | 110 | // DESC weight order |
7fed6375 | 111 | if (aWeight === bWeight) return 0 |
e1c55031 | 112 | if (aWeight < bWeight) return 1 |
7fed6375 C |
113 | return -1 |
114 | }) | |
115 | ||
116 | const loginOptions = { | |
117 | id: req.body.username, | |
118 | password: req.body.password | |
119 | } | |
120 | ||
121 | for (const pluginAuth of pluginAuths) { | |
e1c55031 C |
122 | const authOptions = pluginAuth.registerAuthOptions |
123 | ||
7fed6375 | 124 | logger.debug( |
e1c55031 C |
125 | 'Using auth method %s of plugin %s to login %s with weight %d.', |
126 | authOptions.authName, pluginAuth.npmName, loginOptions.id, authOptions.getWeight() | |
7fed6375 C |
127 | ) |
128 | ||
e1c55031 | 129 | const loginResult = await authOptions.login(loginOptions) |
7fed6375 | 130 | if (loginResult) { |
e1c55031 C |
131 | logger.info( |
132 | 'Login success with auth method %s of plugin %s for %s.', | |
133 | authOptions.authName, pluginAuth.npmName, loginOptions.id | |
134 | ) | |
7fed6375 C |
135 | |
136 | res.locals.bypassLogin = { | |
137 | bypass: true, | |
138 | pluginName: pluginAuth.npmName, | |
e1c55031 | 139 | authName: authOptions.authName, |
7fed6375 C |
140 | user: { |
141 | username: loginResult.username, | |
142 | email: loginResult.email, | |
143 | role: loginResult.role || UserRole.USER, | |
144 | displayName: loginResult.displayName || loginResult.username | |
145 | } | |
146 | } | |
147 | ||
e307e4fc | 148 | return |
7fed6375 C |
149 | } |
150 | } | |
7fed6375 | 151 | } |