You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-10 21:23:02 +03:00
Move /lib to /src
This commit is contained in:
228
src/interactive-auth.js
Normal file
228
src/interactive-auth.js
Normal file
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
Copyright 2016 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";
|
||||
|
||||
/** @module interactive-auth */
|
||||
var q = require("q");
|
||||
|
||||
var utils = require("./utils");
|
||||
|
||||
/**
|
||||
* Abstracts the logic used to drive the interactive auth process.
|
||||
*
|
||||
* <p>Components implementing an interactive auth flow should instantiate one of
|
||||
* these, passing in the necessary callbacks to the constructor. They should
|
||||
* then call attemptAuth, which will return a promise which will resolve or
|
||||
* reject when the interactive-auth process completes.
|
||||
*
|
||||
* <p>Meanwhile, calls will be made to the startAuthStage and doRequest
|
||||
* callbacks, and information gathered from the user can be submitted with
|
||||
* submitAuthDict.
|
||||
*
|
||||
* @constructor
|
||||
* @alias module:interactive-auth
|
||||
*
|
||||
* @param {object} opts options object
|
||||
*
|
||||
* @param {object?} opts.authData error response from the last request. If
|
||||
* null, a request will be made with no auth before starting.
|
||||
*
|
||||
* @param {function(object?): module:client.Promise} opts.doRequest
|
||||
* called with the new auth dict to submit the request. Should return a
|
||||
* promise which resolves to the successful response or rejects with a
|
||||
* MatrixError.
|
||||
*
|
||||
* @param {function(string, object?)} opts.startAuthStage
|
||||
* called to ask the UI to start a particular auth stage. The arguments
|
||||
* are: the login type (eg m.login.password); and (if the last request
|
||||
* returned an error), an error object, with fields 'errcode' and 'error'.
|
||||
*
|
||||
*/
|
||||
function InteractiveAuth(opts) {
|
||||
this._data = opts.authData;
|
||||
this._requestCallback = opts.doRequest;
|
||||
this._startAuthStageCallback = opts.startAuthStage;
|
||||
this._completionDeferred = null;
|
||||
}
|
||||
|
||||
InteractiveAuth.prototype = {
|
||||
/**
|
||||
* begin the authentication process.
|
||||
*
|
||||
* @return {module:client.Promise} which resolves to the response on success,
|
||||
* or rejects with the error on failure.
|
||||
*/
|
||||
attemptAuth: function() {
|
||||
this._completionDeferred = q.defer();
|
||||
|
||||
if (!this._data) {
|
||||
this._doRequest(null);
|
||||
} else {
|
||||
this._startNextAuthStage();
|
||||
}
|
||||
|
||||
return this._completionDeferred.promise;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the auth session ID
|
||||
*
|
||||
* @return {string} session id
|
||||
*/
|
||||
getSessionId: function() {
|
||||
return this._data ? this._data.session : undefined;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the server params for a given stage
|
||||
*
|
||||
* @param {string} login type for the stage
|
||||
* @return {object?} any parameters from the server for this stage
|
||||
*/
|
||||
getStageParams: function(loginType) {
|
||||
var params = {};
|
||||
if (this._data && this._data.params) {
|
||||
params = this._data.params;
|
||||
}
|
||||
return params[loginType];
|
||||
},
|
||||
|
||||
/**
|
||||
* submit a new auth dict and fire off the request. This will either
|
||||
* make attemptAuth resolve/reject, or cause the startAuthStage callback
|
||||
* to be called for a new stage.
|
||||
*
|
||||
* @param {object} authData new auth dict to send to the server. Should
|
||||
* include a `type` propterty denoting the login type, as well as any
|
||||
* other params for that stage.
|
||||
*/
|
||||
submitAuthDict: function(authData) {
|
||||
if (!this._completionDeferred) {
|
||||
throw new Error("submitAuthDict() called before attemptAuth()");
|
||||
}
|
||||
|
||||
// use the sessionid from the last request.
|
||||
var auth = {
|
||||
session: this._data.session,
|
||||
};
|
||||
utils.extend(auth, authData);
|
||||
|
||||
this._doRequest(auth);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fire off a request, and either resolve the promise, or call
|
||||
* startAuthStage.
|
||||
*
|
||||
* @private
|
||||
* @param {object?} auth new auth dict, including session id
|
||||
*/
|
||||
_doRequest: function(auth) {
|
||||
var self = this;
|
||||
|
||||
// hackery to make sure that synchronous exceptions end up in the catch
|
||||
// handler (without the additional event loop entailed by q.fcall or an
|
||||
// extra q().then)
|
||||
var prom;
|
||||
try {
|
||||
prom = this._requestCallback(auth);
|
||||
} catch (e) {
|
||||
prom = q.reject(e);
|
||||
}
|
||||
|
||||
prom.then(
|
||||
function(result) {
|
||||
console.log("result from request: ", result);
|
||||
self._completionDeferred.resolve(result);
|
||||
}, function(error) {
|
||||
if (error.httpStatus !== 401 || !error.data || !error.data.flows) {
|
||||
// doesn't look like an interactive-auth failure. fail the whole lot.
|
||||
throw error;
|
||||
}
|
||||
self._data = error.data;
|
||||
self._startNextAuthStage();
|
||||
}
|
||||
).catch(this._completionDeferred.reject).done();
|
||||
},
|
||||
|
||||
/**
|
||||
* Pick the next stage and call the callback
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_startNextAuthStage: function() {
|
||||
var nextStage = this._chooseStage();
|
||||
if (!nextStage) {
|
||||
throw new Error("No incomplete flows from the server");
|
||||
}
|
||||
|
||||
var stageError = null;
|
||||
if (this._data.errcode || this._data.error) {
|
||||
stageError = {
|
||||
errcode: this._data.errcode || "",
|
||||
error: this._data.error || "",
|
||||
};
|
||||
}
|
||||
this._startAuthStageCallback(nextStage, stageError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Pick the next auth stage
|
||||
*
|
||||
* @private
|
||||
* @return {string?} login type
|
||||
*/
|
||||
_chooseStage: function() {
|
||||
var flow = this._chooseFlow();
|
||||
console.log("Active flow => %s", JSON.stringify(flow));
|
||||
var nextStage = this._firstUncompletedStage(flow);
|
||||
console.log("Next stage: %s", nextStage);
|
||||
return nextStage;
|
||||
},
|
||||
|
||||
/**
|
||||
* Pick one of the flows from the returned list
|
||||
*
|
||||
* @private
|
||||
* @return {object} flow
|
||||
*/
|
||||
_chooseFlow: function() {
|
||||
var flows = this._data.flows || [];
|
||||
// always use the first flow for now
|
||||
return flows[0];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the first uncompleted stage in the given flow
|
||||
*
|
||||
* @private
|
||||
* @param {object} flow
|
||||
* @return {string} login type
|
||||
*/
|
||||
_firstUncompletedStage: function(flow) {
|
||||
var completed = (this._data || {}).completed || [];
|
||||
for (var i = 0; i < flow.stages.length; ++i) {
|
||||
var stageType = flow.stages[i];
|
||||
if (completed.indexOf(stageType) === -1) {
|
||||
return stageType;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/** */
|
||||
module.exports = InteractiveAuth;
|
Reference in New Issue
Block a user