Problem
Config-level validation policy (<maxTokenLifetimeSeconds> on <authenticationProvider> and <requiredScopes> on <oauthBearer>) is one-size-fits-all across the deployment. Operators cannot tighten policy for a specific caller - e.g. a short-lived CI key that should never emit a 1-hour token next to a long-running automation key, or an OAuth Client that should only accept tokens carrying spe.remoting.read while another requires spe.remoting.write. Today the only workarounds are to set the global ceiling pessimistically (hurts the long-running caller) or run separate <oauthBearer> entries per scope set (doesn't scale).
Shared Secret Client and OAuth Client items already express per-caller attributes (secret / kid / impersonated user / policy / throttle / expiry). These two validation knobs are the natural next layer: they vary per caller and belong on the item.
Proposed additions
Two optional fields on the Remoting Client templates. Both are additive: they can only narrow the global bound, never widen it.
1. MaxTokenLifetimeSeconds on the Remoting Client base template
Inherited by Shared Secret Client and OAuth Client.
- Empty / 0 means "inherit the config-level value" (current behaviour).
- A positive value caps the token lifetime below the global ceiling. Enforcement is
min(item.MaxTokenLifetimeSeconds, config.MaxTokenLifetimeSeconds) with 0 treated as "no item cap."
2. RequiredScopes on the OAuth Client template
Multi-line field, one scope per line (same shape as OAuth Client Ids).
- Empty means "inherit the config-level
<requiredScopes>" (current behaviour).
- When populated, enforcement is the union of config + item scopes; every listed scope must be present on the incoming token.
- Shared Secret Client does not get this field (scopes are an OAuth concept).
Security notes
- Both overrides are strictly additive: an item cannot weaken the global policy, only strengthen it.
- Operators should still set sensible config-level ceilings; items are a refinement layer, not a replacement.
RequiredScopes save-time validator should trim whitespace, reject duplicates, and warn on wildcard-like entries.
Scope
- Two new fields on the templates (new GUIDs, migration-safe defaults).
- Plumb values through
RemotingClientProvider.ParseApiKey / ParseOAuthClient.
- Extend
SharedSecretAuthenticationProvider.ValidateToken and OAuthBearerTokenAuthenticationProvider.Validate with per-request override arguments.
- Dispatch in
RemoteScriptCall.ashx.cs passes the matched item's overrides alongside the existing SharedSecret override.
- Integration tests:
- Client with item-lifetime=60s rejects a token minted with exp=300s even when config ceiling is 3600s.
- Two OAuth Clients with different
RequiredScopes; wrong-scope token against each returns 401.
- README "How config and items layer" update noting lifetime + scopes are the two knobs that can tighten per-item.
Follow-up to the layering discussion documented in README "How config and items layer."
Problem
Config-level validation policy (
<maxTokenLifetimeSeconds>on<authenticationProvider>and<requiredScopes>on<oauthBearer>) is one-size-fits-all across the deployment. Operators cannot tighten policy for a specific caller - e.g. a short-lived CI key that should never emit a 1-hour token next to a long-running automation key, or an OAuth Client that should only accept tokens carryingspe.remoting.readwhile another requiresspe.remoting.write. Today the only workarounds are to set the global ceiling pessimistically (hurts the long-running caller) or run separate<oauthBearer>entries per scope set (doesn't scale).Shared Secret Client and OAuth Client items already express per-caller attributes (secret / kid / impersonated user / policy / throttle / expiry). These two validation knobs are the natural next layer: they vary per caller and belong on the item.
Proposed additions
Two optional fields on the Remoting Client templates. Both are additive: they can only narrow the global bound, never widen it.
1.
MaxTokenLifetimeSecondson theRemoting Clientbase templateInherited by Shared Secret Client and OAuth Client.
min(item.MaxTokenLifetimeSeconds, config.MaxTokenLifetimeSeconds)with 0 treated as "no item cap."2.
RequiredScopeson theOAuth ClienttemplateMulti-line field, one scope per line (same shape as
OAuth Client Ids).<requiredScopes>" (current behaviour).Security notes
RequiredScopessave-time validator should trim whitespace, reject duplicates, and warn on wildcard-like entries.Scope
RemotingClientProvider.ParseApiKey/ParseOAuthClient.SharedSecretAuthenticationProvider.ValidateTokenandOAuthBearerTokenAuthenticationProvider.Validatewith per-request override arguments.RemoteScriptCall.ashx.cspasses the matched item's overrides alongside the existingSharedSecretoverride.RequiredScopes; wrong-scope token against each returns 401.Follow-up to the layering discussion documented in README "How config and items layer."