Help us shape the future of Go! Take the 2026 Go Developer Survey

traefik_keycloak_authorizer

package module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 6, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

README

Keycloak authorizer middleware

A Traefik plugin that extracts roles and/or permissions from a request's bearer JWT (optionally cross-checking permissions against Keycloak) and forwards them as request headers, rejecting requests that don't satisfy the configured gating rules.

Security model

This plugin performs authorization only — it does not authenticate the request. It assumes the bearer JWT has already been verified (signature and expiry checked) by an earlier middleware in the Traefik chain, and it trusts the claims in that token as given. This plugin itself never verifies a JWT signature or expiry, regardless of which config blocks (roles, permissions) are enabled — including when permissions.enabled is true, where the call to Keycloak's token endpoint is for exchanging/scoping permissions, not for validating the original token. Deploy this plugin only behind (i.e. after) an authentication middleware that verifies the token; otherwise any request with an unverified, self-signed, or expired bearer token will be authorized based on its claims alone.

Both roles and permissions can gate requests, symmetrically: if enabled is true, the request is rejected when nothing is found at all, or when some/ every don't match — unless enforce is explicitly set to false, in which case that block only extracts and forwards the header, without rejecting the request (see roles.enforce/permissions.enforce below). The request is also rejected when the bearer token is missing/malformed, or when the Keycloak permissions request fails, regardless of enforce.

Installation

For a plugin to be active, it must be declared in Traefik's static configuration, referencing a released git tag of this repository:

# Static configuration
experimental:
  plugins:
    jwtAuthorization:
      moduleName: github.com/sasd13/traefik-keycloak-authorizer
      version: v1.1.0

Once loaded, the plugin is configured per-middleware in the dynamic configuration, under the alias declared above (jwtAuthorization here):

# Dynamic configuration
http:
  middlewares:
    my-authorizer:
      plugin:
        jwtAuthorization:
          issuer: https://auth.example.com/realms/myrealm
          roles:
            enabled: true
          permissions:
            enabled: true
            audience: my-client

Configuration

The plugin.jwtAuthorization block above accepts the following fields. Here it is shown in full, as a Kubernetes Middleware CRD (the same fields apply under http.middlewares.<name>.plugin.jwtAuthorization in any other dynamic configuration provider):

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: jwt-authorization
spec:
  plugin:
    jwtAuthorization:
      issuer: https://auth.example.com/realms/myrealm
      roles:
        enabled: true
        enforce: true             # default: true
        headerName: X-User-Rol   # default: X-User-Rol
        some:
          - my-client:admin
        every:
          - my-client:verified
      permissions:
        enabled: true
        enforce: true             # default: true
        headerName: X-User-Prm   # default: X-User-Prm
        audience: my-client
        some:
          - orders:read
        every:
          - billing:write
      headerMap:
        X-User-Email: email
  • issuer — the Keycloak realm issuer URL. Required (plugin construction fails otherwise) when permissions.enabled is true, since it's used to build the Keycloak token-exchange request. Not used, and not required, when only roles.enabled is true — roles-only configs never call Keycloak.
  • If neither roles.enabled nor permissions.enabled is true, the plugin still requires and parses a bearer JWT but performs no gating and forwards every request that carries a parseable token — only headerMap still applies. Set at least one of them to actually enforce authorization.
  • roles.enabled — when true, extracts roles from the request token's resource_access claim directly (no network call to Keycloak). Roles from realm_access are not included. All clients present in resource_access are included — there's no audience filter for roles. Rejects the request (403) if no roles are found at all, unless roles.enforce is false (see below).
  • roles.enforce — default true. When false, roles are still extracted and forwarded via roles.headerName, but never gate the request: an empty role list and unmatched some/every are both allowed through. Use this when a downstream service already performs its own per-route authorization from the forwarded header/claims (e.g. app-level guards keyed on role or permission, fed by this plugin's output) and this plugin's job is reduced to authentication-adjacent extraction rather than gating — for example a single shared middleware fronting routes that require either a role or a permission, never unconditionally both, where a blanket non-empty check on either block would incorrectly reject legitimate callers that only carry one of the two (e.g. a machine-to-machine client authorized purely via permissions, with no roles at all).
  • roles.headerName — header the roles are written to. Format: client:role,client:role2,client2:role3 — clients and roles sorted alphabetically. Role and client names must not contain , or :.
  • roles.some — if non-empty, and roles.enforce is true, at least one of these client:role pairs must be present, or the request is rejected (403). Ignored when roles.enforce is false.
  • roles.every — if non-empty, and roles.enforce is true, all of these client:role pairs must be present, or the request is rejected (403). Ignored when roles.enforce is false.
  • permissions.enabled — when true, exchanges the request token with Keycloak's token endpoint (urn:ietf:params:oauth:grant-type:uma-ticket) for a permissions-bearing token, scoped to permissions.audience. Rejects the request (403) if the resulting permissions list is empty, unless permissions.enforce is false (see below).
  • permissions.enforce — default true. Same semantics as roles.enforce, applied to the permissions block: when false, permissions are still fetched from Keycloak and forwarded via permissions.headerName, but an empty permissions list and unmatched some/every no longer reject the request.
  • permissions.headerName — header the permissions are written to. Format: resource:scope,resource2:scope2.
  • permissions.audience — the Keycloak client id (resource server) to request permissions for. Required when permissions.enabled is true.
  • permissions.some — if non-empty, and permissions.enforce is true, at least one of these resource:scope pairs must be present, or the request is rejected (403). Ignored when permissions.enforce is false.
  • permissions.every — if non-empty, and permissions.enforce is true, all of these resource:scope pairs must be present, or the request is rejected (403). Ignored when permissions.enforce is false.
  • headerMap — maps arbitrary token claims to request headers (<header-name>: <claim-name>). Applied against the original request token, or against the Keycloak-issued token when permissions.enabled is true.

Documentation

Overview

Package traefik-keycloak-authorizer provides functionality for authorizing requests given a JWT token.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func New

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error)

New creates a new KeycloakAuthorizer plugin. revive:disable-next-line unused-parameter.

Types

type Config

type Config struct {
	Issuer      string            `json:"issuer,omitempty"`
	Roles       RolesConfig       `json:"roles,omitempty"`
	Permissions PermissionsConfig `json:"permissions,omitempty"`
	HeaderMap   map[string]string `json:"headerMap,omitempty"`
}

Config the plugin configuration.

func CreateConfig

func CreateConfig() *Config

CreateConfig creates the default plugin configuration.

type KeycloakAuthorizer

type KeycloakAuthorizer struct {
	// contains filtered or unexported fields
}

KeycloakAuthorizer plugin struct.

func (*KeycloakAuthorizer) ServeHTTP

func (p *KeycloakAuthorizer) ServeHTTP(rw http.ResponseWriter, r *http.Request)

type PermissionsConfig added in v1.0.0

type PermissionsConfig struct {
	Enabled    bool     `json:"enabled,omitempty"`
	Enforce    bool     `json:"enforce,omitempty"`
	HeaderName string   `json:"headerName,omitempty"`
	Audience   string   `json:"audience,omitempty"`
	Some       []string `json:"some,omitempty"`
	Every      []string `json:"every,omitempty"`
}

PermissionsConfig configures permissions extraction and gating.

type RolesConfig added in v1.0.0

type RolesConfig struct {
	Enabled    bool     `json:"enabled,omitempty"`
	Enforce    bool     `json:"enforce,omitempty"`
	HeaderName string   `json:"headerName,omitempty"`
	Some       []string `json:"some,omitempty"`
	Every      []string `json:"every,omitempty"`
}

RolesConfig configures roles extraction and gating.

Directories

Path Synopsis
internal
keycloak
Package keycloak provides keycloak functionalities.
Package keycloak provides keycloak functionalities.
util
Package util provides util functionalities.
Package util provides util functionalities.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL