From 483c32cfbfd3debadbec447621302dd7d694f849 Mon Sep 17 00:00:00 2001 From: ReAnzu Date: Sun, 25 Feb 2018 04:44:02 -0600 Subject: [PATCH] User Token Authentication * Users are only authenticated against their password on login, and to retrieve a token. * Passwords are wiped from the app 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 * A user interface to revoke tokens will be added * Tokens correctly delete themselves on logout * API documentation updated for the new user-token endpoints * Added a Manage tokens tab to the user panel * Added bullet point about the token authentication for the API * Added tests for new endpoints and tests against authentication middleware --- .gitignore | 3 + API.md | 160 +++++++++++++++++- README.md | 1 + client/css/user-view.styl | 20 ++- client/html/user.tpl | 3 + client/html/user_tokens.tpl | 29 ++++ client/js/api.js | 85 +++++++++- client/js/controllers/user_controller.js | 78 ++++++++- client/js/models/user_token.js | 76 +++++++++ client/js/views/user_tokens_view.js | 86 ++++++++++ client/js/views/user_view.js | 12 +- config.yaml.dist | 9 + server/szurubooru/api/__init__.py | 1 + server/szurubooru/api/user_token_api.py | 60 +++++++ server/szurubooru/func/auth.py | 9 + server/szurubooru/func/user_tokens.py | 86 ++++++++++ server/szurubooru/func/users.py | 18 +- server/szurubooru/middleware/authenticator.py | 30 +++- .../a39c7f98a7fa_add_user_token_table.py | 35 ++++ server/szurubooru/model/__init__.py | 4 +- server/szurubooru/model/user.py | 19 +++ server/szurubooru/rest/__init__.py | 1 + .../tests/api/test_user_token_creating.py | 29 ++++ .../tests/api/test_user_token_deleting.py | 29 ++++ .../tests/api/test_user_token_retrieving.py | 31 ++++ .../tests/api/test_user_token_updating.py | 41 +++++ server/szurubooru/tests/conftest.py | 19 ++- server/szurubooru/tests/func/test_auth.py | 10 ++ .../szurubooru/tests/func/test_user_tokens.py | 72 ++++++++ .../szurubooru/tests/middleware/__init__.py | 0 .../tests/middleware/test_authenticator.py | 48 ++++++ .../szurubooru/tests/model/test_user_token.py | 14 ++ 32 files changed, 1086 insertions(+), 32 deletions(-) create mode 100644 client/html/user_tokens.tpl create mode 100644 client/js/models/user_token.js create mode 100644 client/js/views/user_tokens_view.js create mode 100644 server/szurubooru/api/user_token_api.py create mode 100644 server/szurubooru/func/user_tokens.py create mode 100644 server/szurubooru/migrations/versions/a39c7f98a7fa_add_user_token_table.py create mode 100644 server/szurubooru/tests/api/test_user_token_creating.py create mode 100644 server/szurubooru/tests/api/test_user_token_deleting.py create mode 100644 server/szurubooru/tests/api/test_user_token_retrieving.py create mode 100644 server/szurubooru/tests/api/test_user_token_updating.py create mode 100644 server/szurubooru/tests/func/test_user_tokens.py create mode 100644 server/szurubooru/tests/middleware/__init__.py create mode 100644 server/szurubooru/tests/middleware/test_authenticator.py create mode 100644 server/szurubooru/tests/model/test_user_token.py diff --git a/.gitignore b/.gitignore index a683cefa..30cd78cf 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ config.yaml */*_modules/ .coverage .cache +__pycache__ +.idea/ +*.iml \ No newline at end of file diff --git a/API.md b/API.md index 62e0eb22..ae2bb6b3 100644 --- a/API.md +++ b/API.md @@ -7,6 +7,7 @@ 1. [General rules](#general-rules) - [Authentication](#authentication) + - [User token authentication](#user-token-authentication) - [Basic requests](#basic-requests) - [File uploads](#file-uploads) - [Error handling](#error-handling) @@ -56,6 +57,11 @@ - [Updating user](#updating-user) - [Getting user](#getting-user) - [Deleting user](#deleting-user) + - User Tokens + - [Listing tokens](#listing-tokens) + - [Creating token](#creating-token) + - [Updating token](#updating-token) + - [Deleting token](#deleting-token) - Password reset - [Password reset - step 1: mail request](#password-reset---step-2-confirmation) - [Password reset - step 2: confirmation](#password-reset---step-2-confirmation) @@ -70,6 +76,7 @@ - [User](#user) - [Micro user](#micro-user) + - [User token](#user-token) - [Tag category](#tag-category) - [Tag](#tag) - [Micro tag](#micro-tag) @@ -91,16 +98,35 @@ ## Authentication Authentication is achieved by means of [basic HTTP -auth](https://en.wikipedia.org/wiki/Basic_access_authentication). For this -reason, it is recommended to connect through HTTPS. There are no sessions, so -every privileged request must be authenticated. Available privileges depend on -the user's rank. The way how rank translates to privileges is defined in the -server's configuration. +auth](https://en.wikipedia.org/wiki/Basic_access_authentication) or through the +use of [user token authentication](#user-token-authentication). For this reason, +it is recommended to connect through HTTPS. There are no sessions, so every +privileged request must be authenticated. Available privileges depend on the +user's rank. The way how rank translates to privileges is defined in the server's +configuration. It is recommended to add `?bump-login` GET parameter to the first request in a client "session" (where the definition of a session is up to the client), so that the user's last login time is kept up to date. +## User token authentication + +User token authentication works similarly to [basic HTTP +auth](https://en.wikipedia.org/wiki/Basic_access_authentication). Because it +operates similarly to ***basic HTTP auth*** it is still recommended to connect +through HTTPS. The authorization header uses the type of Token and the username +and token are encoded as Base64 and sent as the second parameter. + +Example header for user1:token-is-more-secure +``` +Authorization: Token dXNlcjE6dG9rZW4taXMtbW9yZS1zZWN1cmU= +``` + +The benefit of token authentication is that beyond the initial login to acquire +the first token, there is no need to transmit the user password in plaintext via +basic auth. Additionally tokens can be revoked at anytime allowing a cleaner +interface for isolating clients from user credentials. + ## Basic requests Every request must use `Content-Type: application/json` and `Accept: @@ -1469,6 +1495,104 @@ data. Deletes existing user. +## Listing tokens +- **Request** + + `GET /user-tokens/` + +- **Output** + + An [unpaged search result resource](#unpaged-search-result), for which + `` is a [user token resource](#user-token). + +- **Errors** + + - privileges are too low + +- **Description** + + Searches for users tokens for the currently logged in user. + +## Creating token +- **Request** + + `POST /user-token` + +- **Input** + + ```json5 + {} + ``` + +- **Output** + + A [user token resource](#user-token). + +- **Errors** + + - privileges are too low + +- **Description** + + Creates a new user token that can be used for authentication of api + endpoints instead of a password. + +## Updating user +- **Request** + + `PUT /user-token/` + +- **Input** + + ```json5 + { + "version": , + "enabled": , // optional + } + ``` + +- **Output** + + A [user token resource](#user-token). + +- **Errors** + + - the version is outdated + - the user token does not exist + - privileges are too low + +- **Description** + + Updates an existing user token using specified parameters. All fields + except the [`version`](#versioning) are optional - update concerns only + provided fields. + +## Deleting token +- **Request** + + `DELETE /user-token/` + +- **Input** + + ```json5 + {} + ``` + +- **Output** + + ```json5 + {} + ``` + +- **Errors** + + - the token does not exist + - privileges are too low + +- **Description** + + Deletes existing user token. + ## Password reset - step 1: mail request - **Request** @@ -1701,6 +1825,32 @@ A single user. A [user resource](#user) stripped down to `name` and `avatarUrl` fields. +## User token +**Description** + +A single user token. + +**Structure** + +```json5 +{ + "user": , + "token": , + "enabled": , + "version": , + "creationTime": , + "lastEditTime": , +} +``` + +**Field meaning** +- ``: micro user. See [micro user](#micro-user). +- ``: the token that can be used to authenticate the user. +- ``: whether the token is still valid for authentication. +- ``: resource version. See [versioning](#versioning). +- ``: time the user token was created , formatted as per RFC 3339. +- ``: time the user token was edited, formatted as per RFC 3339. + ## Tag category **Description** diff --git a/README.md b/README.md index ceb91093..3b5c4e44 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ scrubbing](http://sjp.pwn.pl/sjp/;2527372). It is pronounced as *shoorubooru*. - Post comments - Post notes / annotations, including arbitrary polygons - Rich JSON REST API ([see documentation](https://github.com/rr-/szurubooru/blob/master/API.md)) +- Token based authentication for clients - Rich search system - Rich privilege system - Autocomplete in search and while editing tags diff --git a/client/css/user-view.styl b/client/css/user-view.styl index 12cba75e..46f3044a 100644 --- a/client/css/user-view.styl +++ b/client/css/user-view.styl @@ -1,6 +1,6 @@ #user width: 100% - max-width: 35em + max-width: 45em nav.text-nav margin-bottom: 1.5em @@ -37,6 +37,24 @@ height: 1px clear: both + #user-tokens + .token-flex-container + width: 100% + display: flex; + flex-direction column; + padding-bottom: 0.5em; + + .token-flex-row + display: flex; + flex-direction: row; + justify-content: space-between; + padding-top: 0.25em; + padding-bottom: 0.25em; + border-bottom: black solid 1px; + + form + width: auto; + #user-delete form width: 100% diff --git a/client/html/user.tpl b/client/html/user.tpl index 28e34e67..cbf4f18f 100644 --- a/client/html/user.tpl +++ b/client/html/user.tpl @@ -6,6 +6,9 @@ --><% if (ctx.canEditAnything) { %>
  • '>Account settings
  • <% } %><% if (ctx.canListTokens) { %>
  • '>Manage tokens
  • <% } %><% if (ctx.canDelete) { %>
  • '>Account deletion
  • <% } %>