mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-19 19:22:49 +00:00
feat(dashboard): scoped API keys with rate limits and quotas
Bearer keys (dlk_...) are checked per request against their owner's current account (ban, lock, demotion, sign out everywhere stop or narrow them at once) and their scope: permission routes need the permission in scope, read-only keys only read, level-only routes need an all-permission key, and session-only paths (sign-in, password, 2FA, key management) are never reachable with a key. Per-key rate limit and daily quota with 429 and X-RateLimit/X-Quota/Retry-After headers; usage is written in batches every minute. WebSocket subscriptions honour the scope too. Routes to list, make, rotate and revoke keys; staff with api_keys_manage can see and revoke others' keys under the rank rules. POST /api/auth/token now makes an all-permission key. Audit entries for create/rotate/revoke/denied, and actions done with a key are attributed to "user (key name)". Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
#include "AuthRoutes.h"
|
||||
#include "ApiKeyRoutes.h"
|
||||
#include "Permissions.h"
|
||||
#include "DashboardAuthService.h"
|
||||
#include "AuthTokenHandler.h"
|
||||
@@ -216,7 +217,9 @@ void RegisterAuthRoutes() {
|
||||
|
||||
// POST /api/auth/token - Issue a bearer token for API clients (bots, scripts)
|
||||
// Request body: { "days": number (1-365, default 30) }
|
||||
// The token carries the caller's identity; its permissions always follow the account's current GM level.
|
||||
// Kept for scripts written for the old API tokens: it now makes an API key named "API token" with all of the
|
||||
// caller's permissions (which always follow the account's current GM level). Pick a narrower scope, limits and
|
||||
// no expiry with POST /api/api_keys. Tokens made before API keys keep working until they expire.
|
||||
Game::web.RegisterHTTPRoute({
|
||||
.path = "/api/auth/token",
|
||||
.method = eHTTPMethod::POST,
|
||||
@@ -230,10 +233,7 @@ void RegisterAuthRoutes() {
|
||||
}
|
||||
const auto json = RouteUtils::ParseBody(context);
|
||||
const int64_t days = std::clamp<int64_t>(json ? json->value("days", 30) : 30, 1, MAX_API_TOKEN_DAYS);
|
||||
const auto token = JWTUtils::GenerateSessionToken(context.accountId, context.authenticatedUser, context.gmLevel, false, days * 24 * 60 * 60);
|
||||
if (token.empty()) return RouteUtils::JsonError(reply, eHTTPStatusCode::INTERNAL_SERVER_ERROR, "Token generation failed");
|
||||
|
||||
RouteUtils::Audit(context, "create_api_token", "Valid for " + std::to_string(days) + " days");
|
||||
const auto token = ApiKeyRoutes::CreateFullKey(context, "API token", days);
|
||||
RouteUtils::JsonReply(reply, eHTTPStatusCode::OK, { {"token", token}, {"expiresInDays", days} });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user