Session¶
The session routes turn the API key and secret into the access token that every other route needs, report whether that token is still good, and end the session. There is one token for the whole application at a time, so these three routes manage a single shared login rather than one login per client.
The table below lists the three routes on this page.
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/session/connect |
Exchanges the api-key and api-secret headers for the access token |
| DELETE | /api/session/disconnect |
Revokes the access token for every client |
| GET | /api/session/status |
Says the token is valid and when it expires |
How the token lives and dies¶
A token is a random UUID that the API mints and stores in two places. MongoDB holds the record, in the last_login collection under broker_name unified_broker_interface, beside the ten broker logins. Redis holds a copy in the last_login hash under the same field, and that copy is what every request checks.
The diagram below shows every state the token can be in and what moves it from one state to the next. "07:00" means 07:00 on the machine's clock, which the project runs on India time.
stateDiagram-v2
[*] --> NoToken
NoToken --> Live: connect mints a token
Live --> Live: connect after the latest 07:00<br/>returns the same token
Live --> Live: connect when the token was issued<br/>before the latest 07:00 mints a new one
Live --> Expired: expires_at passes
Live --> Revoked: disconnect
Expired --> Live: connect mints a token
Revoked --> Live: connect mints a token
The rules behind that diagram come from TokenStore in unified_broker_interface/utilities/tokens.py, and they are listed below.
- A
connectfirst reads the stored login document from MongoDB, not from the Redis copy. - If that document holds a token that was issued at or after the most recent 07:00 and has not expired, the same token is returned unchanged. Before 07:00, "the most recent 07:00" is yesterday's, so a token issued after midnight is kept.
- Otherwise a new token is minted. It replaces the old one, so any other client still holding the old token starts getting 401.
- A new token is accepted for
UNIFIED_BROKER_INTERFACE_API_TOKEN_TTL_SECONDSseconds, which defaults to86400(one day). - The new document is written to MongoDB first and to Redis second. If the Redis write fails, the Redis field is deleted instead, so the next request falls back to MongoDB rather than trusting a copy that is out of date.
- A
disconnectstores a document whoseaccess_tokenandexpires_atare bothnull, which makes the old token fail immediately.
A token can outlive 07:00
The daily renewal only happens when someone calls connect. With the default one-day lifetime, a token issued at 10:00 stays valid until 10:00 the next day unless a connect after 07:00 replaces it first. A program that runs all day should call connect once each morning after 07:00, and it will get the same token back on any later call that day.
The login document that is stored looks like this. The values here are placeholders.
{
"broker_name": "unified_broker_interface",
"access_token": "5b0c9d0e-8f7a-4c35-9a53-2f4f0f4f7a61",
"last_login": "2026-09-26 07:05:11.402913",
"expires_at": "2026-09-27 07:05:11.402913"
}
How a request is checked¶
Every route except connect and GET /api/ runs the same check before doing any work. The check reads the login document from Redis, or from MongoDB when Redis has none or cannot be reached, and compares the header against it in constant time.
The flowchart below shows the order of the checks and the message each failure returns.
flowchart TD
A["Request arrives"] --> B{"access-token<br/>header present?"}
B -- no --> E1["401<br/>Access token is required"]
B -- yes --> C{"matches the<br/>stored token?"}
C -- no --> E2["401<br/>Invalid access token"]
C -- yes --> D{"expires_at<br/>in the future?"}
D -- no --> E3["401<br/>Access token has expired"]
D -- yes --> OK["Route runs"]
A stored document without a readable expires_at counts as expired. After a disconnect the stored token is null, so every token fails the match and the answer is Invalid access token rather than Access token has expired.
Connect¶
POST /api/session/connectapi-key and api-secret
This route checks the key and secret against the api_key and api_secret fields of the document whose broker_name is unified_broker_interface in the MongoDB settings collection. If both match, it returns the token in force or mints a new one, following the rules above. It takes no body.
Request parameters¶
The two headers below are the only input.
| Name | In | Type | Required | Description |
|---|---|---|---|---|
api-key |
header | string | Yes | The key stored in settings for unified_broker_interface |
api-secret |
header | string | Yes | The matching secret |
Example¶
Response¶
The token comes back under the key access-token, spelled with a hyphen to match the header it will be sent in.
{
"access-token": "5b0c9d0e-8f7a-4c35-9a53-2f4f0f4f7a61",
"expires_at": "2026-09-27 07:05:11.402913"
}
Response attributes¶
| Attribute | Type | Description |
|---|---|---|
access-token |
string | The token to send in the access-token header of every other request |
expires_at |
string | When the token stops being accepted, as YYYY-MM-DD HH:MM:SS.ffffff in the server's local time |
Status codes¶
| Status | When |
|---|---|
| 200 | The key and secret matched. The body holds the token in force or a newly minted one. |
| 401 | Invalid API key or secret: either header is missing, empty or wrong. |
| 500 | unified_broker_interface settings are not configured: MongoDB settings has no document for unified_broker_interface. |
Under the hood
- Route:
SessionBlueprint.connectinunified_broker_interface/blueprints/session.py. Both credentials are compared withhmac.compare_digest. - Token logic:
TokenStore.connect, which callsissuewhen a new token is needed. - MongoDB: reads
settings, reads and writeslast_login. - Redis: writes the
last_loginhash, fieldunified_broker_interface. - Logs: every refused connect is logged with the caller's address, and every success says whether the token was new or reused.
Disconnect¶
DELETE /api/session/disconnectaccess-token
This route revokes the token in force. Because there is only one token, disconnecting ends the session for every client that shares it, not only for the caller. The route itself needs a valid token, so an expired token cannot be used to disconnect.
Request parameters¶
| Name | In | Type | Required | Description |
|---|---|---|---|---|
access-token |
header | string | Yes | The token in force |
Example¶
Response¶
Response attributes¶
| Attribute | Type | Description |
|---|---|---|
status |
string | Always disconnected |
Status codes¶
| Status | When |
|---|---|
| 200 | The token was revoked. |
| 401 | Access token is required, Invalid access token or Access token has expired. |
Under the hood
- Route:
SessionBlueprint.disconnectinunified_broker_interface/blueprints/session.py. - Token logic:
TokenStore.revokestoresaccess_token: null,expires_at: nulland the current time aslast_login, MongoDB first and Redis second.
Status¶
GET /api/session/statusaccess-token
This route answers only when the token is valid, so a 200 is itself the proof that the session is connected. The body adds when the token expires.
Request parameters¶
| Name | In | Type | Required | Description |
|---|---|---|---|---|
access-token |
header | string | Yes | The token to check |
Example¶
Response¶
Response attributes¶
| Attribute | Type | Description |
|---|---|---|
status |
string | Always connected |
expires_at |
string | When the token stops being accepted, as YYYY-MM-DD HH:MM:SS.ffffff in the server's local time |
Status codes¶
| Status | When |
|---|---|
| 200 | The token is valid. |
| 401 | Access token is required, Invalid access token or Access token has expired. |
Under the hood
- Route:
SessionBlueprint.statusinunified_broker_interface/blueprints/session.py. - The check is the
authenticateddecorator inunified_broker_interface/blueprints/base.py, which callsTokenStore.check. TokenStore.currentreads Redis first. When Redis has nothing, it reads MongoDB and fills Redis withHSETNX, so filling the cache can never overwrite aconnectthat lands in between.- See Sessions and logins for how the broker logins beside this document work.