summaryrefslogtreecommitdiff
path: root/client/js/api.js
diff options
context:
space:
mode:
authorReAnzu <anzu@reanzu.com>2018-02-25 04:44:02 -0600
committerrr- <rr-@sakuya.pl>2018-03-25 22:23:29 +0200
commit2a69f0193f9e5aa451e0dbcb8c89070f3e49ffa5 (patch)
treebda640614ae711e8fef0e0463f118f85ef83ac0e /client/js/api.js
parente35e70992736340bff3995ca6f425baf2f431e2f (diff)
server/auth: add token authentication
* Users are only authenticated against their password on login, and to retrieve a token * Passwords are wiped from the GUI frontend and cookies after login and token retrieval * Tokens are revoked at the end of the session/logout * If the user chooses the "remember me" option, the token is stored in the cookie * Tokens correctly delete themselves on logout * Tokens can expire at user-specified date * Tokens have their last usage time * Tokens can have user defined descriptions * Users can manage login tokens in their account settings
Diffstat (limited to 'client/js/api.js')
-rw-r--r--client/js/api.js94
1 files changed, 88 insertions, 6 deletions
diff --git a/client/js/api.js b/client/js/api.js
index abfeb6f..3623045 100644
--- a/client/js/api.js
+++ b/client/js/api.js
@@ -15,6 +15,7 @@ class Api extends events.EventTarget {
this.user = null;
this.userName = null;
this.userPassword = null;
+ this.token = null;
this.cache = {};
this.allRanks = [
'anonymous',
@@ -87,16 +88,16 @@ class Api extends events.EventTarget {
loginFromCookies() {
const auth = cookies.getJSON('auth');
- return auth && auth.user && auth.password ?
- this.login(auth.user, auth.password, true) :
+ return auth && auth.user && auth.token ?
+ this.loginWithToken(auth.user, auth.token, true) :
Promise.resolve();
}
- login(userName, userPassword, doRemember) {
+ loginWithToken(userName, token, doRemember) {
this.cache = {};
return new Promise((resolve, reject) => {
this.userName = userName;
- this.userPassword = userPassword;
+ this.token = token;
this.get('/user/' + userName + '?bump-login=true')
.then(response => {
const options = {};
@@ -105,8 +106,70 @@ class Api extends events.EventTarget {
}
cookies.set(
'auth',
- {'user': userName, 'password': userPassword},
+ {'user': userName, 'token': token},
+ options);
+ this.user = response;
+ resolve();
+ this.dispatchEvent(new CustomEvent('login'));
+ }, error => {
+ reject(error);
+ this.logout();
+ });
+ });
+ }
+
+ createToken(userName, options) {
+ let userTokenRequest = {
+ enabled: true,
+ note: 'Web Login Token'
+ };
+ if (typeof options.expires !== 'undefined') {
+ userTokenRequest.expirationTime = new Date().addDays(options.expires).toISOString()
+ }
+ return new Promise((resolve, reject) => {
+ this.post('/user-token/' + userName, userTokenRequest)
+ .then(response => {
+ cookies.set(
+ 'auth',
+ {'user': userName, 'token': response.token},
+ options);
+ this.userName = userName;
+ this.token = response.token;
+ this.userPassword = null;
+ }, error => {
+ reject(error);
+ });
+ });
+ }
+
+ deleteToken(userName, userToken) {
+ return new Promise((resolve, reject) => {
+ this.delete('/user-token/' + userName + '/' + userToken, {})
+ .then(response => {
+ const options = {};
+ cookies.set(
+ 'auth',
+ {'user': userName, 'token': null},
options);
+ resolve();
+ }, error => {
+ reject(error);
+ });
+ });
+ }
+
+ login(userName, userPassword, doRemember) {
+ this.cache = {};
+ return new Promise((resolve, reject) => {
+ this.userName = userName;
+ this.userPassword = userPassword;
+ this.get('/user/' + userName + '?bump-login=true')
+ .then(response => {
+ const options = {};
+ if (doRemember) {
+ options.expires = 365;
+ }
+ this.createToken(this.userName, options);
this.user = response;
resolve();
this.dispatchEvent(new CustomEvent('login'));
@@ -118,9 +181,20 @@ class Api extends events.EventTarget {
}
logout() {
+ let self = this;
+ this.deleteToken(this.userName, this.token)
+ .then(response => {
+ self._logout();
+ }, error => {
+ self._logout();
+ });
+ }
+
+ _logout() {
this.user = null;
this.userName = null;
this.userPassword = null;
+ this.token = null;
this.dispatchEvent(new CustomEvent('logout'));
}
@@ -137,6 +211,10 @@ class Api extends events.EventTarget {
}
}
+ isCurrentAuthToken(userToken) {
+ return userToken.token === this.token;
+ }
+
_getFullUrl(url) {
const fullUrl =
(config.apiUrl + '/' + url).replace(/([^:])\/+/g, '$1/');
@@ -258,7 +336,11 @@ class Api extends events.EventTarget {
}
try {
- if (this.userName && this.userPassword) {
+ if (this.userName && this.token) {
+ req.auth = null;
+ req.set('Authorization', 'Token '
+ + new Buffer(this.userName + ":" + this.token).toString('base64'))
+ } else if (this.userName && this.userPassword) {
req.auth(
this.userName,
encodeURIComponent(this.userPassword)