Skip to content

Commit 48888c1

Browse files
committed
feat(sticker-award): validate tokens
1 parent 472a3da commit 48888c1

7 files changed

Lines changed: 188 additions & 37 deletions

File tree

e2e/tests/api/auth-required.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,25 @@ test.describe('API Authentication Requirements', () => {
5353
expect(response.ok()).toBeTruthy();
5454
});
5555
});
56+
57+
test.describe('Sticker Award - Protected Endpoints', () => {
58+
test('POST /api/awards/v1/assignments/{userId} requires authentication', async ({ request }) => {
59+
const response = await request.post('/api/awards/v1/assignments/user-001', {
60+
data: {
61+
stickerId: 'sticker-001',
62+
},
63+
});
64+
expect(response.status()).toBe(401);
65+
});
66+
67+
test('DELETE /api/awards/v1/assignments/{userId}/{stickerId} requires authentication', async ({ request }) => {
68+
const response = await request.delete('/api/awards/v1/assignments/user-001/sticker-001');
69+
expect(response.status()).toBe(401);
70+
});
71+
});
72+
73+
test('GET /api/awards/v1/assignments/{userId} requires authentication', async ({ request }) => {
74+
const response = await request.get('/api/awards/v1/assignments/user-001');
75+
expect(response.status()).toBe(401);
76+
});
5677
});

sticker-award/cmd/server/main.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
dd_logrus "github.com/DataDog/dd-trace-go/contrib/sirupsen/logrus/v2"
2121
"github.com/DataDog/dd-trace-go/v2/ddtrace/tracer"
2222
"github.com/DataDog/dd-trace-go/v2/profiler"
23+
"github.com/datadog/stickerlandia/sticker-award/internal/api/middleware"
2324
"github.com/datadog/stickerlandia/sticker-award/internal/api/router"
2425
"github.com/datadog/stickerlandia/sticker-award/internal/clients/catalogue"
2526
"github.com/datadog/stickerlandia/sticker-award/internal/config"
@@ -132,9 +133,21 @@ func main() {
132133
assignmentService := service.NewAssigner(assignmentRepo, catalogueClient, validator.New(), producer)
133134
log.Info("Assignment service initialized")
134135

136+
// Initialize JWT validator for authentication
137+
log.WithFields(log.Fields{
138+
"jwksUrl": cfg.Auth.JWKSUrl,
139+
"issuer": cfg.Auth.Issuer,
140+
}).Info("Initializing JWT validator...")
141+
jwtValidator, err := middleware.NewJWTValidator(&cfg.Auth)
142+
if err != nil {
143+
log.WithFields(log.Fields{"error": err}).Fatal("Failed to initialize JWT validator")
144+
}
145+
defer jwtValidator.Close()
146+
log.Info("JWT validator initialized")
147+
135148
// Initialize HTTP router
136149
log.Info("Setting up HTTP router...")
137-
r := router.Setup(db, cfg, assignmentService)
150+
r := router.Setup(db, cfg, assignmentService, jwtValidator)
138151
log.Info("HTTP router configured")
139152

140153
// Register our **user registered** event handler. This responds to new users appearing

sticker-award/go.mod

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@ require (
1010
github.com/DataDog/dd-trace-go/contrib/sirupsen/logrus/v2 v2.2.2
1111
github.com/DataDog/dd-trace-go/v2 v2.2.2
1212
github.com/IBM/sarama v1.45.2
13-
github.com/aws/aws-cdk-go/awscdk/v2 v2.232.2
13+
github.com/MicahParks/keyfunc/v3 v3.3.5
1414
github.com/aws/aws-sdk-go-v2 v1.39.5
1515
github.com/aws/aws-sdk-go-v2/config v1.31.16
1616
github.com/aws/aws-sdk-go-v2/service/eventbridge v1.45.9
1717
github.com/aws/aws-sdk-go-v2/service/sqs v1.42.12
18-
github.com/aws/constructs-go/constructs/v10 v10.4.4
19-
github.com/aws/jsii-runtime-go v1.121.0
2018
github.com/gin-gonic/gin v1.9.1
2119
github.com/go-playground/validator/v10 v10.15.5
20+
github.com/golang-jwt/jwt/v5 v5.2.1
2221
github.com/golang-migrate/migrate/v4 v4.16.2
2322
github.com/google/uuid v1.6.0
2423
github.com/sirupsen/logrus v1.9.3
@@ -52,6 +51,7 @@ require (
5251
github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.27.0 // indirect
5352
github.com/DataDog/sketches-go v1.4.7 // indirect
5453
github.com/Masterminds/semver/v3 v3.4.0 // indirect
54+
github.com/MicahParks/jwkset v0.5.19 // indirect
5555
github.com/Microsoft/go-winio v0.6.2 // indirect
5656
github.com/aws/aws-sdk-go-v2/credentials v1.18.20 // indirect
5757
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.12 // indirect
@@ -67,9 +67,6 @@ require (
6767
github.com/aws/smithy-go v1.23.1 // indirect
6868
github.com/bytedance/sonic v1.12.0 // indirect
6969
github.com/bytedance/sonic/loader v0.2.0 // indirect
70-
github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.242 // indirect
71-
github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0 // indirect
72-
github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v48 v48.20.0 // indirect
7370
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
7471
github.com/cespare/xxhash/v2 v2.3.0 // indirect
7572
github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect
@@ -91,7 +88,6 @@ require (
9188
github.com/eapache/queue v1.1.0 // indirect
9289
github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect
9390
github.com/ebitengine/purego v0.8.4 // indirect
94-
github.com/fatih/color v1.18.0 // indirect
9591
github.com/felixge/httpsnoop v1.0.4 // indirect
9692
github.com/fsnotify/fsnotify v1.6.0 // indirect
9793
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
@@ -131,7 +127,6 @@ require (
131127
github.com/lib/pq v1.10.9 // indirect
132128
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
133129
github.com/magiconair/properties v1.8.10 // indirect
134-
github.com/mattn/go-colorable v0.1.13 // indirect
135130
github.com/mattn/go-isatty v0.0.20 // indirect
136131
github.com/mitchellh/mapstructure v1.5.0 // indirect
137132
github.com/moby/docker-image-spec v1.3.1 // indirect
@@ -172,7 +167,6 @@ require (
172167
github.com/tklauser/numcpus v0.9.0 // indirect
173168
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
174169
github.com/ugorji/go/codec v1.2.11 // indirect
175-
github.com/yuin/goldmark v1.4.13 // indirect
176170
github.com/yusufpapurcu/wmi v1.2.4 // indirect
177171
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
178172
go.opentelemetry.io/collector/component v1.31.0 // indirect
@@ -193,14 +187,12 @@ require (
193187
golang.org/x/arch v0.4.0 // indirect
194188
golang.org/x/crypto v0.41.0 // indirect
195189
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
196-
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
197190
golang.org/x/mod v0.27.0 // indirect
198191
golang.org/x/net v0.43.0 // indirect
199192
golang.org/x/sync v0.16.0 // indirect
200193
golang.org/x/sys v0.35.0 // indirect
201194
golang.org/x/text v0.28.0 // indirect
202195
golang.org/x/time v0.11.0 // indirect
203-
golang.org/x/tools v0.36.0 // indirect
204196
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
205197
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
206198
google.golang.org/grpc v1.73.0 // indirect

sticker-award/go.sum

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,13 @@ github.com/IBM/sarama v1.45.2 h1:8m8LcMCu3REcwpa7fCP6v2fuPuzVwXDAM2DOv3CBrKw=
9696
github.com/IBM/sarama v1.45.2/go.mod h1:ppaoTcVdGv186/z6MEKsMm70A5fwJfRTpstI37kVn3Y=
9797
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
9898
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
99+
github.com/MicahParks/jwkset v0.5.19 h1:XZCsgJv05DBCvxEHYEHlSafqiuVn5ESG0VRB331Fxhw=
100+
github.com/MicahParks/jwkset v0.5.19/go.mod h1:q8ptTGn/Z9c4MwbcfeCDssADeVQb3Pk7PnVxrvi+2QY=
101+
github.com/MicahParks/keyfunc/v3 v3.3.5 h1:7ceAJLUAldnoueHDNzF8Bx06oVcQ5CfJnYwNt1U3YYo=
102+
github.com/MicahParks/keyfunc/v3 v3.3.5/go.mod h1:SdCCyMJn/bYqWDvARspC6nCT8Sk74MjuAY22C7dCST8=
99103
github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
100104
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
101105
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
102-
github.com/aws/aws-cdk-go/awscdk/v2 v2.232.2 h1:pCNfP+b7ylf5AaGHOonCkfsJ1RcGuzDNHMucEvXoMY8=
103-
github.com/aws/aws-cdk-go/awscdk/v2 v2.232.2/go.mod h1:iNg8HWCTBI6HAhvtKYTA0+RAKa01M6I3dkEEEMn7RLM=
104106
github.com/aws/aws-sdk-go-v2 v1.39.5 h1:e/SXuia3rkFtapghJROrydtQpfQaaUgd1cUvyO1mp2w=
105107
github.com/aws/aws-sdk-go-v2 v1.39.5/go.mod h1:yWSxrnioGUZ4WVv9TgMrNUeLV3PFESn/v+6T/Su8gnM=
106108
github.com/aws/aws-sdk-go-v2/config v1.31.16 h1:E4Tz+tJiPc7kGnXwIfCyUj6xHJNpENlY11oKpRTgsjc=
@@ -131,23 +133,13 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4 h1:tBw2Qhf0kj4ZwtsVpDiVRU3z
131133
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.4/go.mod h1:Deq4B7sRM6Awq/xyOBlxBdgW8/Z926KYNNaGMW2lrkA=
132134
github.com/aws/aws-sdk-go-v2/service/sts v1.39.0 h1:C+BRMnasSYFcgDw8o9H5hzehKzXyAb9GY5v/8bP9DUY=
133135
github.com/aws/aws-sdk-go-v2/service/sts v1.39.0/go.mod h1:4EjU+4mIx6+JqKQkruye+CaigV7alL3thVPfDd9VlMs=
134-
github.com/aws/constructs-go/constructs/v10 v10.4.4 h1:LL7cFqtg3B4t0ut2shtGxxeubEc+uXY5DDoWoRBuLCs=
135-
github.com/aws/constructs-go/constructs/v10 v10.4.4/go.mod h1:BhiNi267cuLnCZpYK59k68wKYh5G5AF0SoVA8f4iyGo=
136-
github.com/aws/jsii-runtime-go v1.121.0 h1:21aE+9WvNOX/jYSToifEswBcxZElMKJxF6fdByvZzC0=
137-
github.com/aws/jsii-runtime-go v1.121.0/go.mod h1:67f+oydH0cMr//tkmNNj9QpKk02hNEEVu4CByxkpGB0=
138136
github.com/aws/smithy-go v1.23.1 h1:sLvcH6dfAFwGkHLZ7dGiYF7aK6mg4CgKA/iDKjLDt9M=
139137
github.com/aws/smithy-go v1.23.1/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
140138
github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3ls=
141139
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
142140
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
143141
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
144142
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
145-
github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.242 h1:S+uSK6PJ3gbS5imAcMT198W5a/kNbICkpLy0cpV7RO8=
146-
github.com/cdklabs/awscdk-asset-awscli-go/awscliv1/v2 v2.2.242/go.mod h1:1FHlu1VKVvrE/Bmcow4crPddJlOWhEXde/Zi4TcUhkA=
147-
github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0 h1:kElXjprC8wkpJu58vp+WFH6z0AJw4zitg5iSKJPKe3c=
148-
github.com/cdklabs/awscdk-asset-node-proxy-agent-go/nodeproxyagentv6/v2 v2.1.0/go.mod h1:JY4UnvNa1YDGQ4H5wohXTHl6YVY3uCDUWl4JYUrQfb8=
149-
github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v48 v48.20.0 h1:xIOiSJPMXeM6SxfmqtMms+TOwaxuSii5ZvUUBdCIvvQ=
150-
github.com/cdklabs/cloud-assembly-schema-go/awscdkcloudassemblyschema/v48 v48.20.0/go.mod h1:Mv/KtlUxCbyDI6hGu+YgEXn/nBsJ7WfQnUOw9zyBHvU=
151143
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
152144
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
153145
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -215,8 +207,6 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
215207
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
216208
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
217209
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
218-
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
219-
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
220210
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
221211
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
222212
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
@@ -258,6 +248,8 @@ github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
258248
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
259249
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
260250
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
251+
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
252+
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
261253
github.com/golang-migrate/migrate/v4 v4.16.2 h1:8coYbMKUyInrFk1lfGfRovTLAW7PhWp8qQDT2iKfuoA=
262254
github.com/golang-migrate/migrate/v4 v4.16.2/go.mod h1:pfcJX4nPHaVdc5nmdCikFBWtm+UBpiZjRNNsyBbp0/o=
263255
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
@@ -410,9 +402,6 @@ github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMD
410402
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
411403
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
412404
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
413-
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
414-
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
415-
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
416405
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
417406
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
418407
github.com/mdelapenya/tlscert v0.2.0 h1:7H81W6Z/4weDvZBNOfQte5GpIMo0lGYEeWbkGp5LJHI=
@@ -548,7 +537,6 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
548537
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
549538
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
550539
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
551-
github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE=
552540
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
553541
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
554542
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
@@ -663,8 +651,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu
663651
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
664652
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
665653
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
666-
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug=
667-
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
668654
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
669655
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
670656
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
@@ -786,7 +772,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
786772
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
787773
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
788774
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
789-
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
790775
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
791776
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
792777
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -865,8 +850,6 @@ golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4f
865850
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
866851
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
867852
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
868-
golang.org/x/tools v0.36.0 h1:kWS0uv/zsvHEle1LbV5LE8QujrxB3wfQyxHfhOk0Qkg=
869-
golang.org/x/tools v0.36.0/go.mod h1:WBDiHKJK8YgLHlcQPYQzNCkUxUypCaa5ZegCVutKm+s=
870853
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
871854
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
872855
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
2+
// This product includes software developed at Datadog (https://www.datadoghq.com/).
3+
// Copyright 2025-Present Datadog, Inc.
4+
5+
package middleware
6+
7+
import (
8+
"context"
9+
"net/http"
10+
"strings"
11+
"time"
12+
13+
"github.com/MicahParks/keyfunc/v3"
14+
"github.com/gin-gonic/gin"
15+
"github.com/golang-jwt/jwt/v5"
16+
log "github.com/sirupsen/logrus"
17+
18+
"github.com/datadog/stickerlandia/sticker-award/internal/config"
19+
)
20+
21+
// Claims represents the JWT claims we expect
22+
type Claims struct {
23+
jwt.RegisteredClaims
24+
Sub string `json:"sub"`
25+
Email string `json:"email,omitempty"`
26+
Name string `json:"name,omitempty"`
27+
}
28+
29+
// JWTValidator handles JWT token validation using JWKS
30+
type JWTValidator struct {
31+
jwks keyfunc.Keyfunc
32+
issuer string
33+
clockSkew time.Duration
34+
cancelFunc context.CancelFunc
35+
}
36+
37+
// NewJWTValidator creates a new JWT validator with the given configuration
38+
func NewJWTValidator(cfg *config.AuthConfig) (*JWTValidator, error) {
39+
// Create context with cancel for cleanup
40+
ctx, cancel := context.WithCancel(context.Background())
41+
42+
// Create JWKS keyfunc with background refresh
43+
jwks, err := keyfunc.NewDefaultCtx(ctx, []string{cfg.JWKSUrl})
44+
if err != nil {
45+
cancel()
46+
return nil, err
47+
}
48+
49+
return &JWTValidator{
50+
jwks: jwks,
51+
issuer: cfg.Issuer,
52+
clockSkew: time.Duration(cfg.ClockSkew) * time.Second,
53+
cancelFunc: cancel,
54+
}, nil
55+
}
56+
57+
// JWT returns a Gin middleware that validates JWT tokens
58+
func (v *JWTValidator) JWT() gin.HandlerFunc {
59+
return func(c *gin.Context) {
60+
// Extract token from Authorization header
61+
authHeader := c.GetHeader("Authorization")
62+
if authHeader == "" {
63+
log.WithContext(c.Request.Context()).Debug("Missing Authorization header")
64+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
65+
"error": "missing authorization header",
66+
})
67+
return
68+
}
69+
70+
// Check Bearer prefix
71+
if !strings.HasPrefix(authHeader, "Bearer ") {
72+
log.WithContext(c.Request.Context()).Debug("Invalid Authorization header format")
73+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
74+
"error": "invalid authorization header format",
75+
})
76+
return
77+
}
78+
79+
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
80+
81+
// Parse and validate the token
82+
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, v.jwks.Keyfunc,
83+
jwt.WithIssuer(v.issuer),
84+
jwt.WithLeeway(v.clockSkew),
85+
)
86+
87+
if err != nil {
88+
log.WithContext(c.Request.Context()).WithError(err).Debug("Token validation failed")
89+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
90+
"error": "invalid token",
91+
})
92+
return
93+
}
94+
95+
if !token.Valid {
96+
log.WithContext(c.Request.Context()).Debug("Token is not valid")
97+
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{
98+
"error": "invalid token",
99+
})
100+
return
101+
}
102+
103+
// Extract claims and store in context
104+
if claims, ok := token.Claims.(*Claims); ok {
105+
c.Set("user_id", claims.Sub)
106+
c.Set("user_email", claims.Email)
107+
c.Set("user_name", claims.Name)
108+
c.Set("claims", claims)
109+
}
110+
111+
c.Next()
112+
}
113+
}
114+
115+
// Close releases resources used by the validator
116+
func (v *JWTValidator) Close() {
117+
if v.cancelFunc != nil {
118+
v.cancelFunc()
119+
}
120+
}

0 commit comments

Comments
 (0)