1 /* eslint-disable @typescript-eslint/no-unused-expressions,@typescript-eslint/require-await */
4 import { expect } from 'chai'
5 import { ServerConfig, User, UserRole } from '@shared/models'
13 loginUsingExternalToken,
16 setAccessTokensToServers,
20 } from '../../../shared/extra-utils'
21 import { cleanupTests, flushAndRunServer, ServerInfo, waitUntilLog } from '../../../shared/extra-utils/server/servers'
23 async function loginExternal (options: {
29 statusCodeExpected?: number
31 const res = await getExternalAuth({
32 url: options.server.url,
33 npmName: options.npmName,
35 authName: options.authName,
37 statusCodeExpected: options.statusCodeExpected || 302
40 if (res.status !== 302) return
42 const location = res.header.location
43 const { externalAuthToken } = decodeQueryString(location)
45 const resLogin = await loginUsingExternalToken(
48 externalAuthToken as string
54 describe('Test external auth plugins', function () {
55 let server: ServerInfo
57 let cyanAccessToken: string
58 let cyanRefreshToken: string
60 let kefkaAccessToken: string
61 let kefkaRefreshToken: string
63 let externalAuthToken: string
65 before(async function () {
68 server = await flushAndRunServer(1)
69 await setAccessTokensToServers([ server ])
71 for (const suffix of [ 'one', 'two' ]) {
74 accessToken: server.accessToken,
75 path: getPluginTestPath('-external-auth-' + suffix)
80 it('Should display the correct configuration', async function () {
81 const res = await getConfig(server.url)
83 const config: ServerConfig = res.body
85 const auths = config.plugin.registeredExternalAuths
86 expect(auths).to.have.lengthOf(3)
88 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
89 expect(auth2).to.exist
90 expect(auth2.authDisplayName).to.equal('External Auth 2')
91 expect(auth2.npmName).to.equal('peertube-plugin-test-external-auth-one')
94 it('Should redirect for a Cyan login', async function () {
95 const res = await getExternalAuth({
97 npmName: 'test-external-auth-one',
99 authName: 'external-auth-1',
103 statusCodeExpected: 302
106 const location = res.header.location
107 expect(location.startsWith('/login?')).to.be.true
109 const searchParams = decodeQueryString(location)
111 expect(searchParams.externalAuthToken).to.exist
112 expect(searchParams.username).to.equal('cyan')
114 externalAuthToken = searchParams.externalAuthToken as string
117 it('Should reject auto external login with a missing or invalid token', async function () {
118 await loginUsingExternalToken(server, 'cyan', '', 400)
119 await loginUsingExternalToken(server, 'cyan', 'blabla', 400)
122 it('Should reject auto external login with a missing or invalid username', async function () {
123 await loginUsingExternalToken(server, '', externalAuthToken, 400)
124 await loginUsingExternalToken(server, '', externalAuthToken, 400)
127 it('Should reject auto external login with an expired token', async function () {
132 await loginUsingExternalToken(server, 'cyan', externalAuthToken, 400)
134 await waitUntilLog(server, 'expired external auth token')
137 it('Should auto login Cyan, create the user and use the token', async function () {
139 const res = await loginExternal({
141 npmName: 'test-external-auth-one',
142 authName: 'external-auth-1',
149 cyanAccessToken = res.access_token
150 cyanRefreshToken = res.refresh_token
154 const res = await getMyUserInformation(server.url, cyanAccessToken)
156 const body: User = res.body
157 expect(body.username).to.equal('cyan')
158 expect(body.account.displayName).to.equal('cyan')
159 expect(body.email).to.equal('cyan@example.com')
160 expect(body.role).to.equal(UserRole.USER)
164 it('Should auto login Kefka, create the user and use the token', async function () {
166 const res = await loginExternal({
168 npmName: 'test-external-auth-one',
169 authName: 'external-auth-2',
173 kefkaAccessToken = res.access_token
174 kefkaRefreshToken = res.refresh_token
178 const res = await getMyUserInformation(server.url, kefkaAccessToken)
180 const body: User = res.body
181 expect(body.username).to.equal('kefka')
182 expect(body.account.displayName).to.equal('Kefka Palazzo')
183 expect(body.email).to.equal('kefka@example.com')
184 expect(body.role).to.equal(UserRole.ADMINISTRATOR)
188 it('Should refresh Cyan token, but not Kefka token', async function () {
190 const resRefresh = await refreshToken(server, cyanRefreshToken)
191 cyanAccessToken = resRefresh.body.access_token
192 cyanRefreshToken = resRefresh.body.refresh_token
194 const res = await getMyUserInformation(server.url, cyanAccessToken)
195 const user: User = res.body
196 expect(user.username).to.equal('cyan')
200 await refreshToken(server, kefkaRefreshToken, 400)
204 it('Should update Cyan profile', async function () {
207 accessToken: cyanAccessToken,
208 displayName: 'Cyan Garamonde',
209 description: 'Retainer to the king of Doma'
212 const res = await getMyUserInformation(server.url, cyanAccessToken)
214 const body: User = res.body
215 expect(body.account.displayName).to.equal('Cyan Garamonde')
216 expect(body.account.description).to.equal('Retainer to the king of Doma')
219 it('Should logout Cyan', async function () {
220 await logout(server.url, cyanAccessToken)
223 it('Should have logged out Cyan', async function () {
224 await waitUntilLog(server, 'On logout cyan')
226 await getMyUserInformation(server.url, cyanAccessToken, 401)
229 it('Should login Cyan and keep the old existing profile', async function () {
231 const res = await loginExternal({
233 npmName: 'test-external-auth-one',
234 authName: 'external-auth-1',
241 cyanAccessToken = res.access_token
244 const res = await getMyUserInformation(server.url, cyanAccessToken)
246 const body: User = res.body
247 expect(body.username).to.equal('cyan')
248 expect(body.account.displayName).to.equal('Cyan Garamonde')
249 expect(body.account.description).to.equal('Retainer to the king of Doma')
250 expect(body.role).to.equal(UserRole.USER)
253 it('Should reject token of Kefka by the plugin hook', async function () {
258 await getMyUserInformation(server.url, kefkaAccessToken, 401)
261 it('Should uninstall the plugin one and do not login Cyan', async function () {
262 await uninstallPlugin({
264 accessToken: server.accessToken,
265 npmName: 'peertube-plugin-test-external-auth-one'
268 await loginExternal({
270 npmName: 'test-external-auth-one',
271 authName: 'external-auth-1',
276 statusCodeExpected: 404
280 it('Should display the correct configuration', async function () {
281 const res = await getConfig(server.url)
283 const config: ServerConfig = res.body
285 const auths = config.plugin.registeredExternalAuths
286 expect(auths).to.have.lengthOf(1)
288 const auth2 = auths.find((a) => a.authName === 'external-auth-2')
289 expect(auth2).to.not.exist
292 after(async function () {
293 await cleanupTests([ server ])