1
0
Fork 0
mirror of https://github.com/Chocobozzz/PeerTube.git synced 2025-10-05 10:49:28 +02:00

Allow auth plugins to redirect to external url (#7179)

* Allow auth plugins to redirect to external url

Add a new optional field to `RegisterServerExternalAuthenticatedResult`,
the object passed to the `userAuthenticated` callback used by auth plugins.

The server code uses this to redirect to an external website if it is set.

Left TODO:

- This code has been tested manually but a test case is still missing.
- Here or in the plugin, the redirect urls must be limited to values configurable by admins.

* rename to URI for consistency

* add test for the new parameter

* address review comments

- correct syntax for optional parameter
- handle the case where `externalAuthToken` has query parameters included
This commit is contained in:
Jakob Meier 2025-08-07 14:59:19 +02:00 committed by GitHub
parent 8c9b4abe45
commit fc986076c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 5 deletions

View file

@ -39,6 +39,24 @@ async function register ({
} }
}) })
} }
const result = registerExternalAuth({
authName: 'external-auth-9',
authDisplayName: () => 'External Auth 9',
onAuthRequest: (req, res) => {
result.userAuthenticated({
req,
res,
username: 'cid',
email: 'cid@example.com',
displayName: 'Cid Marquez',
externalRedirectUri: 'https://external.com/some/redirect/path'
})
},
onLogout: (user, req) => {
return 'https://example.com/redirectUrl'
}
})
} }
async function unregister () { async function unregister () {

View file

@ -77,7 +77,7 @@ describe('Test external auth plugins', function () {
const config = await server.config.getConfig() const config = await server.config.getConfig()
const auths = config.plugin.registeredExternalAuths const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(9) expect(auths).to.have.lengthOf(10)
const auth2 = auths.find((a) => a.authName === 'external-auth-2') const auth2 = auths.find((a) => a.authName === 'external-auth-2')
expect(auth2).to.exist expect(auth2).to.exist
@ -319,7 +319,7 @@ describe('Test external auth plugins', function () {
const config = await server.config.getConfig() const config = await server.config.getConfig()
const auths = config.plugin.registeredExternalAuths const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(8) expect(auths).to.have.lengthOf(9)
const auth1 = auths.find(a => a.authName === 'external-auth-2') const auth1 = auths.find(a => a.authName === 'external-auth-2')
expect(auth1).to.not.exist expect(auth1).to.not.exist
@ -400,7 +400,7 @@ describe('Test external auth plugins', function () {
const config = await server.config.getConfig() const config = await server.config.getConfig()
const auths = config.plugin.registeredExternalAuths const auths = config.plugin.registeredExternalAuths
expect(auths).to.have.lengthOf(7) expect(auths).to.have.lengthOf(8)
const auth2 = auths.find((a) => a.authName === 'external-auth-2') const auth2 = auths.find((a) => a.authName === 'external-auth-2')
expect(auth2).to.not.exist expect(auth2).to.not.exist
@ -433,4 +433,29 @@ describe('Test external auth plugins', function () {
const { redirectUrl } = await server.login.logout({ token: resLogin.access_token }) const { redirectUrl } = await server.login.logout({ token: resLogin.access_token })
expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token) expect(redirectUrl).to.equal('https://example.com/redirectUrl?access_token=' + resLogin.access_token)
}) })
it('Should redirect to an external site after login if externalRedirectUri is set', async function () {
const res = await server.plugins.getExternalAuth({
npmName: 'test-external-auth-three',
npmVersion: '0.0.1',
authName: 'external-auth-9',
query: {
username: 'cid'
},
expectedStatus: HttpStatusCode.FOUND_302
})
const location = res.header.location
expect(location.startsWith('https://external.com/some/redirect/path?')).to.be.true
const searchParams = decodeQueryString(location)
expect(searchParams.externalAuthToken).to.exist
expect(searchParams.username).to.equal('cid')
externalAuthToken = searchParams.externalAuthToken as string
await server.login.loginUsingExternalToken({ username: 'cid', externalAuthToken, expectedStatus: HttpStatusCode.OK_200 })
})
}) })

View file

@ -45,7 +45,7 @@ async function onExternalUserAuthenticated (options: {
return return
} }
const { res } = authResult const { res, externalRedirectUri } = authResult
if (!isAuthResultValid(npmName, authName, authResult)) { if (!isAuthResultValid(npmName, authName, authResult)) {
res.redirect('/login?externalAuthError=true') res.redirect('/login?externalAuthError=true')
@ -76,7 +76,14 @@ async function onExternalUserAuthenticated (options: {
} }
} }
if (externalRedirectUri) {
const url = new URL(externalRedirectUri)
url.searchParams.set('externalAuthToken', bypassToken)
url.searchParams.set('username', user.username)
res.redirect(url.href)
} else {
res.redirect(`/login?externalAuthToken=${bypassToken}&username=${user.username}`) res.redirect(`/login?externalAuthToken=${bypassToken}&username=${user.username}`)
}
} }
async function getAuthNameFromRefreshGrant (refreshToken?: string) { async function getAuthNameFromRefreshGrant (refreshToken?: string) {

View file

@ -33,6 +33,8 @@ export interface RegisterServerAuthenticatedResult {
export interface RegisterServerExternalAuthenticatedResult extends RegisterServerAuthenticatedResult { export interface RegisterServerExternalAuthenticatedResult extends RegisterServerAuthenticatedResult {
req: express.Request req: express.Request
res: express.Response res: express.Response
// Redirect the user to this external URI after the external auth has been verified.
externalRedirectUri?: string
} }
interface RegisterServerAuthBase { interface RegisterServerAuthBase {