Version: 1.0.0 Category: Authentication
Examples of webapp authentication flows including login, authenticated requests, unauthorized handling, and logout.
The webapp does not use username/password. Login proves control of a Cosmos address by signing a deterministic message with that address’s key; on success the server issues a PHPSESSID session cookie (there is no JWT/bearer token).
Signed message format (SignatureValidationManager::buildLoginMessage):
LOGIN_GUILD{guildId}ADDRESS{address}DATETIME{unix_timestamp}
For example, signing into guild 0-1 as structs1abc... at unix time 1715000000 signs the literal string LOGIN_GUILD0-1ADDRESSstructs1abc...DATETIME1715000000. The unix_timestamp must be within 600 seconds (10 minutes) of server time or login is rejected.
Request:
{
"method": "POST",
"url": "http://localhost:8080/api/auth/login",
"headers": {
"Content-Type": "application/json"
},
"body": {
"address": "structs1abc...",
"signature": "base64-encoded-signature",
"pubkey": "base64-encoded-pubkey",
"guild_id": "0-1",
"unix_timestamp": "1715000000"
}
}
Response (200):
{
"headers": {
"Set-Cookie": "PHPSESSID=abc123def456; Path=/; HttpOnly",
"Content-Type": "application/json"
},
"body": {
"success": true,
"errors": {},
"data": null
}
}
Next Steps:
session.cookie = 'PHPSESSID=abc123def456'GET /api/guild/this with Cookie header). Browser clients must set credentials: include.Login failure (bad signature, expired timestamp, or unknown address) returns HTTP 401 with a keyed errors object — not a message string.
Request:
{
"method": "POST",
"url": "http://localhost:8080/api/auth/login",
"headers": {
"Content-Type": "application/json"
},
"body": {
"address": "structs1abc...",
"signature": "bad-signature",
"pubkey": "base64-encoded-pubkey",
"guild_id": "0-1",
"unix_timestamp": "1715000000"
}
}
Response (401):
{
"body": {
"success": false,
"errors": {
"signature_validation_failed": "Invalid signature"
},
"data": null
}
}
Other possible error keys: player_address_does_not_exists (no approved address for that guild), player_does_not_exists.
errors key.unix_timestamp (signatures expire after 600s), confirm the address is an approved member of the target guild, and verify the signed-message format above.Make an authenticated request using the session cookie obtained at login.
Request:
{
"method": "GET",
"url": "http://localhost:8080/api/guild/this",
"headers": {
"Cookie": "PHPSESSID=abc123def456",
"Accept": "application/json"
}
}
Response (200):
/api/guild/this returns the host/infrastructure guild for this deployment (guild_meta.this_infrastructure = TRUE) — not the logged-in player’s guild. It is one of the few public routes. Guild IDs are type 0.
{
"success": true,
"errors": {},
"data": {
"id": "0-1",
"name": "My Guild"
}
}
A session-gated request made without a valid PHPSESSID cookie returns HTTP 401. (Note: /api/guild/this itself is public; use a session-gated route such as /api/reactor/all/page/1 to see this.)
Request:
{
"method": "GET",
"url": "http://localhost:8080/api/reactor/all/page/1",
"headers": {
"Accept": "application/json"
}
}
Response (401): the request is rejected by PlayerAuthenticator before reaching the controller.
Logout and clear the session.
Request:
{
"method": "GET",
"url": "http://localhost:8080/api/auth/logout",
"headers": {
"Cookie": "PHPSESSID=abc123def456"
}
}
Response (200):
{
"success": true,
"errors": {},
"data": null
}
Next Steps:
session.cookie = null