Safely propagating user access roles between session requests

Stateful Roles


This note documents how roles are propagated from one request to the next without accessing the server's authorization file every time.

  • AES-192 symmetric encryption is employed to hide roles.
  • Cookies are used to carry the encrypted data.
  • Timestamps and idle-time thresholds are used to renew and expire authorized sessions.
  • IP addesses provide an additional safety value against hacker replay attacks.

When a user successfully authenticates through a login request, the server responds by returning a cookie that contains an encrypted list of roles assigned to the user. These roles are matched against subsequent requests to determine whether or not the user has permission to access the requested resource using the requested method.

Once a session cookie has been created, no further interaction with the roles file is necessary. This is referred to as stateful roles.

Special precautions need to be taken to prevent intentional misuse of this mechanism through alteration or hacker replay attacks.

In order to prevent a bad actor from altering the cookie and obtaining illegitimate access, the roles are subjected to the AES-192 symmetric-key encryption algorithm. That algorithm uses a cipher key to encrypt the roles before they are sent to the browser and to decrpyt the roles when they return to the server through an incoming request.

The cipher key used in this encryption/decryption process is an arbitrary value chosen by the webmaster. (Note that this cipher key has nothing to do with the user's password authentication procedure.)

In order to prevent bad actors from capturing the encrypted cookie and replaying it at a later time, the cookie has a limited period of validity. A timestamp is added to the data structure used to store the user's assigned roles, and that timestamp, together with a duration, defines the period of time during which the cookie is valid.

The webmaster configures the duration using the max-idle entry, which specifies the number of seconds that the cookie is valid, as counted from the time captured when the user logged in. The login timestamp is carried inside the cookie's data structure.

It should be noted that the max-idle time is not strictly an expiration time limit. Rather, it is a half-life counter, with automatic renewal. When new requests arrive within the first half of the max-idle's life, they are processed as expected. When new requests arrive within the second-half of the max-idle's life, the request is honored, and the cookie is renewed. In this way, a user that continues to make requests — that are no more than max-idle seconds apart — will continue to stay logged in.

In order to further protect against replay attacks, the user's IP address is encrypted and added to the cookie's data structure during login. When subsequent requests receive the cookie, the decrypted IP address is compared against the incoming request's IP address and mismatches are denied.

An RBAC request may fail for several reasons. Appropriate status codes and information headers are returned as follows:

Triggering event Information header Resulting action
Cookie has been tampered with rw-rbac-forged Request fails with status code 403.
Cookie's remote address differs rw-rbac-remote-address The "anonymous" role is used.
Timestamp is too old rw-rbac-expired The "anonymous" role is used.
Timestamp is in 2nd half-life rw-rbac-renewal A fresh set-cookie is issued.


The rw-rbac cookie's encryption algorithm requires an arbitrary cipher key that is unforgeable, but forgettable, that is, the webmaster can set it once and not think about it again. The cipher-secret entry is used for this. It may be changed safely, during periods of quiet activity, with the only side effect being that currently active users will need to login again. It should never be exposed to the public and never added to a git repository.

A max-idle entry should be configured with some reasonable number of seconds. Typical values are: 1200 (20 minutes), 5400 (90 minutes), 86400 (24 hours). Smaller values provide a shorter window of time for any potential replay attack to be successful, but at the cost of annoying the user, who may want to legitimately use the website without such fuss.

The roles file, which is created by the addrole CLI utility, and used by the RW-Auth handler, is specified in this same section, using the roles entry. This entry should contain the fully qualified, absolute path to the roles file. Be sure to delimit this path by enclosing it in GRAVE-ACCENTs.


SP ::= U+20
CR ::= U+0D
visible-ascii ::= U+21..U+7E
file-system-chars ::= (ALPHA | DIGIT | )*
roles-file-entry ::= 'roles' SP GRAVE-ACCENT file-system-chars GRAVE-ACCENT CR
cipher-secret-entry ::= 'cipher-secret' SP visible-ascii* CR
max-idle-entry ::= 'max-idle' SP (0..9)* CR
rbac-section ::= 'rbac' SP LEFT-CURLY-BRACKET CR

† Legal file system characters vary by platform


Example: RBAC stateful roles configuration

This is a partial RBAC configuration, showing only the entries used for cookie based stateful roles. A more complete example is provided in the note that covers the RBAC handler itself.

host {
rbac {
roles `/etc/rwserve/roles` // the file created by the 'addrole' CLI utility
cipher-secret C#9fB$2gD@5zR*7e // secret used to encrypt the 'rw-roles' cookie
max-idle 1800 // number of seconds of inactivity before credentials expire


Key points to remember:

  • A user's assigned roles are carried in the rw-rbac cookie in encrypted form.
  • The RBAC handler user stateful roles to determine whether a uses has appropriate privileges to requested resources.
Read Write Tools icon

Smart tech


Read Write Hub icon

Templates & Content



Rediscover HTML



Safely propagating user access roles between session requests

🔗 🔎