The authentication in damas-core is based upon the JSON Web Token (RFC 7519) authentication and secure transmission. You can find some resources here:
- JSON Web Token - Wikipedia page
- jwt.io - and Introduction
- npm JSONWebToken - JWT module for NodeJS
The implementation of this RFC is found in the jwt damas-core extension. Please find below some documentation about it.
The tokens are delivered by the server using the signIn operation to authenticate users.
The default value for tokens' lifespan can be set in the server's conf.json under extensions.jwt.exp
, and its value is "1d" (1 day) by default. See the syntax and examples here. This value can also be specified during signIn, using the expiresIn
parameter, in order to retrieve a token with the desired lifespan. A value of "0" ask for a token with an unlimited lifespan (see #237). By changing password, this revokes previously obtained tokens.
Changing the user's password revokes every tokens previously created. This is because the salt used to generate the tokens is also made of the user's hashed password value. This way, we provide a simple and secure way to revoke every tokens at once for a user without adding more complex operations.
The tokens are encrypted using a salt composed of a secret passphrase specified on the server under extension.jwt.secret
, and the users' hashed passwords. If we change either one, every previously obtained tokens is revoked.
The users are regular elements we can create, update or delete using the API.
{
"_id" : "usr/axel"
"class": "user",
"lastActivity": 1561237341643,
"lastlogin": 1561237340643,
"password" : "55ae0b1ed81e88357d77d0e9",
"username": "axel"
}
Some keys can be added to users elements by the server, depending on its configuration: lastActivity
, lastLogin
.
The users' passwords are stored in the database under a password
key for each user element. The passwords are stored as encoded strings, using the sha1
or md5
hash algorithms. You can specify the preferred algorithm in extensions.jwt.passwordHashAlgorithm
. The algorithm is automatically detected at signIn (using the hash length) so a mix of methods could exist in the database (this is useful to migrate or merge multiple user databases that use different hash algorithms).
To create a new user using Python:
import damas
import hashlib
project = http_connection("http://localhost")
p = hashlib.sha1()
p.update('johnpassword')
project.create({"username":"john", "password": p.hexdigest(), "email":"[email protected]" })
Delete this user (given his username):
project.delete(project.search("username:john")[0])
Request a token for a user:
if project.signIn("john","johnpassword"):
# The token is automatically used in further API calls
new_node = project.create({"key":"value")
else:
print "Invalid username or password"
Ask the server if the current authentication is still valid and didn't expire:
# Python
if project.verify():
print "ok"
else:
print "token expired"
Different types of permissions are available:
- hard-coded permissions for each /api/ operation based on the current user's
class
key (in server-nodejs/routes/perms-tools.js) - extension for update permissions based on the modified key name and the user's
class
key. See restricted_keys extension. - read permissions based on the
author
key. See conf.jsonauthorMode
directive.
The available user classes are: admin
editor
user
guest
.
Operation | guest | user | editor | admin |
---|---|---|---|---|
create | x | x | x | |
read | x | x | x | x |
update | k | x | x | |
delete | x | x | ||
graph | x | x | x | x |
search | x | x | x | x |
search_mongo | x | x | x | x |
# Python
project.token['username'] # The user name used to log in
project.token['token'] # The actual encrypted json web token
project.token['token_exp'] # The time at which the token expires (Unix timestamp in seconds)
project.token['token_iat'] # The time when the token was generated
project.token['_id'] # The user node id
The jwt_delegate extension can be additionally used to centralize the authentication on a different server. When an user signs in, instead of authenticating him against the local database, the extension creates a new request that is sent to the delegation server. Once the user is authenticated, its element is copied in the local server, as if the user was authenticated locally.
- Request an access token from the server:
$ curl https://localhost/api/signIn -d "username=remyla&password=yyy" > /tmp/token
- Read the token:
$ cat /tmp/token
{"_id":"56029d03dff07e50a860a09d","username":"remyla","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NjAyOWQwM2RmZjA3ZTUwYTg2MGEwOWQiLCJ1x2VybmFtZSI6InJlbXlsYSIsImlhdCI6MTQ1NDA3ODY1MiwiZXhwIjoxNDU0MTY1MDUyfQ.5AhJIh6ReeS2y6H0Mpcx8fJralsTDSidJAniuaJiVP8","token_exp":1454165052,"token_iat":1454078652}
- Use the token:
$ curl https://localhost/api/verify -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJfaWQiOiI1NjAyOWQwM2RmZjA3ZTUwYTg2MGEwOWQiLCJ1x2VybmFtZSI6InJlbXlsYSIsImlhdCI6MTQ1NDA3ODY1MiwiZXhwIjoxNDU0MTY1MDUyfQ.5AhJIh6ReeS2y6H0Mpcx8fJralsTDSidJAniuaJiVP8"
{"_id":"56029d03dff07e50a860a09d","username":"remyla","iat":1454078652,"exp":1454165052}