Skip to content

Add optional RAS auth gateway for multi-service browser frontends #14

@JedimEmO

Description

@JedimEmO

Summary

Add an optional RAS auth gateway / reverse proxy for browser frontends that talk to multiple backend services.

The gateway should validate a RAS web session locally, route requests to the correct backend audience, narrow the caller's audience-scoped permissions to that target audience, mint a short-lived single-audience backend token, cache derived tokens briefly, and forward only the narrowed token to the backend.

This should be an opt-in deployment component. Single-service apps should continue to use embedded auth authority mode from #13 without deploying a gateway.

Related:

Problem

For one service, the simplest deployment is one Axum/RAS app hosting both /auth/* and /api/*, with the backend enforcing RAS permissions directly.

For multi-service browser apps, a single frontend often talks to several backend services behind a reverse proxy:

browser
  -> proxy/gateway
  -> invoice-service
  -> billing-service
  -> admin-service

Forwarding the full browser session token to every backend has drawbacks:

  • backend services may see permissions for unrelated services/domains
  • every backend must understand multi-audience permission maps
  • permission names can collide if not scoped correctly
  • debugging/logging can expose cross-service permission information
  • domain/service isolation is weaker than necessary

Calling the RAS authority on every proxied request is also undesirable. The point of signed JWTs is local validation and avoiding hot-path authority calls.

Proposed Direction

Create an optional gateway component/crate, likely:

  • ras-authorization-gateway
    • reverse proxy/token exchanger for Axum/tower/hyper
    • local RAS web session validation through JWKS/config
    • route-to-audience mapping
    • audience-scoped permission narrowing
    • short-lived backend token minting
    • derived-token cache
    • safe header forwarding rules
    • observability/audit hooks

The gateway should be able to consume generated topology artifacts from #15. Hand-written config should remain possible for simple/manual deployments, but generated topology profiles should be the preferred path for larger systems.

The gateway is a trusted auth component. It is allowed to mint derived backend tokens, but only by narrowing a valid RAS web session token. It must never invent permissions, widen audiences, or forward the original browser session token to backend services.

The gateway must not grow an independent authorization model. Its token format, signing/verification traits, token type, issuer/key handling, and JWKS behavior should come from the shared authorization token primitives in #13. If deployments use a gateway-specific signing key, that key should still be represented as delegated RAS auth infrastructure and exposed through an intentional JWKS/issuer configuration that backends validate explicitly.

Flow

browser sends RAS web session cookie/JWT
  -> gateway validates RAS session locally via JWKS/config
  -> gateway maps route to target audience
  -> gateway extracts only that audience's permissions
  -> gateway mints short-lived backend token
  -> gateway caches token by session/audience/context/version
  -> gateway forwards request with Authorization: Bearer <backend token>
  -> backend validates single-audience token via JWKS/config

Example route config:

[auth]
authority = "https://auth.internal"

[routes."/invoices"]
audience = "invoice-service"
upstream = "http://invoice-service:3000"

[routes."/billing"]
audience = "billing-service"
upstream = "http://billing-service:3000"

In generated mode, the route/audience portion of this config should come from a #15 gateway profile. Deployment-specific upstream resolution remains external and pluggable.

Derived backend token shape:

{
  "typ": "ras_gateway_access",
  "sub": "alice",
  "aud": "invoice-service",
  "tenant": "acme",
  "permissions": ["invoice:read", "invoice:approve"],
  "authz_version": 42,
  "exp": 123
}

The backend only sees its own audience and permissions.

Route Matching Model

Route matching should be deterministic and specified up front:

  • each route profile declares whether matches are exact paths, prefixes, host+path rules, or method+host+path rules
  • if prefix matching is supported, longest-prefix wins
  • conflicts within one gateway profile fail validation
  • route conflicts across different gateway profiles are allowed
  • path rewrite behavior is explicit and disabled by default
  • unmatched routes fail closed

This keeps generated #15 profiles, hand-written config, startup validation, and observability labels aligned.

Cache Model

The gateway may cache derived tokens to avoid signing on every request.

Recommended cache key:

session_id + subject + tenant/context + audience + authz_version

Cache TTL:

min(web_session_expiry, derived_token_expiry, configured_gateway_cache_max)

The cache is an optimization only. Losing the cache should not trigger external IdP login or a full auth cycle; it should only require local validation and local re-signing from the gateway.

Security Requirements

  • Gateway mode is opt-in. Single-service embedded deployments must not require it.
  • The gateway must validate the RAS web session locally: issuer, expiry, signature, key ID, algorithm allowlist, token type, tenant/context, and authz/session version where applicable.
  • Route-to-audience mapping must be explicit and fail closed.
  • Route-to-audience mapping may be generated from Add RAS topology macro for compile-checked service graphs and generated artifacts #15 topology gateway profiles and should be validated against deployment-provided upstream config at startup.
  • Route matching semantics must be deterministic: exact/prefix/host/method behavior, longest-prefix rules, rewrites, and conflicts are part of validated configuration.
  • Derived tokens must be single-audience.
  • Derived tokens must include only the target audience's permissions.
  • The gateway must never forward the browser session token to backend services.
  • The gateway must never add permissions that were not present for the target audience in the validated web session.
  • The gateway must not derive tokens for audiences absent from the web session or route configuration.
  • Derived-token signing keys must be managed as auth infrastructure and support rotation/JWKS.
  • Gateway-derived tokens must use the shared Add RAS-native authorization control plane and internal service token issuer #13 token/JWKS primitives and a token type that backends can distinguish from web sessions and internal service tokens.
  • Backends must validate token type, issuer, audience, expiry, signature, key ID, tenant/context, and permissions.
  • Header forwarding must strip or overwrite inbound Authorization, credential, and hop-by-hop headers before proxying.
  • Request bodies should stream without buffering by default.
  • Audit/observability hooks should record derivation failures and route/audience mismatches without logging token values.

Acceptance Criteria

  • A gateway can route browser requests to multiple backend services using declarative route-to-audience configuration.
  • A gateway can consume a generated Add RAS topology macro for compile-checked service graphs and generated artifacts #15 gateway profile for route-to-audience configuration.
  • The gateway validates a RAS web session locally without calling the RAS authority on every request.
  • The gateway mints short-lived single-audience backend tokens from a valid RAS web session.
  • The gateway caches derived backend tokens using session/audience/context/version cache keys.
  • Backend services receive only the derived token, not the original browser session token.
  • Derived tokens omit unrelated audience permissions.
  • Route/audience mismatches fail closed.
  • Missing target-audience permissions fail closed by default. Authenticated-only backend routes may allow an empty permission set only when the route and backend operation are explicitly declared as authenticated-only.
  • Derived signing keys support rotation/JWKS.
  • A backend validation helper can validate gateway-derived tokens with simple single-audience semantics.
  • The gateway strips unsafe inbound headers before forwarding.
  • The gateway can run as an Axum/tower service or equivalent Rust binary example.
  • Docs clearly position the gateway as the third deployment preset after Embedded and Central Authority.

Test Plan

  • Validate route-to-audience mapping and fail-closed behavior.
  • Validate exact/prefix route matching, longest-prefix behavior, path rewrite defaults, and route conflict detection.
  • Validate generated Add RAS topology macro for compile-checked service graphs and generated artifacts #15 gateway profile consumption and startup validation against deployment-provided upstreams.
  • Validate local RAS web session verification.
  • Validate derived token contains exactly one audience.
  • Validate unrelated audience permissions are omitted.
  • Validate no permissions are invented or widened.
  • Validate cache key includes session ID, subject, tenant/context, audience, and authz version.
  • Validate cache expiry respects web session expiry and derived token expiry.
  • Validate original browser session token is stripped before forwarding.
  • Validate inbound Authorization is overwritten/removed before proxying.
  • Validate backend helper rejects wrong audience, wrong token type, expired token, invalid signature, missing key ID, and insufficient permissions.
  • Validate missing target-audience permissions fail closed by default and authenticated-only exceptions are explicit.
  • Validate signing key rotation behavior.
  • Validate observability/audit hooks do not log token values.

Non-Goals For v1

Notes

This design keeps backend services simple and privacy-preserving: each backend receives only a short-lived token for itself, with only its own permission set. It also avoids per-request calls to the RAS authority by relying on local JWT validation and optional derived-token caching.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions