diff --git a/package.json b/package.json
index c1c7f5fbe5..7ea508fd8a 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,8 @@
     "matrix-js-sdk": "https://github.com/matrix-org/matrix-js-sdk.git#develop",
     "optimist": "^0.6.1",
     "q": "^1.4.1",
-    "react": "^0.13.3",
+    "react": "^0.14.2",
+    "react-dom": "^0.14.2",
     "react-loader": "^1.4.0"
   },
   "//deps": "The loader packages are here because webpack in a project that depends on us needs them in this package's node_modules folder",
diff --git a/src/CasLogic.js b/src/CasLogic.js
deleted file mode 100644
index 1bcc6d5c78..0000000000
--- a/src/CasLogic.js
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
-Copyright 2015 OpenMarket Ltd
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-'use strict';
-
-var url = require ('url');
-
-function getServiceUrl() {
-    var parsedUrl = url.parse(window.location.href);
-    return parsedUrl.protocol + "//" + parsedUrl.host + parsedUrl.pathname;
-}
-
-module.exports = {
-    getServiceUrl: getServiceUrl
-};
diff --git a/src/Modal.js b/src/Modal.js
index bf7758a1a8..a7dd4ca82d 100644
--- a/src/Modal.js
+++ b/src/Modal.js
@@ -18,6 +18,7 @@ limitations under the License.
 'use strict';
 
 var React = require('react');
+var ReactDOM = require('react-dom');
 
 module.exports = {
     DialogContainerId: "mx_Dialog_Container",
@@ -52,7 +53,7 @@ module.exports = {
             </div>
         );
 
-        React.render(dialog, this.getOrCreateContainer());
+        ReactDOM.render(dialog, this.getOrCreateContainer());
 
         return {close: closeDialog};
     },
@@ -77,7 +78,7 @@ module.exports = {
             </div>
         );
 
-        React.render(dialog, this.getOrCreateContainer());
+        ReactDOM.render(dialog, this.getOrCreateContainer());
 
         return {close: closeDialog};
     },
diff --git a/src/controllers/molecules/MEmoteTile.js b/src/controllers/molecules/MEmoteTile.js
index 1fb117ceef..d32d8ae911 100644
--- a/src/controllers/molecules/MEmoteTile.js
+++ b/src/controllers/molecules/MEmoteTile.js
@@ -24,7 +24,7 @@ linkifyMatrix(linkify);
 
 module.exports = {
     componentDidMount: function() {
-        linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
+        linkifyElement(this.refs.content, linkifyMatrix.options);
     }
 };
 
diff --git a/src/controllers/molecules/MNoticeTile.js b/src/controllers/molecules/MNoticeTile.js
index aceb029495..597ce3cd10 100644
--- a/src/controllers/molecules/MNoticeTile.js
+++ b/src/controllers/molecules/MNoticeTile.js
@@ -23,6 +23,6 @@ linkifyMatrix(linkify);
 
 module.exports = {
     componentDidMount: function() {
-        linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
+        linkifyElement(this.refs.content, linkifyMatrix.options);
     }
 };
diff --git a/src/controllers/molecules/MTextTile.js b/src/controllers/molecules/MTextTile.js
index 1fb117ceef..d32d8ae911 100644
--- a/src/controllers/molecules/MTextTile.js
+++ b/src/controllers/molecules/MTextTile.js
@@ -24,7 +24,7 @@ linkifyMatrix(linkify);
 
 module.exports = {
     componentDidMount: function() {
-        linkifyElement(this.refs.content.getDOMNode(), linkifyMatrix.options);
+        linkifyElement(this.refs.content, linkifyMatrix.options);
     }
 };
 
diff --git a/src/controllers/molecules/MessageComposer.js b/src/controllers/molecules/MessageComposer.js
index c2b67c7898..7bb2ef9c18 100644
--- a/src/controllers/molecules/MessageComposer.js
+++ b/src/controllers/molecules/MessageComposer.js
@@ -130,7 +130,7 @@ module.exports = {
     componentDidMount: function() {
         this.dispatcherRef = dis.register(this.onAction);
         this.sentHistory.init(
-            this.refs.textarea.getDOMNode(),
+            this.refs.textarea,
             this.props.room.roomId
         );
     },
@@ -143,14 +143,14 @@ module.exports = {
     onAction: function(payload) {
         switch (payload.action) {
             case 'focus_composer':
-                this.refs.textarea.getDOMNode().focus();
+                this.refs.textarea.focus();
                 break;
         }
     },
 
     onKeyDown: function (ev) {
         if (ev.keyCode === KeyCode.ENTER) {
-            var input = this.refs.textarea.getDOMNode().value;
+            var input = this.refs.textarea.value;
             if (input.length === 0) {
                 ev.preventDefault();
                 return;
@@ -179,7 +179,7 @@ module.exports = {
 
         var self = this;
         setTimeout(function() {
-            if (self.refs.textarea && self.refs.textarea.getDOMNode().value != '') {
+            if (self.refs.textarea && self.refs.textarea.value != '') {
                 self.onTypingActivity();
             } else {
                 self.onFinishedTyping();
@@ -188,13 +188,13 @@ module.exports = {
     },
 
     onEnter: function(ev) {
-        var contentText = this.refs.textarea.getDOMNode().value;
+        var contentText = this.refs.textarea.value;
 
         var cmd = SlashCommands.processInput(this.props.room.roomId, contentText);
         if (cmd) {
             ev.preventDefault();
             if (!cmd.error) {
-                this.refs.textarea.getDOMNode().value = '';
+                this.refs.textarea.value = '';
             }
             if (cmd.promise) {
                 cmd.promise.done(function() {
@@ -241,12 +241,12 @@ module.exports = {
                 action: 'message_send_failed'
             });
         });
-        this.refs.textarea.getDOMNode().value = '';
+        this.refs.textarea.value = '';
         ev.preventDefault();
     },
 
     onTab: function(ev, sortedMembers) {
-        var textArea = this.refs.textarea.getDOMNode();
+        var textArea = this.refs.textarea;
         if (!this.tabStruct.completing) {
             this.tabStruct.completing = true;
             this.tabStruct.index = 0;
diff --git a/src/controllers/organisms/CasLogin.js b/src/controllers/organisms/CasLogin.js
index b01c3781b0..d84306e587 100644
--- a/src/controllers/organisms/CasLogin.js
+++ b/src/controllers/organisms/CasLogin.js
@@ -17,20 +17,17 @@ limitations under the License.
 'use strict';
 
 var MatrixClientPeg = require("../../MatrixClientPeg");
-var Cas = require("../../CasLogic");
+var url = require("url");
 
 module.exports = {
 
     onCasClicked: function(ev) {
-        var serviceRedirectUrl = Cas.getServiceUrl() + "#/login/cas";
-        var self = this;
-        MatrixClientPeg.get().getCasServer().done(function(data) {
-            var serverUrl = data.serverUrl + "/login?service=" + encodeURIComponent(serviceRedirectUrl);
-            window.location.href = serverUrl;
-        }, function(error) {
-            self.setStep("stage_m.login.cas");
-            self.setState({errorText: 'Login failed.'});
-        });
+        var cli = MatrixClientPeg.get();
+        var parsedUrl = url.parse(window.location.href, true);
+        parsedUrl.query["homeserver"] = cli.getHomeserverUrl();
+        parsedUrl.query["identityServer"] = cli.getIdentityServerUrl();
+        var casUrl = MatrixClientPeg.get().getCasLoginUrl(url.format(parsedUrl));
+        window.location.href = casUrl;
     },
 
 };
diff --git a/src/controllers/organisms/CreateRoom.js b/src/controllers/organisms/CreateRoom.js
index f6404eb231..3c48e43f74 100644
--- a/src/controllers/organisms/CreateRoom.js
+++ b/src/controllers/organisms/CreateRoom.js
@@ -72,7 +72,7 @@ module.exports = {
                     {
                         type: "m.room.join_rules",
                         content: {
-                            "join_rules": this.state.is_private ? "invite" : "public"
+                            "join_rule": this.state.is_private ? "invite" : "public"
                         }
                     },
                     {
@@ -107,7 +107,7 @@ module.exports = {
             deferred = deferred.then(function(res) {
                 response = res;
                 return encryption.enableEncryption(
-                    cli, response.roomId, options.invite
+                    cli, response.room_id, options.invite
                 );
             }).then(function() {
                 return q(response) }
diff --git a/src/controllers/organisms/MemberList.js b/src/controllers/organisms/MemberList.js
index 4dfe5330e1..8b6733fbeb 100644
--- a/src/controllers/organisms/MemberList.js
+++ b/src/controllers/organisms/MemberList.js
@@ -115,9 +115,11 @@ module.exports = {
     onInvite: function(inputText) {
         var ErrorDialog = sdk.getComponent("organisms.ErrorDialog");
         var self = this;
-        // sanity check the input
         inputText = inputText.trim(); // react requires es5-shim so we know trim() exists
-        if (inputText[0] !== '@' || inputText.indexOf(":") === -1) {
+        var isEmailAddress = /^\S+@\S+\.\S+$/.test(inputText);
+
+        // sanity check the input for user IDs
+        if (!isEmailAddress && (inputText[0] !== '@' || inputText.indexOf(":") === -1)) {
             console.error("Bad user ID to invite: %s", inputText);
             Modal.createDialog(ErrorDialog, {
                 title: "Invite Error",
@@ -125,12 +127,22 @@ module.exports = {
             });
             return;
         }
+
+        var promise;
+        if (isEmailAddress) {
+            promise = MatrixClientPeg.get().inviteByEmail(this.props.roomId, inputText);
+        }
+        else {
+            promise = MatrixClientPeg.get().invite(this.props.roomId, inputText);
+        }
+
         self.setState({
             inviting: true
         });
-        console.log("Invite %s to %s", inputText, this.props.roomId);
-        MatrixClientPeg.get().invite(this.props.roomId, inputText).done(
-        function(res) {
+        console.log(
+            "Invite %s to %s - isEmail=%s", inputText, this.props.roomId, isEmailAddress
+        );
+        promise.done(function(res) {
             console.log("Invited");
             self.setState({
                 inviting: false
diff --git a/src/controllers/organisms/RoomView.js b/src/controllers/organisms/RoomView.js
index 7ab59b497a..3a5d432ebd 100644
--- a/src/controllers/organisms/RoomView.js
+++ b/src/controllers/organisms/RoomView.js
@@ -50,7 +50,7 @@ module.exports = {
 
     componentWillUnmount: function() {
         if (this.refs.messageWrapper) {
-            var messageWrapper = this.refs.messageWrapper.getDOMNode();
+            var messageWrapper = this.refs.messageWrapper;
             messageWrapper.removeEventListener('drop', this.onDrop);
             messageWrapper.removeEventListener('dragover', this.onDragOver);
             messageWrapper.removeEventListener('dragleave', this.onDragLeaveOrEnd);
@@ -85,7 +85,7 @@ module.exports = {
                 // scroll to bottom
                 var messageWrapper = this.refs.messageWrapper;
                 if (messageWrapper) {
-                    messageWrapper = messageWrapper.getDOMNode();
+                    messageWrapper = messageWrapper;
                     messageWrapper.scrollTop = messageWrapper.scrollHeight;
                 }
                 break;
@@ -116,7 +116,7 @@ module.exports = {
         if (room.roomId != this.props.roomId) return;
 
         if (this.refs.messageWrapper) {
-            var messageWrapper = this.refs.messageWrapper.getDOMNode();
+            var messageWrapper = this.refs.messageWrapper;
             this.atBottom = (
                 messageWrapper.scrollHeight - messageWrapper.scrollTop <=
                 (messageWrapper.clientHeight + 150)
@@ -166,7 +166,7 @@ module.exports = {
 
     componentDidMount: function() {
         if (this.refs.messageWrapper) {
-            var messageWrapper = this.refs.messageWrapper.getDOMNode();
+            var messageWrapper = this.refs.messageWrapper;
 
             messageWrapper.addEventListener('drop', this.onDrop);
             messageWrapper.addEventListener('dragover', this.onDragOver);
@@ -184,7 +184,7 @@ module.exports = {
     componentDidUpdate: function() {
         if (!this.refs.messageWrapper) return;
 
-        var messageWrapper = this.refs.messageWrapper.getDOMNode();
+        var messageWrapper = this.refs.messageWrapper;
 
         if (this.state.paginating && !this.waiting_for_paginate) {
             var heightGained = messageWrapper.scrollHeight - this.oldScrollHeight;
@@ -203,7 +203,7 @@ module.exports = {
 
     fillSpace: function() {
         if (!this.refs.messageWrapper) return;
-        var messageWrapper = this.refs.messageWrapper.getDOMNode();
+        var messageWrapper = this.refs.messageWrapper;
         if (messageWrapper.scrollTop < messageWrapper.clientHeight && this.state.room.oldState.paginationToken) {
             this.setState({paginating: true});
 
@@ -254,7 +254,7 @@ module.exports = {
 
     onMessageListScroll: function(ev) {
         if (this.refs.messageWrapper) {
-            var messageWrapper = this.refs.messageWrapper.getDOMNode();
+            var messageWrapper = this.refs.messageWrapper;
             var wasAtBottom = this.atBottom;
             this.atBottom = messageWrapper.scrollHeight - messageWrapper.scrollTop <= messageWrapper.clientHeight;
             if (this.atBottom && !wasAtBottom) {
diff --git a/src/controllers/pages/MatrixChat.js b/src/controllers/pages/MatrixChat.js
index e54f800e63..2fdb9eb8ef 100644
--- a/src/controllers/pages/MatrixChat.js
+++ b/src/controllers/pages/MatrixChat.js
@@ -24,7 +24,7 @@ var sdk = require('../../index');
 var MatrixTools = require('../../MatrixTools');
 var linkifyMatrix = require("../../linkify-matrix");
 
-var Cas = require("../../CasLogic");
+var url = require('url');
 
 module.exports = {
     PageTypes: {
@@ -142,14 +142,17 @@ module.exports = {
                 });
                 this.notifyNewScreen('login');
                 break;
-            case 'cas_login':
+            case 'token_login':
                 if (this.state.logged_in) return;
 
                 var self = this;
-                var client = MatrixClientPeg.get();
-                var serviceUrl = Cas.getServiceUrl();
+                MatrixClientPeg.replaceUsingUrls(
+                    payload.params.homeserver,
+                    payload.params.identityServer
+                );
 
-                client.loginWithCas(payload.params.ticket, serviceUrl).done(function(data) {
+                var client = MatrixClientPeg.get();
+                client.loginWithToken(payload.params.loginToken).done(function(data) {
                     MatrixClientPeg.replaceUsingAccessToken(
                         client.getHomeserverUrl(), client.getIdentityServerUrl(),
                         data.user_id, data.access_token
@@ -158,8 +161,13 @@ module.exports = {
                         screen: undefined,
                         logged_in: true
                     });
-                    self.startMatrixClient();
-                    self.notifyNewScreen('');
+
+                    // We're left with the login token, hs and is url as query params
+                    // in the url, a little nasty but let's redirect to clear them
+                    var parsedUrl = url.parse(window.location.href);
+                    parsedUrl.search = "";
+                    window.location.href = url.format(parsedUrl);
+
                 }, function(error) {
                     self.notifyNewScreen('login');
                     self.setState({errorText: 'Login failed.'});
@@ -391,9 +399,9 @@ module.exports = {
                 action: 'start_login',
                 params: params
             });
-        } else if (screen == 'cas_login') {
+        } else if (screen == 'token_login') {
             dis.dispatch({
-                action: 'cas_login',
+                action: 'token_login',
                 params: params
             });
         } else if (screen.indexOf('room/') == 0) {
diff --git a/src/controllers/templates/Register.js b/src/controllers/templates/Register.js
index 53d1ad1219..59377b55f8 100644
--- a/src/controllers/templates/Register.js
+++ b/src/controllers/templates/Register.js
@@ -87,7 +87,7 @@ module.exports = {
             var scriptTag = document.createElement('script');
             window.mx_on_recaptcha_loaded = this.onCaptchaLoaded;
             scriptTag.setAttribute('src', global.location.protocol+"//www.google.com/recaptcha/api.js?onload=mx_on_recaptcha_loaded&render=explicit");
-            this.refs.recaptchaContainer.getDOMNode().appendChild(scriptTag);
+            this.refs.recaptchaContainer.appendChild(scriptTag);
         }
     },