Skip to content

Commit 8d467da

Browse files
authored
Merge pull request #644 from thivindu/api-key
Implement API key hashing
2 parents 4f7009d + c0fcbfe commit 8d467da

File tree

28 files changed

+3909
-855
lines changed

28 files changed

+3909
-855
lines changed

docs/gateway/policies/apikey-authentication.md

Lines changed: 134 additions & 96 deletions
Large diffs are not rendered by default.

gateway/configs/config.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,17 @@ gateway_controller:
217217
# Expected issuer value in the JWT `iss` claim
218218
issuer: ""
219219

220+
# API key hashing configuration
221+
# Used to hash API keys before storing/comparing them
222+
# By default, API keys hashing is enabled with SHA-256 algorithm
223+
# Supported algorithms: "sha256", "bcrypt", "argon2id"
224+
# By default a user can have up to 10 API keys per API
225+
api_key:
226+
# Number of API keys allowed per user per API
227+
api_keys_per_user_per_api: 10
228+
# Hashing algorithm: "sha256", "bcrypt", "argon2id"
229+
algorithm: sha256
230+
220231
# Logging configuration
221232
logging:
222233
# Log level: "debug", "info", "warn", or "error"

gateway/gateway-builder/go.mod

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@ require (
88
gopkg.in/yaml.v3 v3.0.1
99
)
1010

11-
require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
11+
require (
12+
golang.org/x/crypto v0.46.0 // indirect
13+
golang.org/x/sys v0.39.0 // indirect
14+
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
15+
)
1216

1317
// Local module replacements for Docker builds
1418
replace github.com/wso2/api-platform/sdk => ../../sdk

gateway/gateway-builder/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,12 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
77
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
88
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
99
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
10+
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
11+
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
1012
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
1113
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
14+
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
15+
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
1216
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
1317
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
1418
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=

gateway/gateway-controller/api/openapi.yaml

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -347,7 +347,7 @@ paths:
347347
schema:
348348
$ref: "#/components/schemas/ErrorResponse"
349349

350-
/apis/{id}/generate-api-key:
350+
/apis/{id}/api-keys:
351351
post:
352352
summary: Generate API key for an API
353353
description: |
@@ -402,7 +402,6 @@ paths:
402402
schema:
403403
$ref: "#/components/schemas/ErrorResponse"
404404

405-
/apis/{id}/api-keys:
406405
get:
407406
summary: Get the list of API keys for an API
408407
description: |
@@ -442,13 +441,13 @@ paths:
442441

443442
/apis/{id}/api-keys/{apiKeyName}/regenerate:
444443
post:
445-
summary: Rotate API key for an API
444+
summary: Regenerate API key for an API
446445
description: |
447-
Rotate the given API key for the specified API. The newly rotated key can be
446+
Regenerate the given API key for the specified API. The newly generated key can be
448447
used by clients to authenticate requests to the API if API Key validation
449448
policy is applied. The key is a 32-byte random value encoded in hexadecimal
450449
and prefixed with "apip_".
451-
operationId: rotateAPIKey
450+
operationId: regenerateAPIKey
452451
tags:
453452
- API Management
454453
parameters:
@@ -464,7 +463,7 @@ paths:
464463
in: path
465464
required: true
466465
description: |
467-
Name of the API key to rotate
466+
Name of the API key to regenerate
468467
schema:
469468
type: string
470469
example: weather-api-key
@@ -473,10 +472,10 @@ paths:
473472
content:
474473
application/yaml:
475474
schema:
476-
$ref: "#/components/schemas/APIKeyRotationRequest"
475+
$ref: "#/components/schemas/APIKeyRegenerationRequest"
477476
application/json:
478477
schema:
479-
$ref: "#/components/schemas/APIKeyRotationRequest"
478+
$ref: "#/components/schemas/APIKeyRegenerationRequest"
480479
responses:
481480
'200':
482481
description: API key rotated successfully
@@ -503,8 +502,8 @@ paths:
503502
schema:
504503
$ref: "#/components/schemas/ErrorResponse"
505504

506-
/apis/{id}/revoke-api-key:
507-
post:
505+
/apis/{id}/api-keys/{apiKeyName}:
506+
delete:
508507
summary: Revoke an API key
509508
description: |
510509
Revoke a previously generated API key for the specified API. Once revoked,
@@ -521,15 +520,14 @@ paths:
521520
schema:
522521
type: string
523522
example: weather-api-v1.0
524-
requestBody:
525-
required: true
526-
content:
527-
application/yaml:
528-
schema:
529-
$ref: "#/components/schemas/APIKeyRevocationRequest"
530-
application/json:
531-
schema:
532-
$ref: "#/components/schemas/APIKeyRevocationRequest"
523+
- name: apiKeyName
524+
in: path
525+
required: true
526+
description: |
527+
Name of the API key to revoke
528+
schema:
529+
type: string
530+
example: weather-api-key
533531
responses:
534532
'200':
535533
description: API key revoked successfully
@@ -2243,6 +2241,10 @@ components:
22432241
message:
22442242
type: string
22452243
example: API key generated successfully
2244+
remaining_api_key_quota:
2245+
type: integer
2246+
description: Remaining API key quota for the user
2247+
example: 9
22462248
api_key:
22472249
$ref: '#/components/schemas/APIKey'
22482250
required:
@@ -2299,7 +2301,7 @@ components:
22992301
- created_at
23002302
- created_by
23012303
- expires_at
2302-
APIKeyRotationRequest:
2304+
APIKeyRegenerationRequest:
23032305
type: object
23042306
properties:
23052307
expires_in:
@@ -2329,15 +2331,6 @@ components:
23292331
format: date-time
23302332
description: Expiration timestamp
23312333
example: "2026-12-08T10:30:00Z"
2332-
APIKeyRevocationRequest:
2333-
type: object
2334-
properties:
2335-
api_key:
2336-
type: string
2337-
description: API key to be revoked
2338-
example: "apip_1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"
2339-
required:
2340-
- api_key
23412334
APIKeyRevocationResponse:
23422335
type: object
23432336
properties:

gateway/gateway-controller/cmd/controller/main.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ func main() {
114114

115115
// Initialize in-memory API key store for xDS
116116
apiKeyStore := storage.NewAPIKeyStore(log)
117+
apiKeySnapshotManager := apikeyxds.NewAPIKeySnapshotManager(apiKeyStore, log)
118+
apiKeyXDSManager := apikeyxds.NewAPIKeyStateManager(apiKeyStore, apiKeySnapshotManager, log)
117119

118120
// Load configurations from database on startup (if persistent mode)
119121
if cfg.IsPersistentMode() && db != nil {
@@ -131,7 +133,7 @@ func main() {
131133
if err := storage.LoadAPIKeysFromDatabase(db, configStore, apiKeyStore); err != nil {
132134
log.Fatal("Failed to load API keys from database", zap.Error(err))
133135
}
134-
log.Info("Loaded API keys", zap.Int("count", apiKeyStore.Count()))
136+
log.Info("Loaded API keys", zap.Int("count", apiKeyXDSManager.GetAPIKeyCount()))
135137
}
136138

137139
// Initialize xDS snapshot manager with router config
@@ -174,12 +176,10 @@ func main() {
174176
}
175177
}()
176178

177-
apiKeySnapshotManager := apikeyxds.NewAPIKeySnapshotManager(apiKeyStore, log)
178-
apiKeyXDSManager := apikeyxds.NewAPIKeyStateManager(apiKeyStore, apiKeySnapshotManager, log)
179-
180179
// Generate initial API key snapshot if API keys were loaded from database
181-
if cfg.IsPersistentMode() && apiKeyStore.Count() > 0 {
182-
log.Info("Generating initial API key snapshot for policy engine", zap.Int("api_key_count", apiKeyStore.Count()))
180+
if cfg.IsPersistentMode() && apiKeyXDSManager.GetAPIKeyCount() > 0 {
181+
log.Info("Generating initial API key snapshot for policy engine",
182+
zap.Int("api_key_count", apiKeyXDSManager.GetAPIKeyCount()))
183183
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
184184
if err := apiKeySnapshotManager.UpdateSnapshot(ctx); err != nil {
185185
log.Warn("Failed to generate initial API key snapshot", zap.Error(err))
@@ -432,10 +432,10 @@ func generateAuthConfig(config *config.Config) commonmodels.AuthConfig {
432432
"PUT /llm-proxies/:id": {"admin", "developer"},
433433
"DELETE /llm-proxies/:id": {"admin", "developer"},
434434

435-
"POST /apis/:id/generate-api-key": {"admin", "consumer"},
435+
"POST /apis/:id/api-keys": {"admin", "consumer"},
436436
"GET /apis/:id/api-keys": {"admin", "consumer"},
437437
"POST /apis/:id/api-keys/:apiKeyName/regenerate": {"admin", "consumer"},
438-
"POST /apis/:id/revoke-api-key": {"admin", "consumer"},
438+
"DELETE /apis/:id/api-keys/:apiKeyName": {"admin", "consumer"},
439439

440440
"GET /config_dump": {"admin"},
441441
}

gateway/gateway-controller/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ require (
2121
github.com/wso2/api-platform/sdk v0.0.0
2222
github.com/xeipuuv/gojsonschema v1.2.0
2323
go.uber.org/zap v1.27.1
24+
golang.org/x/crypto v0.46.0
2425
google.golang.org/grpc v1.78.0
2526
google.golang.org/protobuf v1.36.11
2627
gopkg.in/yaml.v3 v3.0.1
@@ -87,7 +88,6 @@ require (
8788
go.yaml.in/yaml/v2 v2.4.2 // indirect
8889
go.yaml.in/yaml/v3 v3.0.4 // indirect
8990
golang.org/x/arch v0.23.0 // indirect
90-
golang.org/x/crypto v0.46.0 // indirect
9191
golang.org/x/net v0.48.0 // indirect
9292
golang.org/x/sys v0.39.0 // indirect
9393
golang.org/x/text v0.32.0 // indirect

0 commit comments

Comments
 (0)