Browse Source

Provide express request to onLogout call

+ pluginInfo related changes
pull/3342/head
Chocobozzz 1 week ago
parent
commit
74fd2643b4
8 changed files with 129 additions and 15 deletions
  1. +6
    -2
      client/src/app/core/auth/auth.service.ts
  2. +2
    -2
      server/lib/auth.ts
  3. +6
    -4
      server/lib/oauth-model.ts
  4. +10
    -2
      server/lib/plugins/plugin-manager.ts
  5. +53
    -0
      server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js
  6. +20
    -0
      server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json
  7. +30
    -4
      server/tests/plugins/external-auth.ts
  8. +2
    -1
      server/types/plugins/register-server-auth.model.ts

+ 6
- 2
client/src/app/core/auth/auth.service.ts View File

@@ -167,9 +167,13 @@ Ensure you have correctly configured PeerTube (config/ directory), in particular
const authHeaderValue = this.getRequestHeaderValue()
const headers = new HttpHeaders().set('Authorization', authHeaderValue)

this.http.post<void>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers })
this.http.post<{ redirectUrl?: string }>(AuthService.BASE_REVOKE_TOKEN_URL, {}, { headers })
.subscribe(
() => { /* nothing to do */ },
res => {
if (res.redirectUrl) {
window.location.href = res.redirectUrl
}
},

err => console.error(err)
)


+ 2
- 2
server/lib/auth.ts View File

@@ -52,7 +52,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons
const token = res.locals.oauth.token

res.locals.explicitLogout = true
await revokeToken(token)
const result = await revokeToken(token)

// FIXME: uncomment when https://github.com/oauthjs/node-oauth2-server/pull/289 is released
// oAuthServer.revoke(req, res, err => {
@@ -68,7 +68,7 @@ async function handleTokenRevocation (req: express.Request, res: express.Respons
// }
// })

return res.json()
return res.json(result)
}

async function onExternalUserAuthenticated (options: {


+ 6
- 4
server/lib/oauth-model.ts View File

@@ -141,13 +141,15 @@ async function getUser (usernameOrEmail?: string, password?: string) {
return user
}

async function revokeToken (tokenInfo: { refreshToken: string }) {
async function revokeToken (tokenInfo: { refreshToken: string }): Promise<{ success: boolean, redirectUrl?: string }> {
const res: express.Response = this.request.res
const token = await OAuthTokenModel.getByRefreshTokenAndPopulateUser(tokenInfo.refreshToken)

if (token) {
let redirectUrl: string

if (res.locals.explicitLogout === true && token.User.pluginAuth && token.authName) {
PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User)
redirectUrl = await PluginManager.Instance.onLogout(token.User.pluginAuth, token.authName, token.User, this.request)
}

clearCacheByToken(token.accessToken)
@@ -155,10 +157,10 @@ async function revokeToken (tokenInfo: { refreshToken: string }) {
token.destroy()
.catch(err => logger.error('Cannot destroy token when revoking token.', { err }))

return true
return { success: true, redirectUrl }
}

return false
return { success: false }
}

async function saveToken (token: TokenInfo, client: OAuthClientModel, user: UserModel) {


+ 10
- 2
server/lib/plugins/plugin-manager.ts View File

@@ -1,3 +1,4 @@
import * as express from 'express'
import { createReadStream, createWriteStream } from 'fs'
import { outputFile, readJSON } from 'fs-extra'
import { basename, join } from 'path'
@@ -166,18 +167,25 @@ export class PluginManager implements ServerHook {

// ###################### External events ######################

onLogout (npmName: string, authName: string, user: MUser) {
async onLogout (npmName: string, authName: string, user: MUser, req: express.Request) {
const auth = this.getAuth(npmName, authName)

if (auth?.onLogout) {
logger.info('Running onLogout function from auth %s of plugin %s', authName, npmName)

try {
auth.onLogout(user)
// Force await, in case or onLogout returns a promise
const result = await auth.onLogout(user, req)

return typeof result === 'string'
? result
: undefined
} catch (err) {
logger.warn('Cannot run onLogout function from auth %s of plugin %s.', authName, npmName, { err })
}
}

return undefined
}

onSettingsChanged (name: string, settings: any) {


+ 53
- 0
server/tests/fixtures/peertube-plugin-test-external-auth-three/main.js View File

@@ -0,0 +1,53 @@
async function register ({
registerExternalAuth,
peertubeHelpers
}) {
{
const result = registerExternalAuth({
authName: 'external-auth-7',
authDisplayName: () => 'External Auth 7',
onAuthRequest: (req, res) => {
result.userAuthenticated({
req,
res,
username: 'cid',
email: 'cid@example.com',
displayName: 'Cid Marquez'
})
},
onLogout: (user, req) => {
return 'https://example.com/redirectUrl'
}
})
}

{
const result = registerExternalAuth({
authName: 'external-auth-8',
authDisplayName: () => 'External Auth 8',
onAuthRequest: (req, res) => {
result.userAuthenticated({
req,
res,
username: 'cid',
email: 'cid@example.com',
displayName: 'Cid Marquez'
})
},
onLogout: (user, req) => {
return 'https://example.com/redirectUrl?access_token=' + req.headers['authorization'].split(' ')[1]
}
})
}
}

async function unregister () {

}

module.exports = {
register,
unregister
}

// ###########################################################################

+ 20
- 0
server/tests/fixtures/peertube-plugin-test-external-auth-three/package.json View File

@@ -0,0 +1,20 @@
{
"name": "peertube-plugin-test-external-auth-three",
"version": "0.0.1",
"description": "External auth three",
"engine": {
"peertube": ">=1.3.0"
},
"keywords": [
"peertube",
"plugin"
],
"homepage": "https://github.com/Chocobozzz/PeerTube",
"author": "Chocobozzz",
"bugs": "https://github.com/Chocobozzz/PeerTube/issues",
"library": "./main.js",
"staticDirs": {},
"css": [],
"clientScripts": [],
"translations": {}
}

+ 30
- 4
server/tests/plugins/external-auth.ts View File

@@ -73,7 +73,7 @@ describe('Test external auth plugins', function () {
server = await flushAndRunServer(1)
await setAccessTokensToServers([ server ])

for (const suffix of [ 'one', 'two' ]) {
for (const suffix of [ 'one', 'two', 'three' ]) {
await installPlugin({
url: server.url,
accessToken: server.accessToken,
@@ -88,7 +88,7 @@ describe('Test external auth plugins', function () {
const config: ServerConfig = res.body

const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(6)
expect(auths).to.have.lengthOf(8)

const auth2 = auths.find((a) => a.authName === 'external-auth-2')
expect(auth2).to.exist
@@ -301,7 +301,7 @@ describe('Test external auth plugins', function () {
const config: ServerConfig = res.body

const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(5)
expect(auths).to.have.lengthOf(7)

const auth1 = auths.find(a => a.authName === 'external-auth-2')
expect(auth1).to.not.exist
@@ -371,7 +371,7 @@ describe('Test external auth plugins', function () {
const config: ServerConfig = res.body

const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(4)
expect(auths).to.have.lengthOf(6)

const auth2 = auths.find((a) => a.authName === 'external-auth-2')
expect(auth2).to.not.exist
@@ -380,4 +380,30 @@ describe('Test external auth plugins', function () {
after(async function () {
await cleanupTests([ server ])
})

it('Should forward the redirectUrl if the plugin returns one', async function () {
const resLogin = await loginExternal({
server,
npmName: 'test-external-auth-three',
authName: 'external-auth-7',
username: 'cid'
})

const resLogout = await logout(server.url, resLogin.access_token)

expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl')
})

it('Should call the plugin\'s onLogout method with the request', async function () {
const resLogin = await loginExternal({
server,
npmName: 'test-external-auth-three',
authName: 'external-auth-8',
username: 'cid'
})

const resLogout = await logout(server.url, resLogin.access_token)

expect(resLogout.body.redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
})
})

+ 2
- 1
server/types/plugins/register-server-auth.model.ts View File

@@ -21,7 +21,8 @@ interface RegisterServerAuthBase {
authName: string

// Called by PeerTube when a user from your plugin logged out
onLogout?(user: MUser): void
// Returns a redirectUrl sent to the client or nothing
onLogout?(user: MUser, req: express.Request): Promise<string>

// Your plugin can hook PeerTube access/refresh token validity
// So you can control for your plugin the user session lifetime


Loading…
Cancel
Save