diff --git a/connection.js b/connection.js index 5add17e48..94f73e5e4 100644 --- a/connection.js +++ b/connection.js @@ -1,6 +1,5 @@ /* global APP, JitsiMeetJS, config */ -//FIXME: -import LoginDialog from './modules/UI/authentication/LoginDialog'; +import AuthHandler from './modules/UI/authentication/AuthHandler'; const ConnectionEvents = JitsiMeetJS.events.connection; const ConnectionErrors = JitsiMeetJS.errors.connection; @@ -92,33 +91,6 @@ function connect(id, password, roomName) { }); } -/** - * Show Authentication Dialog and try to connect with new credentials. - * If failed to connect because of PASSWORD_REQUIRED error - * then ask for password again. - * @param {string} [roomName] - * @returns {Promise} - */ -function requestAuth(roomName) { - return new Promise(function (resolve, reject) { - let authDialog = LoginDialog.showAuthDialog( - function (id, password) { - connect(id, password, roomName).then(function (connection) { - authDialog.close(); - resolve(connection); - }, function (err) { - if (err === ConnectionErrors.PASSWORD_REQUIRED) { - authDialog.displayError(err); - } else { - authDialog.close(); - reject(err); - } - }); - } - ); - }); -} - /** * Open JitsiConnection using provided credentials. * If retry option is true it will show auth dialog on PASSWORD_REQUIRED error. @@ -157,7 +129,7 @@ export function openConnection({id, password, retry, roomName}) { if (config.token) { throw err; } else { - return requestAuth(roomName); + return AuthHandler.requestAuth(roomName, connect); } } else { throw err; diff --git a/modules/UI/authentication/AuthHandler.js b/modules/UI/authentication/AuthHandler.js index fc321af3a..5c9288346 100644 --- a/modules/UI/authentication/AuthHandler.js +++ b/modules/UI/authentication/AuthHandler.js @@ -1,15 +1,20 @@ -/* global JitsiMeetJS, APP */ +/* global APP, config, JitsiMeetJS, Promise */ import LoginDialog from './LoginDialog'; -import UIEvents from '../../../service/UI/UIEvents'; import UIUtil from '../util/UIUtil'; import {openConnection} from '../../../connection'; const ConferenceEvents = JitsiMeetJS.events.conference; +const ConnectionErrors = JitsiMeetJS.errors.connection; let externalAuthWindow; let authRequiredDialog; +let isTokenAuthEnabled + = typeof config.tokenAuthUrl === "string" && config.tokenAuthUrl.length; +let getTokenAuthUrl + = JitsiMeetJS.util.AuthUtil.getTokenAuthUrl.bind(null, config.tokenAuthUrl); + /** * Authenticate using external service or just focus * external auth window if there is one already. @@ -23,19 +28,103 @@ function doExternalAuth (room, lockPassword) { return; } if (room.isJoined()) { - room.getExternalAuthUrl(true).then(function (url) { + let getUrl; + if (isTokenAuthEnabled) { + getUrl = Promise.resolve(getTokenAuthUrl(room.getName(), true)); + initJWTTokenListener(room); + } else { + getUrl = room.getExternalAuthUrl(true); + } + getUrl.then(function (url) { externalAuthWindow = LoginDialog.showExternalAuthDialog( url, function () { externalAuthWindow = null; - room.join(lockPassword); + if (!isTokenAuthEnabled) { + room.join(lockPassword); + } } ); }); } else { // If conference has not been started yet // then redirect to login page - room.getExternalAuthUrl().then(UIUtil.redirect); + if (isTokenAuthEnabled) { + 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 wil bring the user + * back with "?jwt={the JWT token}" query parameter added. + * @param {string} [roomName] the name of the conference room. + */ +function redirectToTokenAuthService(roomName) { + UIUtil.redirect(getTokenAuthUrl(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 fo the conference room. + */ +function initJWTTokenListener(room) { + var self = this; + var listener = function (event) { + if (externalAuthWindow !== event.source) { + console.warn("Ignored message not coming " + + "from external authnetication window"); + return; + } + if (event.data && event.data.jwtToken) { + config.token = event.data.jwtToken; + console.info("Received JWT token:", config.token); + var roomName = room.getName(); + openConnection({retry: false, roomName: roomName }) + .then(function (connection) { + // Start new connection + let newRoom = connection.initJitsiConference( + roomName, APP.conference._getConferenceOptions()); + // Authenticate from the new connection to get + // the session-ID from the focus, which wil then be used + // to upgrade current connection's user role + newRoom.room.moderator.authenticate().then(function () { + connection.disconnect(); + // At this point we'll have session-ID stored in + // the settings. It wil be used in the call below + // to upgrade user's role + room.room.moderator.authenticate() + .then(function () { + console.info("User role upgrade done !"); + unregister(); + }).catch(function (err, errCode) { + console.error( + "Authentication failed: ", err, errCode); + unregister(); + } + ); + }).catch(function (error, code) { + unregister(); + connection.disconnect(); + console.error( + 'Authentication failed on the new connection', + error, code); + }); + }, function (err) { + unregister(); + console.error("Failed to open new connection", err); + }); + } + }; + var unregister = function () { + window.removeEventListener("message", listener); + }; + if (window.addEventListener) { + window.addEventListener("message", listener, false); } } @@ -100,7 +189,7 @@ function doXmppAuth (room, lockPassword) { * @param {string} [lockPassword] password to use if the conference is locked */ function authenticate (room, lockPassword) { - if (room.isExternalAuthEnabled()) { + if (isTokenAuthEnabled || room.isExternalAuthEnabled()) { doExternalAuth(room, lockPassword); } else { doXmppAuth(room, lockPassword); @@ -157,10 +246,52 @@ function closeAuth() { } } +function showXmppPasswordPrompt(roomName, connect) { + return new Promise(function (resolve, reject) { + let authDialog = LoginDialog.showAuthDialog( + function (id, password) { + connect(id, password, roomName).then(function (connection) { + authDialog.close(); + resolve(connection); + }, function (err) { + if (err === ConnectionErrors.PASSWORD_REQUIRED) { + authDialog.displayError(err); + } else { + authDialog.close(); + reject(err); + } + }); + } + ); + }); +} + +/** + * Show Authentication Dialog and try to connect with new credentials. + * If failed to connect because of PASSWORD_REQUIRED error + * then ask for password again. + * @param {string} [roomName] name of the conference room + * @param {function(id, password, roomName)} [connect] function that returns + * a Promise which resolves with JitsiConnection or fails with one of + * ConnectionErrors. + * @returns {Promise} + */ +function requestAuth(roomName, connect) { + if (isTokenAuthEnabled) { + // This Promise never resolves as user gets redirected to another URL + return new Promse(function (resolve, reject) { + redirectToTokenAuthService(roomName); + }); + } else { + return showXmppPasswordPrompt(roomName, connect); + } +} + export default { authenticate, requireAuth, + requestAuth, closeAuth, logout };