Skip to content

Commit 864fcbb

Browse files
authored
Merge pull request #78 from BinHsu/feat/phase-4e-1-oidc-provider
feat(phase-4e-1): gateway OIDCProvider for Cognito JWT validation
2 parents 7c0ea82 + 76674b0 commit 864fcbb

7 files changed

Lines changed: 660 additions & 7 deletions

File tree

MODULE.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ use_repo(
184184
"com_github_coder_websocket",
185185
"com_github_golang_jwt_jwt_v5",
186186
"com_github_improbable_eng_grpc_web",
187+
"com_github_lestrrat_go_jwx_v2",
187188
"com_github_pion_webrtc_v4",
188189
"com_github_prometheus_client_golang",
189190
"org_golang_google_grpc",

gateway_go/cmd/gateway/main.go

Lines changed: 69 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,12 @@ import (
3333
"context"
3434
"encoding/json"
3535
"errors"
36+
"fmt"
3637
"net"
3738
"net/http"
3839
"os"
3940
"os/signal"
41+
"strings"
4042
"syscall"
4143
"time"
4244

@@ -268,12 +270,23 @@ func main() {
268270
die("gatewaygrpc.New", "err", err)
269271
}
270272

271-
// Auth provider: Local mode uses NoOp (synthetic "local" Principal
272-
// on every RPC); Cloud mode would swap in a StaticJWTProvider or
273-
// (future) a real Cognito client — see internal/auth for the port
274-
// definition and Phase 2 "Known Gaps" in ROADMAP.md for the
275-
// Cognito-integration scope that is descoped from this phase.
276-
authProvider := auth.NoOpProvider{}
273+
// Auth provider: picked by DEPLOY_MODE per ADR-0034 §LOCAL mode
274+
// posture. One binary, three modes, one env-var switch:
275+
//
276+
// local → NoOpProvider (synthetic "local" Principal; ADR-0007)
277+
// cloud → OIDCProvider (Cognito JWKS; ADR-0034 §D1)
278+
// cloud-test → StaticJWTProvider (HS256 pre-shared-secret;
279+
// Phase 2 A2 scaffold preserved for integration-
280+
// test scenarios)
281+
//
282+
// Empty / unset DEPLOY_MODE defaults to local to preserve Phase 3
283+
// LAN demo posture. Unrecognised values panic loudly rather than
284+
// silently degrade to NoOp — a typo like `DEPLOY_MODE=prod` must
285+
// not accidentally disable auth in a cloud deploy.
286+
authProvider, err := buildAuthProvider(processCtx)
287+
if err != nil {
288+
die("build auth provider", "err", err)
289+
}
277290

278291
// gRPC server for aegis.v1.Gateway. Interceptors fire in registration
279292
// order: auth first (attaches Principal to ctx); future additions
@@ -670,3 +683,53 @@ func corsAllowed(p *corspolicy.Policy, h http.HandlerFunc) http.HandlerFunc {
670683
h(w, r)
671684
}
672685
}
686+
687+
// buildAuthProvider selects the auth.Provider implementation at startup
688+
// based on the DEPLOY_MODE env var, per ADR-0034 §LOCAL mode posture.
689+
//
690+
// "" / "local" → NoOpProvider (synthetic "local" Principal; ADR-0007)
691+
// "cloud" → OIDCProvider (Cognito JWKS + claim mapping; ADR-0034 §D1)
692+
// "cloud-test" → StaticJWTProvider (HS256 pre-shared-secret; Phase 2 A2 scaffold)
693+
//
694+
// Unrecognised DEPLOY_MODE values return an error that causes main to
695+
// die() — a typo like `DEPLOY_MODE=prod` must not silently fall through
696+
// to NoOp in a cloud deploy.
697+
//
698+
// Required env vars per mode:
699+
//
700+
// local — (none)
701+
// cloud — AEGIS_COGNITO_ISSUER, AEGIS_COGNITO_AUDIENCE;
702+
// optional AEGIS_COGNITO_JWKS_URL (default derived from issuer).
703+
// cloud-test — AEGIS_JWT_STATIC_SECRET; optional AEGIS_JWT_STATIC_AUDIENCE.
704+
func buildAuthProvider(ctx context.Context) (auth.Provider, error) {
705+
mode := strings.ToLower(strings.TrimSpace(os.Getenv("DEPLOY_MODE")))
706+
switch mode {
707+
case "", "local":
708+
return auth.NoOpProvider{}, nil
709+
710+
case "cloud":
711+
issuer := os.Getenv("AEGIS_COGNITO_ISSUER")
712+
audience := os.Getenv("AEGIS_COGNITO_AUDIENCE")
713+
if issuer == "" || audience == "" {
714+
return nil, fmt.Errorf("DEPLOY_MODE=cloud requires AEGIS_COGNITO_ISSUER and AEGIS_COGNITO_AUDIENCE")
715+
}
716+
return auth.NewOIDCProvider(ctx, auth.OIDCConfig{
717+
Issuer: issuer,
718+
Audience: audience,
719+
JWKSURL: os.Getenv("AEGIS_COGNITO_JWKS_URL"),
720+
})
721+
722+
case "cloud-test":
723+
secret := os.Getenv("AEGIS_JWT_STATIC_SECRET")
724+
if secret == "" {
725+
return nil, errors.New("DEPLOY_MODE=cloud-test requires AEGIS_JWT_STATIC_SECRET")
726+
}
727+
return auth.StaticJWTProvider{
728+
Secret: []byte(secret),
729+
ExpectedAudience: os.Getenv("AEGIS_JWT_STATIC_AUDIENCE"),
730+
}, nil
731+
732+
default:
733+
return nil, fmt.Errorf("unrecognized DEPLOY_MODE %q (valid: local, cloud, cloud-test)", mode)
734+
}
735+
}

gateway_go/go.mod

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,16 @@ require (
2424
github.com/beorn7/perks v1.0.1 // indirect
2525
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
2626
github.com/cespare/xxhash/v2 v2.3.0 // indirect
27+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
2728
github.com/desertbit/timer v1.0.1 // indirect
29+
github.com/goccy/go-json v0.10.3 // indirect
2830
github.com/google/uuid v1.6.0 // indirect
2931
github.com/klauspost/compress v1.17.9 // indirect
32+
github.com/lestrrat-go/blackmagic v1.0.3 // indirect
33+
github.com/lestrrat-go/httpcc v1.0.1 // indirect
34+
github.com/lestrrat-go/httprc v1.0.6 // indirect
35+
github.com/lestrrat-go/iter v1.0.2 // indirect
36+
github.com/lestrrat-go/option v1.0.1 // indirect
3037
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
3138
github.com/pion/datachannel v1.6.0 // indirect
3239
github.com/pion/dtls/v3 v3.1.2 // indirect
@@ -47,6 +54,7 @@ require (
4754
github.com/prometheus/common v0.55.0 // indirect
4855
github.com/prometheus/procfs v0.15.1 // indirect
4956
github.com/rs/cors v1.11.1 // indirect
57+
github.com/segmentio/asm v1.2.0 // indirect
5058
github.com/wlynxg/anet v0.0.5 // indirect
5159
golang.org/x/crypto v0.48.0 // indirect
5260
golang.org/x/time v0.10.0 // indirect
@@ -57,6 +65,7 @@ require (
5765
github.com/coder/websocket v1.8.14
5866
github.com/golang-jwt/jwt/v5 v5.3.1
5967
github.com/improbable-eng/grpc-web v0.15.0
68+
github.com/lestrrat-go/jwx/v2 v2.1.6
6069
github.com/pion/webrtc/v4 v4.2.11
6170
github.com/prometheus/client_golang v1.20.5
6271
golang.org/x/net v0.50.0 // indirect

gateway_go/go.sum

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7Do
5353
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
5454
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5555
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
56+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
57+
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
5658
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
5759
github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo=
5860
github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE=
@@ -94,6 +96,8 @@ github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/me
9496
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
9597
github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
9698
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
99+
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
100+
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
97101
github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
98102
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
99103
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -196,6 +200,18 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
196200
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
197201
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
198202
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
203+
github.com/lestrrat-go/blackmagic v1.0.3 h1:94HXkVLxkZO9vJI/w2u1T0DAoprShFd13xtnSINtDWs=
204+
github.com/lestrrat-go/blackmagic v1.0.3/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw=
205+
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
206+
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
207+
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
208+
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
209+
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
210+
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
211+
github.com/lestrrat-go/jwx/v2 v2.1.6 h1:hxM1gfDILk/l5ylers6BX/Eq1m/pnxe9NBwW6lVfecA=
212+
github.com/lestrrat-go/jwx/v2 v2.1.6/go.mod h1:Y722kU5r/8mV7fYDifjug0r8FK8mZdw0K0GpJw/l8pU=
213+
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
214+
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
199215
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
200216
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
201217
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
@@ -332,6 +348,8 @@ github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
332348
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
333349
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
334350
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
351+
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
352+
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
335353
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
336354
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
337355
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -352,7 +370,9 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
352370
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
353371
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
354372
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
373+
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
355374
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
375+
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
356376
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
357377
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
358378
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=

gateway_go/internal/auth/BUILD.bazel

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,14 @@ go_library(
77
"interceptor.go",
88
"jwt.go",
99
"noop.go",
10+
"oidc_provider.go",
1011
],
1112
importpath = "github.com/BinHsu/aegis-core/gateway_go/internal/auth",
1213
visibility = ["//gateway_go:__subpackages__"],
1314
deps = [
1415
"@com_github_golang_jwt_jwt_v5//:jwt",
16+
"@com_github_lestrrat_go_jwx_v2//jwk",
17+
"@com_github_lestrrat_go_jwx_v2//jwt",
1518
"@org_golang_google_grpc//:grpc",
1619
"@org_golang_google_grpc//codes",
1720
"@org_golang_google_grpc//metadata",
@@ -22,10 +25,16 @@ go_library(
2225
go_test(
2326
name = "auth_test",
2427
size = "small",
25-
srcs = ["auth_test.go"],
28+
srcs = [
29+
"auth_test.go",
30+
"oidc_provider_test.go",
31+
],
2632
embed = [":auth"],
2733
deps = [
2834
"@com_github_golang_jwt_jwt_v5//:jwt",
35+
"@com_github_lestrrat_go_jwx_v2//jwa",
36+
"@com_github_lestrrat_go_jwx_v2//jwk",
37+
"@com_github_lestrrat_go_jwx_v2//jwt",
2938
"@org_golang_google_grpc//:grpc",
3039
"@org_golang_google_grpc//codes",
3140
"@org_golang_google_grpc//metadata",

0 commit comments

Comments
 (0)