diff --git a/routes.go b/routes.go index 2aca0ad..a546c30 100644 --- a/routes.go +++ b/routes.go @@ -30,8 +30,9 @@ const ( // bucketManage throttles the management API per token, or per client IP // for a request without one. bucketManage = "throttle:jz.bm.manage" - // bucketRealtime throttles the realtime token and subscribe-proxy - // routes per user, or per client IP for a request without one. + // bucketRealtime throttles the realtime token route per user (it runs + // after jwt.auth), or per client IP for a request without one. The + // subscribe proxy is not throttled: see mountRealtime. bucketRealtime = "throttle:jz.bm.realtime" ) @@ -154,16 +155,21 @@ func (p *Plugin) Routes(r pact.Router) error { // of golem15.presentations is the only realtime consumer, so nothing is // mounted without it, and the token route is limited to presentation owners // by its presentations.realtime-owner guard, named here without importing -// the plugin. Routes built without Boot (bare tests) mount nothing; the -// default null driver declares no routes. +// the plugin. Only the token route is throttled, per user. The subscribe +// proxy is called by Centrifugo from 127.0.0.1 for every client, so any +// throttle there would be one shared bucket that a single token holder +// could exhaust and lock the owner out; it relies on its constant-time +// secret check and Centrifugo's own limits instead. Routes built without +// Boot (bare tests) mount nothing; the default null driver declares no +// routes. func (p *Plugin) mountRealtime(r pact.Router) error { if p.realtime == nil || p.app == nil || !p.app.HasPlugin("golem15.presentations") { return nil } return lighthouse.Mount(r, p.realtime.Driver(), lighthouse.Surfaces{ - UserAuth: surf.Use("jwt.auth", "presentations.realtime-owner"), + UserAuth: surf.Use("jwt.auth", "presentations.realtime-owner", bucketRealtime), ServerToServer: surf.Use(), - Middleware: surf.Use(bucketRealtime), + Middleware: surf.Use(), }) }