Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
go-version: '1.25.6'

- name: Download dependencies
run: go mod download
Expand All @@ -38,13 +38,13 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
go-version: '1.25.6'

- name: Install golangci-lint
run: go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.64.8
run: go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.1

- name: Run golangci-lint
run: golangci-lint run --out-format=colored-line-number
run: golangci-lint run

build:
runs-on: ubuntu-latest
Expand All @@ -54,7 +54,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.25'
go-version: '1.25.6'

- name: Build binaries
run: |
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ bin/
test-values-gatekeeperd.yaml
test-values-relay.yaml
coverage.out
coverage.html
coverage*.out

# Ephemeral planning docs
PLAN.md
Expand Down
64 changes: 40 additions & 24 deletions .golangci.yml
Original file line number Diff line number Diff line change
@@ -1,47 +1,63 @@
# golangci-lint configuration
# https://golangci-lint.run/usage/configuration/

version: "2"

run:
timeout: 5m
tests: true

linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- gofmt
- goimports
- misspell
- unconvert
- unparam
- gocritic
settings:
misspell:
locale: US
gocritic:
enabled-tags:
- diagnostic
- style
- performance
disabled-checks:
- hugeParam # Allow passing large structs by value
- rangeValCopy # Allow copying in range loops
- octalLiteral # 0644 vs 0o644 is a style preference
- emptyStringTest # len(s) == 0 vs s == "" is a style preference
- deprecatedComment # Strict comment formatting is optional
- httpNoBody # nil vs http.NoBody is a style preference in tests
exclusions:
rules:
# Test files: relax errcheck (os.Setenv/Unsetenv and Close errors are
# benign in tests) and unparam (unused params in goroutines are common)
- path: "_test\\.go"
linters:
- unparam
- errcheck
# Deferred Close() calls on HTTP response bodies and connections are
# conventionally not checked; errors are not actionable in a defer
- source: "defer (resp|pubsub|client|m\\.client|httpResp)\\..*Close"
linters:
- errcheck

linters-settings:
gofmt:
simplify: true
goimports:
local-prefixes: github.com/tight-line/gatekeeper
misspell:
locale: US
gocritic:
enabled-tags:
- diagnostic
- style
- performance
disabled-checks:
- hugeParam # Allow passing large structs by value
- rangeValCopy # Allow copying in range loops
formatters:
enable:
- gofmt
- goimports
settings:
gofmt:
simplify: true
goimports:
local-prefixes:
- github.com/tight-line/gatekeeper

issues:
exclude-rules:
# Exclude some linters from running on tests files
- path: _test\.go
linters:
- unparam
- gocritic
max-issues-per-linter: 0
max-same-issues: 0
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- `oidc` verifier type for OIDC JWT bearer token authentication. Supports any OIDC-compliant identity provider (Google, Azure AD, etc.) via issuer discovery or explicit JWKS URI configuration. Handles both standard JWK Set format and Google X.509 certificate map format. Configurable audience, required claims, and automatic key caching with refresh.

## [0.2.8] - 2026-02-17

### Added
Expand Down
31 changes: 31 additions & 0 deletions config/example.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,37 @@ verifiers:
type: gitlab
token: "${GITLAB_WEBHOOK_TOKEN}"

# OIDC JWT bearer token verification
# Supports any OIDC-compliant provider (Google, Azure AD, etc.)
# The jwks_uri is optional: if omitted, it is auto-discovered from the issuer's
# /.well-known/openid-configuration endpoint.
#
# Example: Google Chat (using project-number audience mode)
# Configure Google Chat app with "Authentication Audience: Project number"
# so downstream services can re-validate with the same audience.
google-chat:
type: oidc
issuer: "chat@system.gserviceaccount.com"
audience: "${GCP_PROJECT_NUMBER}"
jwks_uri: "https://www.googleapis.com/service_accounts/v1/metadata/x509/chat@system.gserviceaccount.com"

# Example: Google Chat (using app-url audience mode) with OIDC discovery
# Configure Google Chat app with "Authentication Audience: HTTP endpoint URL"
google-chat-appurl:
type: oidc
issuer: "https://accounts.google.com"
audience: "https://hooks.example.com/googlechat"
# jwks_uri omitted: auto-discovered from https://accounts.google.com/.well-known/openid-configuration
claims:
email: "chat@system.gserviceaccount.com"

# Example: Azure Event Grid with Azure Active Directory (AAD) authentication
azure-eventgrid:
type: oidc
issuer: "https://sts.windows.net/${AZURE_TENANT_ID}/"
audience: "${AZURE_APP_ID_URI}"
# jwks_uri omitted: auto-discovered from AAD tenant metadata

# Shared noop verifier for testing/development
none:
type: noop
Expand Down
3 changes: 2 additions & 1 deletion docs/PROVIDER_TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Webhook providers we want to support in the future. Contributions welcome.
| GitLab | DevOps | `gitlab` |
| Shopify | E-commerce | `shopify` |
| Google Calendar | Productivity | `api_key` |
| Google Chat | Communication | `oidc` |
| Generic HMAC | Any | `hmac` |
| Generic API Key | Any | `api_key` |

Expand Down Expand Up @@ -52,7 +53,7 @@ Less common or requiring complex verification schemes.
| PayPal | Payments | Certificate-based | Requires fetching PayPal certs |
| Salesforce | CRM | Org-specific | Complex org validation |
| AWS SNS | Cloud | Certificate-based | X.509 signature verification |
| Azure Event Grid | Cloud | SAS token or AAD | Multiple auth schemes |
| Azure Event Grid | Cloud | SAS token or AAD (`oidc`) | AAD mode supported via oidc verifier; SAS token not yet implemented |
| Okta | Identity | HMAC-SHA256 | [link](https://developer.okta.com/docs/concepts/event-hooks/) |
| Auth0 | Identity | HMAC-SHA256 | [link](https://auth0.com/docs/customize/hooks) |
| Zoom | Communication | HMAC-SHA256 | [link](https://developers.zoom.us/docs/api/rest/webhook-reference/) |
Expand Down
4 changes: 1 addition & 3 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
module github.com/tight-line/gatekeeper

go 1.25.0

toolchain go1.25.1
go 1.25.6

require (
github.com/alicebob/miniredis/v2 v2.36.0
Expand Down
21 changes: 20 additions & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ type RateLimiterConfig struct {

// VerifierConfig defines a webhook signature verifier
type VerifierConfig struct {
Type string `yaml:"type"` // slack, github, gitlab, shopify, api_key, hmac, json_field, query_param, header_query_param, noop
Type string `yaml:"type"` // slack, github, gitlab, shopify, api_key, hmac, json_field, query_param, header_query_param, oidc, noop

// For slack verifier
SigningSecret string `yaml:"signing_secret,omitempty"`
Expand All @@ -76,6 +76,12 @@ type VerifierConfig struct {

// For query_param and header_query_param verifiers
Name string `yaml:"name,omitempty"` // query parameter name or key name within header

// For oidc verifier
Issuer string `yaml:"issuer,omitempty"`
Audience string `yaml:"audience,omitempty"`
JWKSUri string `yaml:"jwks_uri,omitempty"`
Claims map[string]string `yaml:"claims,omitempty"`
}

// ValidatorConfig defines a payload structure validator
Expand Down Expand Up @@ -269,6 +275,8 @@ func validateVerifier(name string, v VerifierConfig) error {
return validateQueryParamVerifier(name, v)
case "header_query_param":
return validateHeaderQueryParamVerifier(name, v)
case "oidc":
return validateOIDCVerifier(name, v)
case "noop":
// No validation needed
case "":
Expand Down Expand Up @@ -329,6 +337,17 @@ func validateQueryParamVerifier(name string, v VerifierConfig) error {
return nil
}

// validateOIDCVerifier validates oidc verifier config
func validateOIDCVerifier(name string, v VerifierConfig) error {
if v.Issuer == "" {
return fmt.Errorf("verifier %q: issuer is required for oidc verifier", name)
}
if v.Audience == "" {
return fmt.Errorf("verifier %q: audience is required for oidc verifier", name)
}
return nil
}

// validateHeaderQueryParamVerifier validates header_query_param verifier config
func validateHeaderQueryParamVerifier(name string, v VerifierConfig) error {
if v.Header == "" {
Expand Down
Loading
Loading