aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--client/src/app/core/auth/auth.service.ts8
-rw-r--r--server/lib/auth.ts4
-rw-r--r--server/lib/oauth-model.ts10
-rw-r--r--server/lib/plugins/plugin-manager.ts12
-rw-r--r--server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js53
-rw-r--r--server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json20
-rw-r--r--server/tests/plugins/external-auth.ts34
-rw-r--r--server/types/plugins/register-server-auth.model.ts3
8 files changed, 129 insertions, 15 deletions
diff --git a/client/src/app/core/auth/auth.service.ts b/client/src/app/core/auth/auth.service.ts
index 3410c5a6f..fd6062d3f 100644
--- a/client/src/app/core/auth/auth.service.ts
+++ b/client/src/app/core/auth/auth.service.ts
@@ -167,9 +167,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
167 const authHeaderValue = this.getRequestHeaderValue() 167 const authHeaderValue = this.getRequestHeaderValue()
168 const headers = new HttpHeaders().set('Authorization', authHeaderValue) 168 const headers = new HttpHeaders().set('Authorization', authHeaderValue)
169 169
170 this.http.post<void>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers }) 170 this.http.post<{ redirectUrl?: string }>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers })
171 .subscribe( 171 .subscribe(
172 () => { /* nothing to do */ }, 172 res => {
173 if (res.redirectUrl) {
174 window.location.href = res.redirectUrl
175 }
176 },
173 177
174 err => console.error(err) 178 err => console.error(err)
175 ) 179 )
diff --git a/server/lib/auth.ts b/server/lib/auth.ts
index 3f8e18633..acf0da18a 100644
--- a/server/lib/auth.ts
+++ b/server/lib/auth.ts
@@ -52,7 +52,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons
52 const token = res.locals.oauth.token 52 const token = res.locals.oauth.token
53 53
54 res.locals.explicitLogout = true 54 res.locals.explicitLogout = true
55 await revokeToken(token) 55 const result = await revokeToken(token)
56 56
57 // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released 57 // FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released
58 // oAuthServer.revoke(req, res, err => { 58 // oAuthServer.revoke(req, res, err => {
@@ -68,7 +68,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons
68 // } 68 // }
69 // }) 69 // })
70 70
71 return res.json() 71 return res.json(result)
72} 72}
73 73
74async function onExternalUserAuthenticated (options: { 74async function onExternalUserAuthenticated (options: {
diff --git a/server/lib/oauth-model.ts b/server/lib/oauth-model.ts
index 3273c6c2d..f7ea98b41 100644
--- a/server/lib/oauth-model.ts
+++ b/server/lib/oauth-model.ts
@@ -141,13 +141,15 @@ async function getUser (usernameOrEmail?: string, password?: string) {
141 return user 141 return user
142} 142}
143 143
144async function revokeToken (tokenInfo: { refreshToken: string }) { 144async function revokeToken (tokenInfo: { refreshToken: string }): Promise<{ success: boolean, redirectUrl?: string }> {
145 const res: express.Response = this.request.res 145 const res: express.Response = this.request.res
146 const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken) 146 const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)
147 147
148 if (token) { 148 if (token) {
149 let redirectUrl: string
150
149 if (res.locals.explicitLogout === true && token.User.pluginAuth && token.authName) { 151 if (res.locals.explicitLogout === true && token.User.pluginAuth && token.authName) {
150 PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User) 152 redirectUrl = await PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User, this.request)
151 } 153 }
152 154
153 clearCacheByToken(token.accessToken) 155 clearCacheByToken(token.accessToken)
@@ -155,10 +157,10 @@ async function revokeToken (tokenInfo: { refreshToken: string }) {
155 token.destroy() 157 token.destroy()
156 .catch(err => logger.error('Cannot destroy token when revoking token.', { err })) 158 .catch(err => logger.error('Cannot destroy token when revoking token.', { err }))
157 159
158 return true 160 return { success: true, redirectUrl }
159 } 161 }
160 162
161 return false 163 return { success: false }
162} 164}
163 165
164async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) { 166async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {
diff --git a/server/lib/plugins/plugin-manager.ts b/server/lib/plugins/plugin-manager.ts
index 94b5ecc41..8e7491257 100644
--- a/server/lib/plugins/plugin-manager.ts
+++ b/server/lib/plugins/plugin-manager.ts
@@ -1,3 +1,4 @@
1import * as express from 'express'
1import { createReadStream, createWriteStream } from 'fs' 2import { createReadStream, createWriteStream } from 'fs'
2import { outputFile, readJSON } from 'fs-extra' 3import { outputFile, readJSON } from 'fs-extra'
3import { basename, join } from 'path' 4import { basename, join } from 'path'
@@ -166,18 +167,25 @@ export class PluginManager implements ServerHook {
166 167
167 // ###################### External events ###################### 168 // ###################### External events ######################
168 169
169 onLogout (npmName: string, authName: string, user: MUser) { 170 async onLogout (npmName: string, authName: string, user: MUser, req: express.Request) {
170 const auth = this.getAuth(npmName, authName) 171 const auth = this.getAuth(npmName, authName)
171 172
172 if (auth?.onLogout) { 173 if (auth?.onLogout) {
173 logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName) 174 logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName)
174 175
175 try { 176 try {
176 auth.onLogout(user) 177 // Force await, in case or onLogout returns a promise
178 const result = await auth.onLogout(user, req)
179
180 return typeof result === 'string'
181 ? result
182 : undefined
177 } catch (err) { 183 } catch (err) {
178 logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err }) 184 logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err })
179 } 185 }
180 } 186 }
187
188 return undefined
181 } 189 }
182 190
183 onSettingsChanged (name: string, settings: any) { 191 onSettingsChanged (name: string, settings: any) {
diff --git a/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js b/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js
new file mode 100644
index 000000000..30cedccc6
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js
@@ -0,0 +1,53 @@
1async function register ({
2 registerExternalAuth,
3 peertubeHelpers
4}) {
5 {
6 const result = registerExternalAuth({
7 authName: 'external-auth-7',
8 authDisplayName: () => 'External Auth 7',
9 onAuthRequest: (req, res) => {
10 result.userAuthenticated({
11 req,
12 res,
13 username: 'cid',
14 email: 'cid@example.com',
15 displayName: 'Cid Marquez'
16 })
17 },
18 onLogout: (user, req) => {
19 return 'https://example.com/redirectUrl'
20 }
21 })
22 }
23
24 {
25 const result = registerExternalAuth({
26 authName: 'external-auth-8',
27 authDisplayName: () => 'External Auth 8',
28 onAuthRequest: (req, res) => {
29 result.userAuthenticated({
30 req,
31 res,
32 username: 'cid',
33 email: 'cid@example.com',
34 displayName: 'Cid Marquez'
35 })
36 },
37 onLogout: (user, req) => {
38 return 'https://example.com/redirectUrl?access_token=' + req.headers['authorization'].split(' ')[1]
39 }
40 })
41 }
42}
43
44async function unregister () {
45
46}
47
48module.exports = {
49 register,
50 unregister
51}
52
53// ###########################################################################
diff --git a/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json b/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json
new file mode 100644
index 000000000..f323d189d
--- /dev/null
+++ b/server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json
@@ -0,0 +1,20 @@
1{
2 "name": "peertube-plugin-test-external-auth-three",
3 "version": "0.0.1",
4 "description": "External auth three",
5 "engine": {
6 "peertube": ">=1.3.0"
7 },
8 "keywords": [
9 "peertube",
10 "plugin"
11 ],
12 "homepage": "https://github.com/Chocobozzz/PeerTube",
13 "author": "Chocobozzz",
14 "bugs": "https://github.com/Chocobozzz/PeerTube/issues",
15 "library": "./main.js",
16 "staticDirs": {},
17 "css": [],
18 "clientScripts": [],
19 "translations": {}
20}
diff --git a/server/tests/plugins/external-auth.ts b/server/tests/plugins/external-auth.ts
index 57361be05..6d907cc51 100644
--- a/server/tests/plugins/external-auth.ts
+++ b/server/tests/plugins/external-auth.ts
@@ -73,7 +73,7 @@ describe('Test external auth plugins', function () {
73 server = await flushAndRunServer(1) 73 server = await flushAndRunServer(1)
74 await setAccessTokensToServers([ server ]) 74 await setAccessTokensToServers([ server ])
75 75
76 for (const suffix of [ 'one', 'two' ]) { 76 for (const suffix of [ 'one', 'two', 'three' ]) {
77 await installPlugin({ 77 await installPlugin({
78 url: server.url, 78 url: server.url,
79 accessToken: server.accessToken, 79 accessToken: server.accessToken,
@@ -88,7 +88,7 @@ describe('Test external auth plugins', function () {
88 const config: ServerConfig = res.body 88 const config: ServerConfig = res.body
89 89
90 const auths = config.plugin.registeredExternalAuths 90 const auths = config.plugin.registeredExternalAuths
91 expect(auths).to.have.lengthOf(6) 91 expect(auths).to.have.lengthOf(8)
92 92
93 const auth2 = auths.find((a) => a.authName === 'external-auth-2') 93 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
94 expect(auth2).to.exist 94 expect(auth2).to.exist
@@ -301,7 +301,7 @@ describe('Test external auth plugins', function () {
301 const config: ServerConfig = res.body 301 const config: ServerConfig = res.body
302 302
303 const auths = config.plugin.registeredExternalAuths 303 const auths = config.plugin.registeredExternalAuths
304 expect(auths).to.have.lengthOf(5) 304 expect(auths).to.have.lengthOf(7)
305 305
306 const auth1 = auths.find(a => a.authName === 'external-auth-2') 306 const auth1 = auths.find(a => a.authName === 'external-auth-2')
307 expect(auth1).to.not.exist 307 expect(auth1).to.not.exist
@@ -371,7 +371,7 @@ describe('Test external auth plugins', function () {
371 const config: ServerConfig = res.body 371 const config: ServerConfig = res.body
372 372
373 const auths = config.plugin.registeredExternalAuths 373 const auths = config.plugin.registeredExternalAuths
374 expect(auths).to.have.lengthOf(4) 374 expect(auths).to.have.lengthOf(6)
375 375
376 const auth2 = auths.find((a) => a.authName === 'external-auth-2') 376 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
377 expect(auth2).to.not.exist 377 expect(auth2).to.not.exist
@@ -380,4 +380,30 @@ describe('Test external auth plugins', function () {
380 after(async function () { 380 after(async function () {
381 await cleanupTests([ server ]) 381 await cleanupTests([ server ])
382 }) 382 })
383
384 it('Should forward the redirectUrl if the plugin returns one', async function () {
385 const resLogin = await loginExternal({
386 server,
387 npmName: 'test-external-auth-three',
388 authName: 'external-auth-7',
389 username: 'cid'
390 })
391
392 const resLogout = await logout(server.url, resLogin.access_token)
393
394 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl')
395 })
396
397 it('Should call the plugin\'s onLogout method with the request', async function () {
398 const resLogin = await loginExternal({
399 server,
400 npmName: 'test-external-auth-three',
401 authName: 'external-auth-8',
402 username: 'cid'
403 })
404
405 const resLogout = await logout(server.url, resLogin.access_token)
406
407 expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
408 })
383}) 409})
diff --git a/server/types/plugins/register-server-auth.model.ts b/server/types/plugins/register-server-auth.model.ts
index 31c71b0d0..3e1a5aeba 100644
--- a/server/types/plugins/register-server-auth.model.ts
+++ b/server/types/plugins/register-server-auth.model.ts
@@ -21,7 +21,8 @@ interface RegisterServerAuthBase {
21 authName: string 21 authName: string
22 22
23 // Called by PeerTube when a user from your plugin logged out 23 // Called by PeerTube when a user from your plugin logged out
24 onLogout?(user: MUser): void 24 // Returns a redirectUrl sent to the client or nothing
25 onLogout?(user: MUser, req: express.Request): Promise<string>
25 26
26 // Your plugin can hook PeerTube access/refresh token validity 27 // Your plugin can hook PeerTube access/refresh token validity
27 // So you can control for your plugin the user session lifetime 28 // So you can control for your plugin the user session lifetime