Merge pull request #13601 from Tainan404/refactor-authentication

Client authentication refactoring
This commit is contained in:
Anton Georgiev 2021-11-02 10:15:16 -04:00 committed by GitHub
commit 7551efaa9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 57 deletions

View File

@ -37,7 +37,6 @@ export default function handleValidateAuthToken({ body }, meetingId) {
check(reasonCode, String);
const pendingAuths = pendingAuthenticationsStore.take(meetingId, userId, authToken);
Logger.info(`PendingAuths length [${pendingAuths.length}]`);
if (pendingAuths.length === 0) return;

View File

@ -2,32 +2,77 @@ import { Meteor } from 'meteor/meteor';
import RedisPubSub from '/imports/startup/server/redis';
import Logger from '/imports/startup/server/logger';
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';
export default function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) {
try {
const REDIS_CONFIG = Meteor.settings.private.redis;
const CHANNEL = REDIS_CONFIG.channels.toAkkaApps;
const EVENT_NAME = 'ValidateAuthTokenReqMsg';
const AUTH_TIMEOUT = 120000;
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,
async function validateAuthToken(meetingId, requesterUserId, requesterToken, externalId) {
let setTimeoutRef = null;
const userValidation = await new Promise((res, rej) => {
const observeFunc = (obj) => {
if (obj.validationStatus === ValidationStates.VALIDATED) {
clearTimeout(setTimeoutRef);
return res(obj);
}
if (obj.validationStatus === ValidationStates.INVALID) {
clearTimeout(setTimeoutRef);
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);
} catch (err) {
Logger.error(`Exception while invoking method validateAuthToken ${err.stack}`);
}
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 });
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;

View File

@ -35,7 +35,6 @@ class PendingAuthentitcations {
// find matches
const matches = this.store.filter(e => e.key === key);
// remove matches (if any)
if (matches.length) {
this.store = this.store.filter(e => e.key !== key);

View File

@ -3,7 +3,6 @@ import { withTracker } from 'meteor/react-meteor-data';
import { defineMessages, injectIntl } from 'react-intl';
import PropTypes from 'prop-types';
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 Meetings from '/imports/ui/local-collections/meetings-collection/meetings';
import { notify } from '/imports/ui/services/notification';
@ -120,9 +119,7 @@ const currentUserEmoji = (currentUser) => (currentUser
);
export default injectIntl(withModalMounter(withTracker(({ intl, baseControls }) => {
const authTokenValidation = AuthTokenValidation.findOne({}, { sort: { updatedAt: -1 } });
if (authTokenValidation.connectionId !== Meteor.connection._lastSessionId) {
if (Auth.connectionID !== Meteor.connection._lastSessionId) {
endMeeting('403');
}

View File

@ -94,5 +94,4 @@ class AuthenticatedHandler extends Component {
}
}
export default AuthenticatedHandler;

View File

@ -33,6 +33,7 @@ class Auth {
this._confname = Storage.getItem('confname');
this._externUserID = Storage.getItem('externUserID');
this._fullname = Storage.getItem('fullname');
this._connectionID = Storage.getItem('connectionID');
}
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(
meetingId,
requesterUserId,
@ -213,53 +223,30 @@ class Auth {
}
validateAuthToken() {
return new Promise(async (resolve, reject) => {
let computation = null;
return new Promise((resolve, reject) => {
SubscriptionRegistry.createSubscription('current-user');
const validationTimeout = setTimeout(() => {
computation.stop();
reject({
error: 408,
description: 'Authentication timeout',
});
}, CONNECTION_TIMEOUT);
makeCall('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID);
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);
Meteor.call('validateAuthToken', this.meetingID, this.userID, this.token, this.externUserID, (err, result) => {
const authenticationTokenValidation = result;
if (!authenticationTokenValidation) return;
switch (authenticationTokenValidation.validationStatus) {
case ValidationStates.INVALID:
c.stop();
reject({ error: 403, description: authenticationTokenValidation.reason });
break;
case ValidationStates.VALIDATED:
initCursorStreamListener();
initAnnotationsStreamListener();
c.stop();
clearTimeout(validationTimeout);
this.connectionID = authenticationTokenValidation.connectionId;
setTimeout(() => resolve(true), 100);
break;
case ValidationStates.VALIDATING:
break;
case ValidationStates.NOT_VALIDATED:
break;
default:
}
});