// @flow import Logger from '@jitsi/logger'; import { openConnection } from '../../../connection'; import { openAuthDialog, openLoginDialog } from '../../../react/features/authentication/actions.web'; import { LoginDialog, WaitForOwnerDialog } from '../../../react/features/authentication/components'; import { getTokenAuthUrl, isTokenAuthEnabled } from '../../../react/features/authentication/functions'; import { getReplaceParticipant } from '../../../react/features/base/config/functions'; import { isDialogOpen } from '../../../react/features/base/dialog'; import { setJWT } from '../../../react/features/base/jwt'; import UIUtil from '../util/UIUtil'; import ExternalLoginDialog from './LoginDialog'; let externalAuthWindow; declare var APP: Object; const logger = Logger.getLogger(__filename); /** * Authenticate using external service or just focus * external auth window if there is one already. * * @param {JitsiConference} room * @param {string} [lockPassword] password to use if the conference is locked */ function doExternalAuth(room, lockPassword) { const config = APP.store.getState()['features/base/config']; if (externalAuthWindow) { externalAuthWindow.focus(); return; } if (room.isJoined()) { let getUrl; if (isTokenAuthEnabled(config)) { getUrl = Promise.resolve(getTokenAuthUrl(config)(room.getName(), true)); initJWTTokenListener(room); } else { getUrl = room.getExternalAuthUrl(true); } getUrl.then(url => { externalAuthWindow = ExternalLoginDialog.showExternalAuthDialog( url, () => { externalAuthWindow = null; if (!isTokenAuthEnabled(config)) { room.join(lockPassword); } } ); }); } else if (isTokenAuthEnabled(config)) { redirectToTokenAuthService(room.getName()); } else { room.getExternalAuthUrl().then(UIUtil.redirect); } } /** * Redirect the user to the token authentication service for the login to be * performed. Once complete it is expected that the service will bring the user * back with "?jwt={the JWT token}" query parameter added. * @param {string} [roomName] the name of the conference room. */ export function redirectToTokenAuthService(roomName: string) { const config = APP.store.getState()['features/base/config']; // FIXME: This method will not preserve the other URL params that were // originally passed. UIUtil.redirect(getTokenAuthUrl(config)(roomName, false)); } /** * Initializes 'message' listener that will wait for a JWT token to be received * from the token authentication service opened in a popup window. * @param room the name of the conference room. */ function initJWTTokenListener(room) { /** * */ function listener({ data, source }) { if (externalAuthWindow !== source) { logger.warn('Ignored message not coming ' + 'from external authnetication window'); return; } let jwt; if (data && (jwt = data.jwtToken)) { logger.info('Received JSON Web Token (JWT):', jwt); APP.store.dispatch(setJWT(jwt)); const roomName = room.getName(); openConnection({ retry: false, roomName }).then(connection => { // Start new connection const newRoom = connection.initJitsiConference( roomName, APP.conference._getConferenceOptions()); // Authenticate from the new connection to get // the session-ID from the focus, which will then be used // to upgrade current connection's user role newRoom.room.moderator.authenticate() .then(() => { connection.disconnect(); // At this point we'll have session-ID stored in // the settings. It will be used in the call below // to upgrade user's role room.room.moderator.authenticate() .then(() => { logger.info('User role upgrade done !'); // eslint-disable-line no-use-before-define unregister(); }) .catch((err, errCode) => { logger.error('Authentication failed: ', err, errCode); unregister(); }); }) .catch((error, code) => { unregister(); connection.disconnect(); logger.error( 'Authentication failed on the new connection', error, code); }); }, err => { unregister(); logger.error('Failed to open new connection', err); }); } } /** * */ function unregister() { window.removeEventListener('message', listener); } if (window.addEventListener) { window.addEventListener('message', listener, false); } } /** * Authenticate for the conference. * Uses external service for auth if conference supports that. * @param {JitsiConference} room * @param {string} [lockPassword] password to use if the conference is locked */ function authenticate(room: Object, lockPassword: string) { const config = APP.store.getState()['features/base/config']; if (isTokenAuthEnabled(config) || room.isExternalAuthEnabled()) { doExternalAuth(room, lockPassword); } else { APP.store.dispatch(openLoginDialog()); } } /** * Notify user that authentication is required to create the conference. * @param {JitsiConference} room * @param {string} [lockPassword] password to use if the conference is locked */ function requireAuth(room: Object, lockPassword: string) { if (isDialogOpen(APP.store, WaitForOwnerDialog) || isDialogOpen(APP.store, LoginDialog)) { return; } APP.store.dispatch( openAuthDialog( room.getName(), authenticate.bind(null, room, lockPassword)) ); } /** * De-authenticate local user. * * @param {JitsiConference} room * @param {string} [lockPassword] password to use if the conference is locked * @returns {Promise} */ function logout(room: Object) { return new Promise(resolve => { room.room.moderator.logout(resolve); }).then(url => { // de-authenticate conference on the fly if (room.isJoined()) { const replaceParticipant = getReplaceParticipant(APP.store.getState()); room.join(null, replaceParticipant); } return url; }); } export default { authenticate, logout, requireAuth };