diff --git a/lib/claims/src/lib.rs b/lib/claims/src/lib.rs index f3fc7d40d8..ba0994eb04 100644 --- a/lib/claims/src/lib.rs +++ b/lib/claims/src/lib.rs @@ -384,6 +384,7 @@ pub trait ClaimsDecode { fn as_access_token(&self) -> GlobalResult; fn as_provisioned_server(&self) -> GlobalResult; fn as_game_namespace_service(&self) -> GlobalResult; + fn as_game_namespace_service_option(&self) -> GlobalResult>; } impl ClaimsDecode for schema::Claims { @@ -679,6 +680,18 @@ impl ClaimsDecode for schema::Claims { )) .and_then(std::convert::identity) } + + fn as_game_namespace_service_option(&self) -> GlobalResult> { + self.entitlements + .iter() + .find_map(|ent| match &ent.kind { + Some(schema::entitlement::Kind::GameNamespaceService(ent)) => { + Some(ent::GameNamespaceService::try_from(ent)) + } + _ => None, + }) + .transpose() + } } pub trait EntitlementTag { diff --git a/svc/api/cloud/src/route/games/namespaces/mod.rs b/svc/api/cloud/src/route/games/namespaces/mod.rs index 665d41d479..7f19ea5e2c 100644 --- a/svc/api/cloud/src/route/games/namespaces/mod.rs +++ b/svc/api/cloud/src/route/games/namespaces/mod.rs @@ -279,8 +279,8 @@ pub async fn create_token_service( ctx: Ctx, game_id: Uuid, namespace_id: Uuid, - body: models::CreateGameNamespaceTokenServiceRequest, -) -> GlobalResult { + body: models::CloudGamesNamespacesCreateGameNamespaceTokenServiceRequest, +) -> GlobalResult { ctx.auth() .check_game_write_or_admin(ctx.op_ctx(), game_id) .await?; @@ -309,7 +309,7 @@ pub async fn create_token_service( }).await?; Ok( - models::CloudGamesNamespacesCreateGameNamespaceTokenPublicResponse { + models::CloudGamesNamespacesCreateGameNamespaceTokenServiceResponse { token: unwrap!(token_res.token).token, }, ) diff --git a/svc/api/cloud/src/route/mod.rs b/svc/api/cloud/src/route/mod.rs index 5ee5cae22b..0a41ab83bc 100644 --- a/svc/api/cloud/src/route/mod.rs +++ b/svc/api/cloud/src/route/mod.rs @@ -77,7 +77,7 @@ define_router! { }, "games" / Uuid / "namespaces" / Uuid / "tokens" / "service": { POST: games::namespaces::create_token_service( - body: models::CreateGameNamespaceTokenServiceRequest + body: models::CloudGamesNamespacesCreateGameNamespaceTokenServiceRequest ), }, "games" / Uuid / "namespaces" / Uuid / "tokens" / "development": { diff --git a/svc/api/matchmaker/src/auth.rs b/svc/api/matchmaker/src/auth.rs index a16181d9e7..142a96cb6a 100644 --- a/svc/api/matchmaker/src/auth.rs +++ b/svc/api/matchmaker/src/auth.rs @@ -1,9 +1,10 @@ use api_helper::{ auth::{ApiAuth, AuthRateLimitCtx}, ctx::Ctx, - util::{as_auth_expired, basic_rate_limit}, + util::{as_auth_expired, basic_rate_limit, basic_rate_limit_with_config}, }; use proto::claims::Claims; +use rivet_cache::{RateLimitBucketConfig, RateLimitConfig}; use rivet_claims::ClaimsDecode; use rivet_operation::prelude::*; @@ -18,15 +19,43 @@ impl ApiAuth for Auth { api_token: Option, rate_limit_ctx: AuthRateLimitCtx<'_>, ) -> GlobalResult { - Self::rate_limit(rate_limit_ctx).await?; - - Ok(Auth { - claims: if let Some(api_token) = api_token { - Some(as_auth_expired(rivet_claims::decode(&api_token)?)?) - } else { - None - }, - }) + // Decode claims + let claims = if let Some(api_token) = api_token { + Some(as_auth_expired(rivet_claims::decode(&api_token)?)?) + } else { + None + }; + + // Customize rate limit + if claims + .as_ref() + .and_then(|x| x.as_game_namespace_service_option().transpose()) + .transpose()? + .is_some() + { + // TODO: Configure by route & by game ID + basic_rate_limit_with_config( + rate_limit_ctx, + Some(RateLimitConfig { + key: "service".into(), + buckets: vec![ + RateLimitBucketConfig { + count: 256, + bucket_duration_ms: util::duration::minutes(1), + }, + RateLimitBucketConfig { + count: 10_000, + bucket_duration_ms: util::duration::hours(1), + }, + ], + }), + ) + .await?; + } else { + Self::rate_limit(rate_limit_ctx).await?; + } + + Ok(Auth { claims }) } async fn rate_limit(rate_limit_ctx: AuthRateLimitCtx<'_>) -> GlobalResult<()> { @@ -54,6 +83,15 @@ impl Auth { .transpose()? { return Ok(game_ns); + } else if let Some(ns_service) = self + .claims + .as_ref() + .and_then(|x| x.as_game_namespace_service_option().transpose()) + .transpose()? + { + return Ok(rivet_claims::ent::GameNamespacePublic { + namespace_id: ns_service.namespace_id, + }); } else { tracing::info!("no ns claims"); }