replaced localStorage state events with postMessage

This commit is contained in:
Wyatt Johnson
2018-02-27 15:14:17 -07:00
parent 9437c5f5a3
commit f125fac077
15 changed files with 188 additions and 30 deletions
+25 -10
View File
@@ -1,14 +1,29 @@
import { HANDLE_SUCCESSFUL_LOGIN } from 'coral-framework/constants/auth';
import sendMessage from 'coral-framework/services/messages/sendMessage';
document.addEventListener('DOMContentLoaded', () => {
// Get the auth element and parse it as JSON by decoding it.
const auth = document.getElementById('auth');
const doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = auth.innerText;
try {
// Get the auth element and parse it as JSON by decoding it.
const auth = document.getElementById('auth');
const doc = document.implementation.createHTMLDocument('');
doc.body.innerHTML = auth.innerText;
// Set the item in localStorage.
localStorage.setItem('auth', doc.body.textContent);
// Auth state is contained within the node.
const { err, data } = JSON.parse(doc.body.textContent);
if (err) {
// TODO: send back the error message.
console.error(err);
} else {
// The data will contain a user and a token.
const { user, token } = data;
// Close the window.
setTimeout(() => {
window.close();
}, 50);
// Send the state back.
sendMessage(HANDLE_SUCCESSFUL_LOGIN, { user, token });
}
} finally {
// Always close the window.
setTimeout(() => {
window.close();
}, 50);
}
});
@@ -1,14 +1,10 @@
import * as actions from '../constants/login';
import { checkLogin } from 'coral-framework/actions/auth';
export const showSignInDialog = () => ({
type: actions.SHOW_SIGNIN_DIALOG,
});
export const hideSignInDialog = () => dispatch => {
dispatch(checkLogin());
dispatch({ type: actions.HIDE_SIGNIN_DIALOG });
};
export const hideSignInDialog = () => ({ type: actions.HIDE_SIGNIN_DIALOG });
export const focusSignInDialog = () => ({
type: actions.FOCUS_SIGNIN_DIALOG,
@@ -29,6 +29,17 @@ import t from 'coral-framework/services/i18n';
import PropTypes from 'prop-types';
import { setActiveTab } from '../actions/embed';
// TODO: refactor out to a HOC
import { HANDLE_SUCCESSFUL_LOGIN } from 'coral-framework/constants/auth';
import {
handleSuccessfulLogin,
checkLogin,
} from 'coral-framework/actions/auth';
import {
subscribeToMessages,
unsubscribeFromMessages,
} from 'coral-framework/services/messages';
class EmbedContainer extends React.Component {
static contextTypes = {
pym: PropTypes.object,
@@ -37,6 +48,8 @@ class EmbedContainer extends React.Component {
subscriptions = [];
subscribeToUpdates(props = this.props) {
subscribeToMessages(this.handleAuth);
if (props.currentUser) {
const newSubscriptions = [
{
@@ -86,8 +99,23 @@ class EmbedContainer extends React.Component {
unsubscribe() {
this.subscriptions.forEach(unsubscribe => unsubscribe());
this.subscriptions = [];
unsubscribeFromMessages(this.handleAuth);
}
// TODO: refactor out to a HOC
handleAuth = ({ name, data }) => {
if (name !== HANDLE_SUCCESSFUL_LOGIN) {
return;
}
// data will contain the user and token.
const { user, token } = data;
const { handleSuccessfulLogin, checkLogin } = this.props;
handleSuccessfulLogin(user, token);
checkLogin();
};
resubscribe(props) {
this.unsubscribe();
this.subscribeToUpdates(props);
@@ -301,6 +329,8 @@ EmbedContainer.propTypes = {
fetchAssetSuccess: PropTypes.func,
showSignInDialog: PropTypes.bool,
signInDialogFocus: PropTypes.bool,
handleSuccessfulLogin: PropTypes.func.isRequired,
checkLogin: PropTypes.func.isRequired,
};
const mapStateToProps = state => ({
@@ -328,6 +358,8 @@ const mapDispatchToProps = dispatch =>
blurSignInDialog,
hideSignInDialog,
updateStatus,
handleSuccessfulLogin,
checkLogin,
},
dispatch
);
+14 -1
View File
@@ -1,5 +1,6 @@
import * as actions from '../constants/auth';
import jwtDecode from 'jwt-decode';
import { sendMessage } from '../services/messages';
function cleanAuthData(localStorage) {
localStorage.removeItem('token');
@@ -65,11 +66,23 @@ export const handleSuccessfulLogin = (user, token) => (
_,
{ client, localStorage }
) => {
const { exp } = jwtDecode(token);
if (localStorage) {
localStorage.setItem('exp', jwtDecode(token).exp);
localStorage.setItem('exp', exp);
localStorage.setItem('token', token);
}
// Send the message via the messages service to the window.opener if it
// exists.
if (window.opener) {
sendMessage(
actions.HANDLE_SUCCESSFUL_LOGIN,
{ user, token },
window.opener
);
}
client.resetWebsocket();
dispatch({
@@ -0,0 +1,11 @@
import { getStaticConfiguration } from 'coral-framework/services/staticConfiguration';
// Load the static url from the static configuration, we'll use it to formulate
// the authorized domain to allow the message to be sent to.
const { STATIC_ORIGIN } = getStaticConfiguration();
// Export the origin that will be sent.
export { STATIC_ORIGIN as ORIGIN };
// Scope all the requests made via the messages service.
export const SCOPE = 'client';
@@ -0,0 +1,2 @@
export { default as sendMessage } from './sendMessage';
export { subscribeToMessages, unsubscribeFromMessages } from './subscriptions';
@@ -0,0 +1,13 @@
import { SCOPE, ORIGIN } from './constants';
export default (name, data, target = window.opener) => {
if (!target) {
return;
}
// Serialize the message to be sent via postMessage.
const msg = { name, data, scope: SCOPE };
// Send the message.
target.postMessage(msg, ORIGIN);
};
@@ -0,0 +1,70 @@
import { SCOPE, ORIGIN } from './constants';
import set from 'lodash/set';
import has from 'lodash/has';
import get from 'lodash/get';
import remove from 'lodash/remove';
// withOriginCheck wraps a given handler for a messages event with a check to
// see if the origin matches.
const withOriginCheck = handler => {
return event => {
const origin = get(event, 'origin');
if (origin !== ORIGIN) {
return;
}
const scope = get(event, 'data.scope');
if (scope !== SCOPE) {
return;
}
// Pass the handler the event details.
handler(event);
};
};
// withParseData will parse the data.
const withParseData = handler => {
return event => {
const data = get(event, 'data.data');
const name = get(event, 'data.name');
if (!data || !name) {
return;
}
handler({ data, name, event });
};
};
// Store a reference to each listener added.
const listeners = {};
export const subscribeToMessages = (handler, target = window) => {
// If this handler is already attached to the target, detach it.
if (has(listeners, [target, handler])) {
unsubscribeFromMessages(handler, target);
}
// Wrap the listener with a origin check.
const listener = withOriginCheck(withParseData(handler));
// Save a reference to the compiled listener.
set(listeners, [target, handler], listener);
// Attach the listener to the target.
target.addEventListener('message', listener);
};
export const unsubscribeFromMessages = (handler, target = window) => {
if (!has(listeners, [target, handler])) {
return;
}
const listener = get(listeners, [target, handler]);
// Remove the listener from the target.
target.removeEventListener('message', listener);
remove(listeners, [target, handler]);
};
+8 -1
View File
@@ -1,6 +1,12 @@
const SettingsService = require('../services/settings');
const { BASE_URL, BASE_PATH, MOUNT_PATH, STATIC_URL } = require('../url');
const {
BASE_URL,
BASE_PATH,
MOUNT_PATH,
STATIC_URL,
STATIC_ORIGIN,
} = require('../url');
const { RECAPTCHA_PUBLIC, WEBSOCKET_LIVE_URI } = require('../config');
@@ -15,6 +21,7 @@ const TEMPLATE_LOCALS = {
TALK_RECAPTCHA_PUBLIC: RECAPTCHA_PUBLIC,
LIVE_URI: WEBSOCKET_LIVE_URI,
STATIC_URL,
STATIC_ORIGIN,
},
};
@@ -1,7 +1,3 @@
export const loginWithFacebook = () => (dispatch, _, { rest }) => {
window.open(
`${rest.uri}/auth/facebook`,
'Continue with Facebook',
'menubar=0,resizable=0,width=500,height=500,top=200,left=500'
);
window.location = `${rest.uri}/auth/facebook`;
};
@@ -1,7 +1,3 @@
export const loginWithGoogle = () => (dispatch, _, { rest }) => {
window.open(
`${rest.uri}/auth/google`,
'Continue with Google',
'menubar=0,resizable=0,width=500,height=500,top=200,left=500'
);
window.location = `${rest.uri}/auth/google`;
};
+3
View File
@@ -18,9 +18,12 @@ const MOUNT_PATH = ROOT_URL_MOUNT_PATH ? BASE_PATH : '/';
// The STATIC_URL is the url where static assets should be loaded from.
const STATIC_URL = trailingSlash(STATIC_URI);
const STATIC_ORIGIN = new URL(STATIC_URI).origin;
module.exports = {
BASE_URL,
BASE_PATH,
MOUNT_PATH,
STATIC_URL,
STATIC_ORIGIN,
};
+3
View File
@@ -1,5 +1,8 @@
<!DOCTYPE html>
<html>
<head>
<%- include partials/data %>
</head>
<body>
<script type="application/json" id="auth"><%- encodeJSONForHTML(auth) %></script>
<script type="text/javascript" src="<%= STATIC_URL %>static/coral-auth-callback/bundle.js"></script>
+3
View File
@@ -0,0 +1,3 @@
<%_ if (data != null) { _%>
<script id="data" type="application/json"><%- JSON.stringify(data) %></script>
<%_ } _%>
+1 -3
View File
@@ -18,7 +18,5 @@
<%_ if (locals.customCssUrl) { _%>
<link href="<%= customCssUrl %>" rel="stylesheet" type="text/css">
<%_ } _%>
<%_ if (data != null) { _%>
<script id="data" type="application/json"><%- JSON.stringify(data) %></script>
<%_ } _%>
<%- include data %>
<base href="<%= BASE_URL %>"/>