Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2)

Side by Side Diff: remoting/webapp/xmpp_login_handler.js

Issue 514343002: XMPP implementation in JavaScript. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/webapp/xmpp_connection.js ('k') | remoting/webapp/xmpp_stream_parser.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 'use strict';
6
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
9
10 /**
11 * XmppLoginHandler handles authentication handshake for XmppConnection. It
12 * receives incoming data using onDataReceived(), calls |sendMessageCallback|
13 * to send outgoing messages and calls |onHandshakeDoneCallback| after
14 * authentication is finished successfully or |onErrorCallback| on error.
15 *
16 * See RFC3920 for description of XMPP and authentication handshake.
17 *
18 * @param {string} server Domain name of the server we are connecting to.
19 * @param {string} username Username.
20 * @param {string} authToken OAuth2 token.
21 * @param {function(string):void} sendMessageCallback Callback to call to send
22 * a message.
23 * @param {function():void} startTlsCallback Callback to call to start TLS on
24 * the underlying socket.
25 * @param {function(string, remoting.XmppStreamParser):void}
26 * onHandshakeDoneCallback Callback to call after authentication is
27 * completed successfully
28 * @param {function(remoting.Error, string):void} onErrorCallback Callback to
29 * call on error. Can be called at any point during lifetime of connection.
30 * @constructor
31 */
32 remoting.XmppLoginHandler = function(server,
33 username,
34 authToken,
35 sendMessageCallback,
36 startTlsCallback,
37 onHandshakeDoneCallback,
38 onErrorCallback) {
39 /** @private */
40 this.server_ = server;
41 /** @private */
42 this.username_ = username;
43 /** @private */
44 this.authToken_ = authToken;
45 /** @private */
46 this.sendMessageCallback_ = sendMessageCallback;
47 /** @private */
48 this.startTlsCallback_ = startTlsCallback;
49 /** @private */
50 this.onHandshakeDoneCallback_ = onHandshakeDoneCallback;
51 /** @private */
52 this.onErrorCallback_ = onErrorCallback;
53
54 /** @private */
55 this.state_ = remoting.XmppLoginHandler.State.INIT;
56 /** @private */
57 this.jid_ = '';
58
59 /** @type {remoting.XmppStreamParser} @private */
60 this.streamParser_ = null;
61 }
62
63 /**
64 * States the handshake goes through. States are iterated from INIT to DONE
65 * sequentially, except for ERROR state which may be accepted at any point.
66 *
67 * Following messages are sent/received in each state:
68 * INIT
69 * client -> server: Stream header
70 * START_SENT
71 * client <- server: Stream header with list of supported features which
72 * should include starttls.
73 * client -> server: <starttls>
74 * STARTTLS_SENT
75 * client <- server: <proceed>
76 * STARTING_TLS
77 * TLS handshake
78 * client -> server: Stream header
79 * START_SENT_AFTER_TLS
80 * client <- server: Stream header with list of supported authentication
81 * methods which is expected to include X-OAUTH2
82 * client -> server: <auth> message with the OAuth2 token.
83 * AUTH_SENT
84 * client <- server: <success> or <failure>
85 * client -> server: Stream header
86 * AUTH_ACCEPTED
87 * client <- server: Stream header with list of features that should
88 * include <bind>.
89 * client -> server: <bind>
90 * BIND_SENT
91 * client <- server: <bind> result with JID.
92 * client -> server: <iq><session/></iq> to start the session
93 * SESSION_IQ_SENT
94 * client <- server: iq result
95 * DONE
96 *
97 * @enum {number}
98 */
99 remoting.XmppLoginHandler.State = {
100 INIT: 0,
101 START_SENT: 1,
102 STARTTLS_SENT: 2,
103 STARTING_TLS: 3,
104 START_SENT_AFTER_TLS: 4,
105 AUTH_SENT: 5,
106 AUTH_ACCEPTED: 6,
107 BIND_SENT: 7,
108 SESSION_IQ_SENT: 8,
109 DONE: 9,
110 ERROR: 10
111 };
112
113 remoting.XmppLoginHandler.prototype.start = function() {
114 this.state_ = remoting.XmppLoginHandler.State.START_SENT;
115 this.startStream_();
116 }
117
118 /** @param {ArrayBuffer} data */
119 remoting.XmppLoginHandler.prototype.onDataReceived = function(data) {
120 base.debug.assert(this.state_ != remoting.XmppLoginHandler.State.INIT &&
121 this.state_ != remoting.XmppLoginHandler.State.DONE &&
122 this.state_ != remoting.XmppLoginHandler.State.ERROR);
123
124 this.streamParser_.appendData(data);
125 }
126
127 /**
128 * @param {Element} stanza
129 * @private
130 */
131 remoting.XmppLoginHandler.prototype.onStanza_ = function(stanza) {
132 switch (this.state_) {
133 case remoting.XmppLoginHandler.State.START_SENT:
134 if (stanza.querySelector('features>starttls')) {
135 this.sendMessageCallback_(
136 '<starttls xmlns="urn:ietf:params:xml:ns:xmpp-tls"/>');
137 this.state_ = remoting.XmppLoginHandler.State.STARTTLS_SENT;
138 } else {
139 this.onError_(remoting.Error.UNEXPECTED, "Server doesn't support TLS.");
140 }
141 break;
142
143 case remoting.XmppLoginHandler.State.STARTTLS_SENT:
144 if (stanza.localName == "proceed") {
145 this.state_ = remoting.XmppLoginHandler.State.STARTING_TLS;
146 this.startTlsCallback_();
147 } else {
148 this.onError_(remoting.Error.UNEXPECTED,
149 "Failed to start TLS: " +
150 (new XMLSerializer().serializeToString(stanza)));
151 }
152 break;
153
154 case remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS:
155 var mechanisms = Array.prototype.map.call(
156 stanza.querySelectorAll('features>mechanisms>mechanism'),
157 /** @param {Element} m */
158 function(m) { return m.textContent; });
159 if (mechanisms.indexOf("X-OAUTH2")) {
160 this.onError_(remoting.Error.UNEXPECTED,
161 "OAuth2 is not supported by the server.");
162 return;
163 }
164
165 var cookie = window.btoa("\0" + this.username_ + "\0" + this.authToken_);
166
167 this.state_ = remoting.XmppLoginHandler.State.AUTH_SENT;
168 this.sendMessageCallback_(
169 '<auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl" ' +
170 'mechanism="X-OAUTH2" auth:service="oauth2" ' +
171 'auth:allow-generated-jid="true" ' +
172 'auth:client-uses-full-bind-result="true" ' +
173 'auth:allow-non-google-login="true" ' +
174 'xmlns:auth="http://www.google.com/talk/protocol/auth">' +
175 cookie +
176 '</auth>');
177 break;
178
179 case remoting.XmppLoginHandler.State.AUTH_SENT:
180 if (stanza.localName == 'success') {
181 this.state_ = remoting.XmppLoginHandler.State.AUTH_ACCEPTED;
182 this.startStream_();
183 } else {
184 this.onError_(remoting.Error.AUTHENTICATION_FAILED,
185 'Failed to authenticate: ' +
186 (new XMLSerializer().serializeToString(stanza)));
187 }
188 break;
189
190 case remoting.XmppLoginHandler.State.AUTH_ACCEPTED:
191 if (stanza.querySelector('features>bind')) {
192 this.sendMessageCallback_(
193 '<iq type="set" id="0">' +
194 '<bind xmlns="urn:ietf:params:xml:ns:xmpp-bind">' +
195 '<resource>chromoting</resource>'+
196 '</bind>' +
197 '</iq>');
198 this.state_ = remoting.XmppLoginHandler.State.BIND_SENT;
199 } else {
200 this.onError_(remoting.Error.UNEXPECTED,
201 "Server doesn't support bind after authentication.");
202 }
203 break;
204
205 case remoting.XmppLoginHandler.State.BIND_SENT:
206 var jidElement = stanza.querySelector('iq>bind>jid');
207 if (stanza.getAttribute('id') != '0' ||
208 stanza.getAttribute('type') != 'result' || !jidElement) {
209 this.onError_(remoting.Error.UNEXPECTED,
210 'Received unexpected response to bind: ' +
211 (new XMLSerializer().serializeToString(stanza)));
212 return;
213 }
214 this.jid_ = jidElement.textContent;
215 this.sendMessageCallback_(
216 '<iq type="set" id="1">' +
217 '<session xmlns="urn:ietf:params:xml:ns:xmpp-session"/>' +
218 '</iq>');
219 this.state_ = remoting.XmppLoginHandler.State.SESSION_IQ_SENT;
220 break;
221
222 case remoting.XmppLoginHandler.State.SESSION_IQ_SENT:
223 if (stanza.getAttribute('id') != '1' ||
224 stanza.getAttribute('type') != 'result') {
225 this.onError_(remoting.Error.UNEXPECTED,
226 'Failed to start session: ' +
227 (new XMLSerializer().serializeToString(stanza)));
228 return;
229 }
230 this.state_ = remoting.XmppLoginHandler.State.DONE;
231 this.onHandshakeDoneCallback_(this.jid_, this.streamParser_);
232 break;
233
234 default:
235 base.debug.assert(false);
236 break;
237 }
238 }
239
240 remoting.XmppLoginHandler.prototype.onTlsStarted = function() {
241 base.debug.assert(this.state_ ==
242 remoting.XmppLoginHandler.State.STARTING_TLS);
243 this.state_ = remoting.XmppLoginHandler.State.START_SENT_AFTER_TLS;
244 this.startStream_();
245 };
246
247 /**
248 * @param {string} text
249 * @private
250 */
251 remoting.XmppLoginHandler.prototype.onParserError_ = function(text) {
252 this.onError_(remoting.Error.UNEXPECTED, text);
253 }
254
255 /**
256 * @private
257 */
258 remoting.XmppLoginHandler.prototype.startStream_ = function() {
259 this.sendMessageCallback_('<stream:stream to="' + this.server_ +
260 '" version="1.0" xmlns="jabber:client" ' +
261 'xmlns:stream="http://etherx.jabber.org/streams">');
262 this.streamParser_ = new remoting.XmppStreamParser();
263 this.streamParser_.setCallbacks(this.onStanza_.bind(this),
264 this.onParserError_.bind(this));
265 }
266
267 /**
268 * @param {remoting.Error} error
269 * @param {string} text
270 * @private
271 */
272 remoting.XmppLoginHandler.prototype.onError_ = function(error, text) {
273 if (this.state_ != remoting.XmppLoginHandler.State.ERROR) {
274 this.onErrorCallback_(error, text);
275 this.state_ = remoting.XmppLoginHandler.State.ERROR;
276 } else {
277 console.error(text);
278 }
279 }
OLDNEW
« no previous file with comments | « remoting/webapp/xmpp_connection.js ('k') | remoting/webapp/xmpp_stream_parser.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698