Merge pull request #13601 from Tainan404/refactor-authentication
Client authentication refactoring
This commit is contained in:
commit
7551efaa9b
@ -37,7 +37,6 @@ export default function handleValidateAuthToken({ body }, meetingId) {
|
|||||||
check(reasonCode, String);
|
check(reasonCode, String);
|
||||||
|
|
||||||
const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
|
const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
|
||||||
|
|
||||||
Logger.info(`PendingAuths length [${pendingAuths.length}]`);
|
Logger.info(`PendingAuths length [${pendingAuths.length}]`);
|
||||||
if (pendingAuths.length === 0) return;
|
if (pendingAuths.length === 0) return;
|
||||||
|
|
||||||
|
@ -2,32 +2,77 @@ import { Meteor } from 'meteor/meteor';
|
|||||||
import RedisPubSub from '/imports/startup/server/redis';
|
import RedisPubSub from '/imports/startup/server/redis';
|
||||||
import Logger from '/imports/startup/server/logger';
|
import Logger from '/imports/startup/server/logger';
|
||||||
import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState';
|
import upsertValidationState from '/imports/api/auth-token-validation/server/modifiers/upsertValidationState';
|
||||||
import { ValidationStates } from '/imports/api/auth-token-validation';
|
import AuthTokenValidation, { ValidationStates } from '/imports/api/auth-token-validation';
|
||||||
import pendingAuthenticationsStore from '../store/pendingAuthentications';
|
import pendingAuthenticationsStore from '../store/pendingAuthentications';
|
||||||
|
|
||||||
export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) {
|
const AUTH_TIMEOUT = 120000;
|
||||||
try {
|
|
||||||
const REDIS_CONFIG = Meteor.settings.private.redis;
|
|
||||||
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
|
||||||
const EVENT_NAME = 'ValidateAuthTokenReqMsg';
|
|
||||||
|
|
||||||
Logger.debug('ValidateAuthToken method called', { meetingId, requesterUserId, requesterToken, externalId });
|
async function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) {
|
||||||
|
let setTimeoutRef = null;
|
||||||
if (!meetingId) return false;
|
const userValidation = await new Promise((res, rej) => {
|
||||||
|
const observeFunc = (obj) => {
|
||||||
// Store reference of methodInvocationObject ( to postpone the connection userId definition )
|
if (obj.validationStatus === ValidationStates.VALIDATED) {
|
||||||
pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this);
|
clearTimeout(setTimeoutRef);
|
||||||
upsertValidationState(meetingId, requesterUserId, ValidationStates.VALIDATING, this.connection.id);
|
return res(obj);
|
||||||
|
}
|
||||||
const payload = {
|
if (obj.validationStatus === ValidationStates.INVALID) {
|
||||||
userId: requesterUserId,
|
clearTimeout(setTimeoutRef);
|
||||||
authToken: requesterToken,
|
return res(obj);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
const authTokenValidationObserver = AuthTokenValidation.find({
|
||||||
|
connectionId: this.connection.id,
|
||||||
|
}).observe({
|
||||||
|
added: observeFunc,
|
||||||
|
changed: observeFunc,
|
||||||
|
});
|
||||||
|
|
||||||
Logger.info(`User '${requesterUserId}' is trying to validate auth token for meeting '${meetingId}' from connection '${this.connection.id}'`);
|
setTimeoutRef = setTimeout(() => {
|
||||||
|
observeFunc.stop();
|
||||||
|
rej();
|
||||||
|
}, AUTH_TIMEOUT);
|
||||||
|
|
||||||
return RedisPubSub.publishUserMessage(CHANNEL, EVENT_NAME, meetingId, requesterUserId, payload);
|
try {
|
||||||
} catch (err) {
|
const REDIS_CONFIG = Meteor.settings.private.redis;
|
||||||
Logger.error(`Exception while invoking method validateAuthToken ${err.stack}`);
|
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
|
||||||
}
|
const EVENT_NAME = 'ValidateAuthTokenReqMsg';
|
||||||
|
|
||||||
|
Logger.debug('ValidateAuthToken method called', { meetingId, requesterUserId, requesterToken, externalId });
|
||||||
|
|
||||||
|
if (!meetingId) return false;
|
||||||
|
|
||||||
|
// Store reference of methodInvocationObject ( to postpone the connection userId definition )
|
||||||
|
pendingAuthenticationsStore.add(meetingId, requesterUserId, requesterToken, this);
|
||||||
|
upsertValidationState(
|
||||||
|
meetingId,
|
||||||
|
requesterUserId,
|
||||||
|
ValidationStates.VALIDATING,
|
||||||
|
this.connection.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
userId: requesterUserId,
|
||||||
|
authToken: requesterToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
Logger.info(`User '${requesterUserId}' is trying to validate auth token for meeting '${meetingId}' from connection '${this.connection.id}'`);
|
||||||
|
|
||||||
|
return RedisPubSub.publishUserMessage(
|
||||||
|
CHANNEL,
|
||||||
|
EVENT_NAME,
|
||||||
|
meetingId,
|
||||||
|
requesterUserId,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
} catch (err) {
|
||||||
|
const errMsg = `Exception while invoking method validateAuthToken ${err}`;
|
||||||
|
Logger.error(errMsg);
|
||||||
|
rej(errMsg);
|
||||||
|
clearTimeout(setTimeoutRef);
|
||||||
|
authTokenValidationObserver.stop();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return userValidation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default validateAuthToken;
|
||||||
|
@ -35,7 +35,6 @@ class PendingAuthentitcations {
|
|||||||
|
|
||||||
// find matches
|
// find matches
|
||||||
const matches = this.store.filter(e => e.key === key);
|
const matches = this.store.filter(e => e.key === key);
|
||||||
|
|
||||||
// remove matches (if any)
|
// remove matches (if any)
|
||||||
if (matches.length) {
|
if (matches.length) {
|
||||||
this.store = this.store.filter(e => e.key !== key);
|
this.store = this.store.filter(e => e.key !== key);
|
||||||
|
@ -3,7 +3,6 @@ import { withTracker } from 'meteor/react-meteor-data';
|
|||||||
import { defineMessages, injectIntl } from 'react-intl';
|
import { defineMessages, injectIntl } from 'react-intl';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import Auth from '/imports/ui/services/auth';
|
import Auth from '/imports/ui/services/auth';
|
||||||
import AuthTokenValidation from '/imports/api/auth-token-validation';
|
|
||||||
import Users from '/imports/ui/local-collections/users-collection/users';
|
import Users from '/imports/ui/local-collections/users-collection/users';
|
||||||
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
import Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
|
||||||
import { notify } from '/imports/ui/services/notification';
|
import { notify } from '/imports/ui/services/notification';
|
||||||
@ -120,9 +119,7 @@ const currentUserEmoji = (currentUser) => (currentUser
|
|||||||
);
|
);
|
||||||
|
|
||||||
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
|
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
|
||||||
const authTokenValidation = AuthTokenValidation.findOne({}, { sort: { updatedAt: -1 } });
|
if (Auth.connectionID !== Meteor.connection._lastSessionId) {
|
||||||
|
|
||||||
if (authTokenValidation.connectionId !== Meteor.connection._lastSessionId) {
|
|
||||||
endMeeting('403');
|
endMeeting('403');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,5 +94,4 @@ class AuthenticatedHandler extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export default AuthenticatedHandler;
|
export default AuthenticatedHandler;
|
||||||
|
@ -33,6 +33,7 @@ class Auth {
|
|||||||
this._confname = Storage.getItem('confname');
|
this._confname = Storage.getItem('confname');
|
||||||
this._externUserID = Storage.getItem('externUserID');
|
this._externUserID = Storage.getItem('externUserID');
|
||||||
this._fullname = Storage.getItem('fullname');
|
this._fullname = Storage.getItem('fullname');
|
||||||
|
this._connectionID = Storage.getItem('connectionID');
|
||||||
}
|
}
|
||||||
|
|
||||||
get meetingID() {
|
get meetingID() {
|
||||||
@ -142,6 +143,15 @@ class Auth {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
set _connectionID(connectionId) {
|
||||||
|
this._connectionID = connectionId;
|
||||||
|
Storage.setItem('sessionToken', this._connectionID);
|
||||||
|
}
|
||||||
|
|
||||||
|
get sessionToken() {
|
||||||
|
return this._sessionToken;
|
||||||
|
}
|
||||||
|
|
||||||
set(
|
set(
|
||||||
meetingId,
|
meetingId,
|
||||||
requesterUserId,
|
requesterUserId,
|
||||||
@ -213,53 +223,30 @@ class Auth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validateAuthToken() {
|
validateAuthToken() {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let computation = null;
|
SubscriptionRegistry.createSubscription('current-user');
|
||||||
|
|
||||||
const validationTimeout = setTimeout(() => {
|
const validationTimeout = setTimeout(() => {
|
||||||
computation.stop();
|
|
||||||
reject({
|
reject({
|
||||||
error: 408,
|
error: 408,
|
||||||
description: 'Authentication timeout',
|
description: 'Authentication timeout',
|
||||||
});
|
});
|
||||||
}, CONNECTION_TIMEOUT);
|
}, CONNECTION_TIMEOUT);
|
||||||
|
|
||||||
makeCall('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID);
|
Meteor.call('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID, (err, result) => {
|
||||||
|
const authenticationTokenValidation = result;
|
||||||
const authTokenSubscription = SubscriptionRegistry.createSubscription('auth-token-validation', {}, { meetingId: this.meetingID, userId: this.userID });
|
|
||||||
SubscriptionRegistry.createSubscription('current-user');
|
|
||||||
|
|
||||||
Tracker.autorun((c) => {
|
|
||||||
computation = c;
|
|
||||||
|
|
||||||
if (!authTokenSubscription.ready()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const selector = {
|
|
||||||
connectionId: Meteor.connection._lastSessionId,
|
|
||||||
};
|
|
||||||
|
|
||||||
const authenticationTokenValidation = AuthTokenValidation.findOne(selector);
|
|
||||||
|
|
||||||
if (!authenticationTokenValidation) return;
|
if (!authenticationTokenValidation) return;
|
||||||
|
|
||||||
switch (authenticationTokenValidation.validationStatus) {
|
switch (authenticationTokenValidation.validationStatus) {
|
||||||
case ValidationStates.INVALID:
|
case ValidationStates.INVALID:
|
||||||
c.stop();
|
|
||||||
reject({ error: 403, description: authenticationTokenValidation.reason });
|
reject({ error: 403, description: authenticationTokenValidation.reason });
|
||||||
break;
|
break;
|
||||||
case ValidationStates.VALIDATED:
|
case ValidationStates.VALIDATED:
|
||||||
initCursorStreamListener();
|
initCursorStreamListener();
|
||||||
initAnnotationsStreamListener();
|
initAnnotationsStreamListener();
|
||||||
c.stop();
|
|
||||||
clearTimeout(validationTimeout);
|
clearTimeout(validationTimeout);
|
||||||
|
this.connectionID = authenticationTokenValidation.connectionId;
|
||||||
setTimeout(() => resolve(true), 100);
|
setTimeout(() => resolve(true), 100);
|
||||||
break;
|
break;
|
||||||
case ValidationStates.VALIDATING:
|
|
||||||
break;
|
|
||||||
case ValidationStates.NOT_VALIDATED:
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user