You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AuthHandler.js 7.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. // @flow
  2. import Logger from '@jitsi/logger';
  3. import { openConnection } from '../../../connection';
  4. import {
  5. openAuthDialog,
  6. openLoginDialog } from '../../../react/features/authentication/actions.web';
  7. import {
  8. LoginDialog,
  9. WaitForOwnerDialog
  10. } from '../../../react/features/authentication/components';
  11. import {
  12. isTokenAuthEnabled,
  13. getTokenAuthUrl
  14. } from '../../../react/features/authentication/functions';
  15. import { getReplaceParticipant } from '../../../react/features/base/config/functions';
  16. import { isDialogOpen } from '../../../react/features/base/dialog';
  17. import { setJWT } from '../../../react/features/base/jwt';
  18. import { setPrejoinPageVisibility } from '../../../react/features/prejoin';
  19. import UIUtil from '../util/UIUtil';
  20. import ExternalLoginDialog from './LoginDialog';
  21. let externalAuthWindow;
  22. declare var APP: Object;
  23. const logger = Logger.getLogger(__filename);
  24. /**
  25. * Authenticate using external service or just focus
  26. * external auth window if there is one already.
  27. *
  28. * @param {JitsiConference} room
  29. * @param {string} [lockPassword] password to use if the conference is locked
  30. */
  31. function doExternalAuth(room, lockPassword) {
  32. const config = APP.store.getState()['features/base/config'];
  33. if (externalAuthWindow) {
  34. externalAuthWindow.focus();
  35. return;
  36. }
  37. if (room.isJoined()) {
  38. let getUrl;
  39. if (isTokenAuthEnabled(config)) {
  40. getUrl = Promise.resolve(getTokenAuthUrl(config)(room.getName(), true));
  41. initJWTTokenListener(room);
  42. } else {
  43. getUrl = room.getExternalAuthUrl(true);
  44. }
  45. getUrl.then(url => {
  46. externalAuthWindow = ExternalLoginDialog.showExternalAuthDialog(
  47. url,
  48. () => {
  49. externalAuthWindow = null;
  50. if (!isTokenAuthEnabled(config)) {
  51. room.join(lockPassword);
  52. }
  53. }
  54. );
  55. });
  56. } else if (isTokenAuthEnabled(config)) {
  57. redirectToTokenAuthService(room.getName());
  58. } else {
  59. room.getExternalAuthUrl().then(UIUtil.redirect);
  60. }
  61. }
  62. /**
  63. * Redirect the user to the token authentication service for the login to be
  64. * performed. Once complete it is expected that the service will bring the user
  65. * back with "?jwt={the JWT token}" query parameter added.
  66. * @param {string} [roomName] the name of the conference room.
  67. */
  68. export function redirectToTokenAuthService(roomName: string) {
  69. const config = APP.store.getState()['features/base/config'];
  70. // FIXME: This method will not preserve the other URL params that were
  71. // originally passed.
  72. UIUtil.redirect(getTokenAuthUrl(config)(roomName, false));
  73. }
  74. /**
  75. * Initializes 'message' listener that will wait for a JWT token to be received
  76. * from the token authentication service opened in a popup window.
  77. * @param room the name of the conference room.
  78. */
  79. function initJWTTokenListener(room) {
  80. /**
  81. *
  82. */
  83. function listener({ data, source }) {
  84. if (externalAuthWindow !== source) {
  85. logger.warn('Ignored message not coming '
  86. + 'from external authnetication window');
  87. return;
  88. }
  89. let jwt;
  90. if (data && (jwt = data.jwtToken)) {
  91. logger.info('Received JSON Web Token (JWT):', jwt);
  92. APP.store.dispatch(setJWT(jwt));
  93. const roomName = room.getName();
  94. openConnection({
  95. retry: false,
  96. roomName
  97. }).then(connection => {
  98. // Start new connection
  99. const newRoom = connection.initJitsiConference(
  100. roomName, APP.conference._getConferenceOptions());
  101. // Authenticate from the new connection to get
  102. // the session-ID from the focus, which will then be used
  103. // to upgrade current connection's user role
  104. newRoom.room.moderator.authenticate()
  105. .then(() => {
  106. connection.disconnect();
  107. // At this point we'll have session-ID stored in
  108. // the settings. It will be used in the call below
  109. // to upgrade user's role
  110. room.room.moderator.authenticate()
  111. .then(() => {
  112. logger.info('User role upgrade done !');
  113. // eslint-disable-line no-use-before-define
  114. unregister();
  115. })
  116. .catch((err, errCode) => {
  117. logger.error('Authentication failed: ',
  118. err, errCode);
  119. unregister();
  120. });
  121. })
  122. .catch((error, code) => {
  123. unregister();
  124. connection.disconnect();
  125. logger.error(
  126. 'Authentication failed on the new connection',
  127. error, code);
  128. });
  129. }, err => {
  130. unregister();
  131. logger.error('Failed to open new connection', err);
  132. });
  133. }
  134. }
  135. /**
  136. *
  137. */
  138. function unregister() {
  139. window.removeEventListener('message', listener);
  140. }
  141. if (window.addEventListener) {
  142. window.addEventListener('message', listener, false);
  143. }
  144. }
  145. /**
  146. * Authenticate for the conference.
  147. * Uses external service for auth if conference supports that.
  148. * @param {JitsiConference} room
  149. * @param {string} [lockPassword] password to use if the conference is locked
  150. */
  151. function authenticate(room: Object, lockPassword: string) {
  152. const config = APP.store.getState()['features/base/config'];
  153. if (isTokenAuthEnabled(config) || room.isExternalAuthEnabled()) {
  154. doExternalAuth(room, lockPassword);
  155. } else {
  156. APP.store.dispatch(setPrejoinPageVisibility(false));
  157. APP.store.dispatch(openLoginDialog());
  158. }
  159. }
  160. /**
  161. * Notify user that authentication is required to create the conference.
  162. * @param {JitsiConference} room
  163. * @param {string} [lockPassword] password to use if the conference is locked
  164. */
  165. function requireAuth(room: Object, lockPassword: string) {
  166. if (isDialogOpen(APP.store, WaitForOwnerDialog) || isDialogOpen(APP.store, LoginDialog)) {
  167. return;
  168. }
  169. APP.store.dispatch(
  170. openAuthDialog(
  171. room.getName(), authenticate.bind(null, room, lockPassword))
  172. );
  173. }
  174. /**
  175. * De-authenticate local user.
  176. *
  177. * @param {JitsiConference} room
  178. * @param {string} [lockPassword] password to use if the conference is locked
  179. * @returns {Promise}
  180. */
  181. function logout(room: Object) {
  182. return new Promise(resolve => {
  183. room.room.moderator.logout(resolve);
  184. }).then(url => {
  185. // de-authenticate conference on the fly
  186. if (room.isJoined()) {
  187. const replaceParticipant = getReplaceParticipant(APP.store.getState());
  188. room.join(null, replaceParticipant);
  189. }
  190. return url;
  191. });
  192. }
  193. export default {
  194. authenticate,
  195. logout,
  196. requireAuth
  197. };