diff options
| author | ReAnzu <anzu@reanzu.com> | 2018-02-25 04:44:02 -0600 |
|---|---|---|
| committer | rr- <rr-@sakuya.pl> | 2018-03-25 22:23:29 +0200 |
| commit | 2a69f0193f9e5aa451e0dbcb8c89070f3e49ffa5 (patch) | |
| tree | bda640614ae711e8fef0e0463f118f85ef83ac0e /client/js/api.js | |
| parent | e35e70992736340bff3995ca6f425baf2f431e2f (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.js | 94 |
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) |