Tokens
The EMu REST API uses Java Web Tokens (JWT) for authentication. Each request supplies an encoded JWT as a Bearer token in the HTTP Authorization header. When a request is received the token is extracted and checked to ensure it is valid, that is it was issued by the shim and has not expired. The only request not checked is POST /{tenant}/tokens as it is used to generate a token.
Tokens have an expiry time associated with them. The default expiry time is defined on a per tenant basis and is the timeout property found in the tenant’s settings in the restapi/conf/tenants.conf
file. A typical expiry time is 30 minutes. By default the expiry time for a token is reset each time the token is used with a valid request. Hence the expiry time is the number of minutes that the token is not used before it is invalid. Think of it as an idle timeout. Once a token’s expiry time is reached the token is removed from the shim. The token timeout value and whether a token’s expiry time is renewed with each request may be overriden when requesting a token’s creation.
The JSON below shows the information available about a token:
{
"id": "emu:/museum/tokens/ca343114-7aa2-45a5-b3cf-9cfb1213a84e",
"version": 1,
"data": {
"iat": 1619581725,
"exp": 1619605035,
"iss": "emu:/shared/tenants/museum",
"sub": "emu:/museum/users/barney",
"gid": "emu:/museum/roles/Admin",
"jti": "emu:/museum/tokens/ca343114-7aa2-45a5-b3cf-9cfb1213a84e",
"lid": "en_GB",
"wfg": [
"Admin",
"Conservation",
"Archives"
]
}
}
A token (JWT) has the following fields defined:
Name | Description |
---|---|
iss | Token issuer. The issuer is a URI identifying the tenant associated with the token. |
exp | Token expiry time. A value representing the number of seconds from 1/1/1970 at which the token expires. Once the expiry date passes the token is no longer valid. |
iat | Time at which the token was issues. The value is interpreted the same as that for exp. |
sub | Token’s subject. The subject is a URI identifying the user associated with the token. |
gid | Token’s group. The group is a URI identifying the role associated with the token. |
jti | Token’s unique identifier. The identifier is a URI that can be used to retrieve the token. |
lid | Token’s locale. The locale to use for error messages generated when this token is used. |
wfg | Token’s workflow groups. A JSON array containing a list of groups associated with the token. |
The following requests are available for the tokens resource.
Create
POST /{tenant}/tokens
The POST method creates a token. A request must pass in one of the following for authentication:
- username/password combination
- OpenID Connect token
- passcode
If the create request is successful then a 201
status is returned and the payload contains the token information. The Location: header contains a URL used to retrieve the token and the Authorization: header contains the encoded token itself.
Each authentication type is explained below.
username/password
If a username/password is sent then the EMu server will authenticate the credentials against the texpress PAM service. The service can be configured to use a number of different authentication sub-systems (e.g. AD, NIS, passwd file, LDAP, etc). The payload for a username/password request should contain:
{
"username": "{username}",
"password": "{password}",
"timeout": {minutes},
"renew": {true|false}
}
The available properties are:
- username - login name to use for authentication
- password - password for username
- timeout - idle/elapsed time in minutes before expiry of the created token
- renew - whether new tokens should be generated with each request
The renew property is a boolean indicating whether the expiry time for the token should be updated with each request. A value of true
will result in a new token being generated with each request with an updated expiry time. In this case the timeout value is the idle time before a token is expired and dropped. If the value is false
then the token expiry time is not updated with each request and the same token is returned. A false
setting forces a token to expire after a fixed amount of time regardless of requests made. If renew is false
a token can always be updated using an explicit update request.
Both the timeout and renew properties are optional. If the timeout property is not supplied then the default timeout value for the tenant is used. If the renew property is not supplied then the default value is true
.
OpenID Connect
If an OpenID Connect token is sent then the EMu server will contact the token’s issuing server to validate it. If validation succeeds then a JWT token is generated for use with the shim. The payload for an OpenID Connect request should contain:
{
"token": "{token}"
"timeout": {minutes},
"renew": {true|false}
}
Both the timeout and renew properties are optional and have the same meaning as for username/password authentication.
Passcode
A passcode is an encrypted string that can be used to authenticate without the need supply either a username/password pair or an OpenID Connect token. The passcode is issued by a system administrator and has an expiry time associated with it. The passcode may be used any time before its expiry to authenticate. The payload for a passcode request should contain:
{
"passcode": "{passcode}",
"timeout": {minutes},
"renew": {true|false}
}
Both the timeout and renew properties are optional and have the same meaning as for username/password authentication.
When a passcode is received by the server it is decrypted and the expiry time is checked. If the passcode has expired authentication fails. If the server file restapi/conf/passcodes.accept
exists then only encrypted passcodes listed in the file are authenticated. If the accept file does not exist then if the server file restapi/conf/passcodes.reject
exists and the encrypted passcode is listed in the file then authentication fails. If neither file exists then any passcode can be used authentication. Such a setup gives flexibility in how passcodes are managed.
A passcode is encrypted using the passcodeKey defined in the restapi/conf/tenants.conf
for the requesting tenant. The key must be a hex string 32 characters long. See Tenants Configuration for details. The passcodeKey should be kept secret in order to preserve the passcode security.
A passcode can be generated using the following command:
echo '{username}|{password}|{expires}' | \
openssl enc -aes-128-cbc -nosalt -a -K '{passcodeKey}' -iv 00000000000000000000000000000000
where:
- username - login name to use with passcode
- password - password associated with username
- expires - an expiry date-time with a time-zone in the ISO-8601 format
- passcodeKey - hex key string as defined in the
tenants.conf
file
The format of the expires date-time is:
yyyy-mm-ddTHH:MM:SS±HH:MM
For example the date-time:
2024-05-29T10:15:30+10:00
represents the 29th May 2024 at 10:15 and 30 seconds with a timezone of plus 10 hours from UTC (Greenwich Mean Time). The timezone offset is required to ensure a passcode can be used anywhere in the world and expire at the correct time.
Example URI
POST /museum/tokens
Headers
Content-Type: application/json
Prefer: representation=minimal
Data
{
"username": "emu",
"password": "********"
}
Response 201
{
"id": "emu:/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c",
"version": 1,
"data": {
"iat": 1620004860,
"exp": 1620005760,
"iss": {
"id": "emu:/shared/tenants/museum",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/shared/tenants/museum"
}
}
},
"sub": {
"id": "emu:/museum/users/emu",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/users/emu"
}
}
},
"gid": {
"id": "emu:/museum/roles/Admin",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/roles/Admin"
}
}
},
"jti": {
"id": "emu:/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c"
}
}
},
"lid": "en_GB",
"wfg": [
"Admin"
]
},
"@namespaces": {
"emu": {
"name": "http://axiell.com/emu#"
}
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c"
},
"start": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/"
},
"emu:create": {
"method": "POST",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"isHrefTemplate": true,
"encoding": "json",
"schemaUrl": "http://swanston.melbourne.axiell.com:8084/museum/resources/tokens"
},
"search": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:update": {
"method": "PATCH",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c",
"encoding": "json",
"schemaUrl": "http://json.schemastore.org/json-patch"
},
"emu:update-all": {
"method": "PATCH",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"encoding": "json",
"schemaUrl": "http://json.schemastore.org/json-patch"
},
"emu:delete": {
"method": "DELETE",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c"
},
"emu:delete-all": {
"method": "DELETE",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
}
}
}
Response Headers
Content-Type: application/vnd.mason+json; charset=UTF-8
Location: http://swanston.melbourne.axiell.com:8084/museum/tokens/d334ee4d-d4e2-4465-99f0-bbf1aa9bacc5
Authorization: Bearer {token}
Delete
DELETE /{tenant}/tokens
DELETE /{tenant}/tokens/{id}
The DELETE method removes a token from the shim. It effectively logs out the user associated with the token. The first form will delete all tokens created by the user making the request. If the requesting user is the admin account then all tokens for the given {tenant} are removed, hence logging out all users of the tenant. The second form removes a single token identified by its unique identifier.
If the token(s) are successfully deleted then a 200
status is returned. The payload will contain the deleted token(s). For the first form a JSON matches array will contain the deleted tokens, while for the second form a single token is returned.
Example URI
DELETE /museum/tokens
Headers
Authorization: Bearer {token}
Prefer: representation=minimal
Response 200
{
"@namespaces": {
"emu": {
"name": "http://axiell.com/emu#"
}
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"start": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/"
},
"emu:create": {
"method": "POST",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"isHrefTemplate": true,
"encoding": "json",
"schemaUrl": "http://swanston.melbourne.axiell.com:8084/museum/resources/tokens"
},
"search": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:delete-all": {
"method": "DELETE",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:update-all": {
"method": "PATCH",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"encoding": "json",
"schemaUrl": "http://json.schemastore.org/json-patch"
}
},
"hits": 1,
"matches": [
{
"id": "emu:/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c",
"version": 1,
"data": {
"iat": 1620004860,
"exp": 1620005783,
"iss": {
"id": "emu:/shared/tenants/museum",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/shared/tenants/museum"
}
}
},
"sub": {
"id": "emu:/museum/users/emu",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/users/emu"
}
}
},
"gid": {
"id": "emu:/museum/roles/Admin",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/roles/Admin"
}
}
},
"jti": {
"id": "emu:/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c"
}
}
},
"lid": "en_GB",
"wfg": [
"Admin"
]
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/65955134-39d7-4041-b4a3-b32aaec0d65c"
}
}
}
]
}
Response Headers
Content-Type: application/vnd.mason+json; charset=UTF-8
Authorization: Bearer {token}
Update
PATCH /{tenant}/tokens
PATCH /{tenant}/tokens/{id}
The PATCH method updates the expiry time on a token(s). It is similar to the UNIX touch command in that it updates the expiry time on the shim’s version of the token, forcing the token to still be valid. The first form will reset the expiry time for all tokens owned by the user making the request. If the shim admin account requests the update then all the tenant’s tokens are updated. The second form allows an individual token, owned by the user, to be updated.
While a token remains valid on the server the user may use it, even if the client program sends a token that has exceeded its expiry date. The mechanism allows for a third party to refresh tokens without the client receiving the updated token for use.
The payload must be empty for both forms of the request.
A 200
status is returned if the updates succeed. The payload will consist of the tokens updated.
Example URI
PATCH /museum/tokens
Headers
Authorization: Bearer {token}
Prefer: representation=minimal
Response 200
{
"@namespaces": {
"emu": {
"name": "http://axiell.com/emu#"
}
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"start": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/"
},
"emu:create": {
"method": "POST",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"isHrefTemplate": true,
"encoding": "json",
"schemaUrl": "http://swanston.melbourne.axiell.com:8084/museum/resources/tokens"
},
"search": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:delete-all": {
"method": "DELETE",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:update-all": {
"method": "PATCH",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"encoding": "json",
"schemaUrl": "http://json.schemastore.org/json-patch"
}
},
"hits": 1,
"matches": [
{
"id": "emu:/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff",
"version": 1,
"data": {
"iat": 1620006482,
"exp": 1620007395,
"iss": {
"id": "emu:/shared/tenants/museum",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/shared/tenants/museum"
}
}
},
"sub": {
"id": "emu:/museum/users/emu",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/users/emu"
}
}
},
"gid": {
"id": "emu:/museum/roles/Admin",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/roles/Admin"
}
}
},
"jti": {
"id": "emu:/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff"
}
}
},
"lid": "en_GB",
"wfg": [
"Admin"
]
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff"
}
}
}
]
}
Response Headers
Content-Type: application/vnd.mason+json; charset=UTF-8
Authorization: Bearer {token}
Retrieve
GET /{tenant}/tokens[?select={selectlist}]
GET /{tenant}/tokens/{id}[?select={selectlist}]
The GET method retrieves the token(s) associated with the requested tenant. The first form retrieves all tokens owned by the requesting user, unless the admin account is used in which case all tokens are returned. The second form allows a single token to be retrieved, provided the requester owns the token or the admin account is used.
By default all values in the record are returned. You can limit the values returned using the select query parameter. The parameter takes a comma separated list of field names. The field names are that of the enveloped record so most field names will start with data.. The Select Syntax section provides a complete description of acceptable values for the select query parameter.
Example URI
GET /museum/tokens
Headers
Authorization: Bearer {token}
Prefer: representation=minimal
Response 200
{
"@namespaces": {
"emu": {
"name": "http://axiell.com/emu#"
}
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"start": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/"
},
"emu:create": {
"method": "POST",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"isHrefTemplate": true,
"encoding": "json",
"schemaUrl": "http://swanston.melbourne.axiell.com:8084/museum/resources/tokens"
},
"search": {
"method": "GET",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:delete-all": {
"method": "DELETE",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens"
},
"emu:update-all": {
"method": "PATCH",
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens",
"encoding": "json",
"schemaUrl": "http://json.schemastore.org/json-patch"
}
},
"hits": 1,
"matches": [
{
"id": "emu:/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff",
"version": 1,
"data": {
"iat": 1620006482,
"exp": 1620007395,
"iss": {
"id": "emu:/shared/tenants/museum",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/shared/tenants/museum"
}
}
},
"sub": {
"id": "emu:/museum/users/emu",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/users/emu"
}
}
},
"gid": {
"id": "emu:/museum/roles/Admin",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/roles/Admin"
}
}
},
"jti": {
"id": "emu:/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff",
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff"
}
}
},
"lid": "en_GB",
"wfg": [
"Admin"
]
},
"@controls": {
"self": {
"href": "http://swanston.melbourne.axiell.com:8084/museum/tokens/550e9f0d-017a-4b3f-8931-0e8c670c24ff"
}
}
}
]
}
Response Headers
Content-Type: application/vnd.mason+json; charset=UTF-8
Authorization: Bearer {token}