webgates-tonic 1.0.0

Tonic server-side transport adapter for webgates authentication and authorization.
Documentation

webgates-tonic

User-focused tonic server-side integration for the webgates stack.

webgates-tonic is the tonic-facing transport adapter for webgates. It lets you apply bearer-token authentication and authorization to gRPC services running on tonic while keeping the core auth and policy logic in the framework-agnostic webgates crate.

This crate is server-side only. It does not provide cookie transport, browser-based OAuth2 flows, or tonic client utilities.

Who this crate is for

Use webgates-tonic when you want to:

  • protect tonic gRPC services with bearer-token authentication
  • enforce webgates authorization policies in server middleware
  • read typed authentication context inside tonic handlers
  • use optional JWT auth context for mixed public/authenticated gRPC methods
  • support static-token service-to-service authentication
  • keep transport concerns separate from the core webgates domain and auth logic

You still depend on webgates for domain types, codecs, policies, claims, and repository contracts.

What this crate helps you build

Use this crate when you want one or more of these tonic-facing workflows:

  • bearer-token authentication for tonic services
  • authorization enforcement in tonic middleware
  • typed auth context extraction inside handlers
  • optional authenticated-or-public gRPC methods
  • static-token service-to-service protection

Install

Most applications should depend on both crates explicitly.

[dependencies]
tonic = "0.14"
webgates = "1.0.0"
webgates-tonic = "1.0.0"

Minimum supported Rust version: 1.91.

The mental model

The easiest way to understand webgates-tonic is:

  1. define auth and authorization rules in webgates
  2. adapt them into tonic middleware with webgates_tonic::gate::Gate
  3. let middleware validate bearer metadata and enforce policy
  4. read typed auth context from request extensions inside your gRPC handlers

This keeps responsibilities separate:

  • webgates owns policies, account claims, and token validation building blocks
  • webgates-tonic owns tonic-layer request interception and status mapping

Quick start

Strict JWT bearer gate

Applies the configured AccessPolicy to every request. Returns UNAUTHENTICATED or PERMISSION_DENIED on failure and inserts JwtAuthContext on success.

use std::sync::Arc;

use webgates::accounts::Account;
use webgates::authz::access_policy::AccessPolicy;
use webgates::codecs::jwt::{JsonWebToken, JwtClaims};
use webgates::groups::Group;
use webgates::roles::Role;
use webgates_tonic::gate::Gate;

type Claims = JwtClaims<Account<Role, Group>>;

let codec = Arc::new(JsonWebToken::<Claims>::default());

let layer = Gate::bearer("my-svc", codec)
    .with_policy(AccessPolicy::<Role, Group>::require_role(Role::Admin));

Require any authenticated user

let layer = Gate::bearer("my-svc", codec).require_login();

Optional authentication

Optional mode forwards all requests and inserts OptionalJwtAuthContext. Handlers decide what to do with authenticated vs anonymous requests.

use webgates_tonic::context::OptionalJwtAuthContext;
use webgates::roles::Role;
use webgates::groups::Group;

let layer = Gate::bearer("my-svc", codec).allow_anonymous_with_optional_user();

// In the handler:
// let ctx = req.extensions().get::<OptionalJwtAuthContext<Role, Group>>();
// if ctx.map(|c| c.is_authenticated()).unwrap_or(false) { ... }

Static-token gate

Static-token mode performs constant-time bearer token matching and inserts StaticTokenAuthorized.

use webgates_tonic::context::StaticTokenAuthorized;

let layer = Gate::bearer("my-svc", codec).with_static_token("internal-static-token");

let optional_layer = Gate::bearer("my-svc", codec)
    .with_static_token("internal-static-token")
    .allow_anonymous_with_optional_user();

Reading auth context in a handler

use tonic::{Request, Response, Status};
use webgates_tonic::context::JwtAuthContext;
use webgates::groups::Group;
use webgates::roles::Role;

async fn my_handler(
    req: Request<MyRequest>,
) -> Result<Response<MyResponse>, Status> {
    let ctx = req
        .extensions()
        .get::<JwtAuthContext<Role, Group>>()
        .ok_or_else(|| Status::unauthenticated("missing auth context"))?;

    let account = ctx.account();
    let claims = ctx.registered_claims();
    let _ = (account, claims);
    todo!()
}

Core concepts

1. Gate is the main integration entry point

Use webgates_tonic::gate::Gate to build tonic bearer-token middleware.

The main entry point is:

  • Gate::bearer(issuer, codec)

From there you can configure:

  • .with_policy(policy)
  • .require_login()
  • .allow_anonymous_with_optional_user()
  • .with_static_token(token)

2. Context types are how handlers read auth state

The middleware inserts typed values into request extensions so handlers can read authentication state without manually decoding metadata.

Main types are:

  • JwtAuthContext
  • OptionalJwtAuthContext
  • StaticTokenAuthorized

3. AuthError defines status mapping

AuthError is the typed error model used by the middleware for auth failures. It maps failures to appropriate tonic::Status values such as:

  • UNAUTHENTICATED
  • PERMISSION_DENIED
  • INTERNAL

Transport mechanism

webgates-tonic uses tower Layer and Service wrappers that operate directly on http::Request<tonic::body::Body>.

That gives the middleware access to the full HTTP header set, including Authorization: Bearer <token>, before the tonic request type is constructed. The layer can then insert typed extensions that handlers later read through request.extensions().

Non-goals

  • client-side tonic authentication
  • cookie-based authentication
  • browser-redirect or OAuth2 authorization code flows
  • any feature not required for server-side bearer token validation

Features

  • default = []
  • audit-logging: enables audit logging integration from webgates
  • prometheus: installs Prometheus metrics integration for auth events; depends on audit-logging

Security checklist

  • keep the issuer identical between token minting and gate validation
  • use persistent signing keys in production and rotate them deliberately
  • prefer short-lived JWTs and explicit observability
  • avoid logging secrets, raw tokens, or sensitive payloads
  • for static-token mode, the comparison is constant-time but the token is still held in memory as a plain string; protect the process environment accordingly

Recommended onboarding path

If you are new to this crate, I recommend this order:

  1. gate::Gate
  2. context
  3. errors
  4. gate::bearer
  5. gate::remote_jwks_bearer if you need remote JWKS-backed verification

Which crate should you use?

  • use webgates-tonic when you want tonic server-side bearer-token integration
  • use webgates when you want the higher-level application-facing auth stack
  • use webgates-axum when you want HTTP/Axum integration instead of gRPC
  • use webgates-codecs when you need codec or JWT support directly

License

MIT