Skip to content
Merged
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
34 changes: 34 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,16 +1,50 @@
# App
# Options: development, test, production.
ENVIRONMENT=development
# Options: true or false; Swagger is disabled automatically in production.
ENABLE_SWAGGER=true
# Options: true or false.
ENABLE_METRICS=false
PORT=8080
# Comma-separated browser origins allowed by CORS.
ALLOWED_ORIGINS=http://localhost:3000
BASE_URL=http://localhost:8080
# Options: DEBUG, INFO, WARN, ERROR. WARNING is also accepted.
LOG_LEVEL=INFO
DOMAIN=localhost

# Database
DATABASE_URL=postgres://postgres:postgres@localhost:5432/auth_go?sslmode=disable

# Authentication rate limiting
# Options: memory, redis, postgres.
RATE_LIMIT_BACKEND=memory
RATE_LIMIT_KEY=change-me-rate-limit-key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -n -C 6 'RATE_LIMIT_KEY|change-me-rate-limit-key|production' internal/platform internal/bootstrap cmd

Repository: Jonathan0823/auth-go

Length of output: 7039


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- candidate files ---\n'
git ls-files | rg '(^|/)(rate_limit_config\.go|bootstrap|main\.go|Dockerfile|docker-compose|compose\.ya?ml|\.env\.example$)' || true

printf '\n--- rate_limit_config.go ---\n'
sed -n '1,140p' internal/platform/rate_limit_config.go

printf '\n--- usage of Validate and environment loading ---\n'
rg -n -C 4 'Validate\(|LoadRateLimitConfig|\bEnvironment\b|\bENVIRONMENT\b|RATE_LIMIT_KEY|change-me-rate-limit-key' -g '*.go' -g 'Dockerfile' -g 'docker-compose*.yml' -g 'compose*.ya?ml' -g '*.yaml' -g '*.yml' .

Repository: Jonathan0823/auth-go

Length of output: 249


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- candidate files ---'
git ls-files | grep -E '(^|/)(rate_limit_config\.go|bootstrap|main\.go|Dockerfile|docker-compose|compose\.ya?ml|\.env\.example$)' || true

printf '%s\n' ''
printf '%s\n' '--- rate_limit_config.go ---'
sed -n '1,140p' internal/platform/rate_limit_config.go

printf '%s\n' ''
printf '%s\n' '--- usage of Validate and environment loading ---'
grep -RInE 'Validate\(|LoadRateLimitConfig|\b(Environment|EnvironmentFunc|Environment)\b|\b(ENVIRONMENT|MODE|APP_ENV)\b|RATE_LIMIT_KEY|change-me-rate-limit-key' -g '*.go' -g 'Dockerfile' -g 'docker-compose*.yml' -g 'compose*.ya?ml' -g '*.yaml' -g '*.yml' . || true

Repository: Jonathan0823/auth-go

Length of output: 5230


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- cmd/auth-go/main.go ---'
sed -n '1,220p' cmd/auth-go/main.go

echo ''
echo '--- internal/bootstrap/ratelimit.go ---'
sed -n '1,180p' internal/bootstrap/ratelimit.go

echo ''
echo '--- internal/bootstrap/bootstrap.go ---'
sed -n '1,220p' internal/bootstrap/bootstrap.go

echo ''
echo '--- Dockerfile ---'
sed -n '1,220p' Dockerfile

echo ''
echo '--- docker-compose.yml ---'
sed -n '1,220p' docker-compose.yml

echo ''
echo '--- .env.example ---'
sed -n '1,80p' .env.example

Repository: Jonathan0823/auth-go

Length of output: 7537


Reject the placeholder rate-limit HMAC key in production.

RATE_LIMIT_KEY is loaded as-is and only fails validation when empty, so change-me-rate-limit-key can pass startup validation if copied into production. Add a production validation rule that rejects the placeholder and requires a deployment-specific random value.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.env.example at line 22, Update the RATE_LIMIT_KEY configuration validation
to reject the default placeholder value change-me-rate-limit-key when running in
production, while continuing to require a non-empty deployment-specific random
value. Locate the existing startup validation for RATE_LIMIT_KEY and add this
production-only check without changing development behavior.

# Positive integer; maximum number of keys held by the memory backend.
RATE_LIMIT_MAX_MEMORY_KEYS=10000
# Options: true or false; keep false in production unless explicitly approved.
RATE_LIMIT_ALLOW_MEMORY_PRODUCTION=false
# Redis host and port, for example localhost:6379.
REDIS_ADDR=localhost:6379
# Leave blank when Redis authentication is disabled.
REDIS_PASSWORD=
# Non-negative Redis logical database index.
REDIS_DB=0
# Comma-separated trusted IP addresses or CIDRs; blank trusts only the direct peer.
TRUSTED_PROXIES=
# Policy format: <limit>/<Go duration>, for example 30/1m.
RATE_LIMIT_LOGIN_IP=30/1m
RATE_LIMIT_LOGIN_ACCOUNT=5/15m
RATE_LIMIT_REGISTER_IP=5/1h
RATE_LIMIT_REGISTER_EMAIL=5/1h
RATE_LIMIT_RECOVERY_IP=10/1h
RATE_LIMIT_RECOVERY_EMAIL=3/1h
RATE_LIMIT_RECOVERY_TOKEN=3/1h
RATE_LIMIT_REFRESH_IP=30/1m
RATE_LIMIT_VERIFY_IP=10/1h
RATE_LIMIT_VERIFY_EMAIL=3/1h
RATE_LIMIT_OAUTH_IP=10/1m

# Auth
JWT_ACCESS_SECRET=change-me
REFRESH_TOKEN_HASH_KEY=change-me-too
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ jobs:
- name: Test unit suite
run: go test ./...

- name: Test race detector
run: go test -race ./...

integration:
runs-on: ubuntu-latest
services:
Expand All @@ -128,6 +131,15 @@ jobs:
--health-interval 3s
--health-timeout 3s
--health-retries 5
redis:
image: redis:7-alpine
ports:
- 6379:6379
options: >-
--health-cmd "redis-cli ping"
--health-interval 3s
--health-timeout 3s
--health-retries 5

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -159,6 +171,9 @@ jobs:
- name: Run integration tests
env:
DATABASE_URL: postgres://postgres:postgres@localhost:5432/auth_go?sslmode=disable
REDIS_ADDR: localhost:6379
REDIS_PASSWORD: ""
REDIS_DB: "0"
JWT_ACCESS_SECRET: ci-test-access-secret
REFRESH_TOKEN_HASH_KEY: ci-test-refresh-hash-key
ENVIRONMENT: test
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ tasks/
.pi/
.scannerwork/
auth-go
coverage/
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,21 @@ MIGRATE_DIR ?= migrations
MIGRATE ?= migrate
SWAG_VERSION ?= v1.16.6
REDOCLY_VERSION ?= v2.43.1
COVERAGE_DIR ?= coverage
COVERAGE_FILE ?= $(COVERAGE_DIR)/coverage.out
Comment on lines +11 to +12

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Create the parent directory for COVERAGE_FILE.

COVERAGE_FILE can be overridden independently from COVERAGE_DIR, but the recipe creates only COVERAGE_DIR. make COVERAGE_FILE=artifacts/coverage.out coverage fails when artifacts/ does not exist.

Create the configured file's parent directory before running go test.

Proposed fix
 coverage: migrate-up
-	mkdir -p "$(COVERAGE_DIR)"
+	mkdir -p "$(COVERAGE_DIR)" "$(dir $(COVERAGE_FILE))"
 	go test -tags=integration ./... -covermode=atomic -coverprofile="$(COVERAGE_FILE)"

Also applies to: 22-24

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@Makefile` around lines 11 - 12, Update the coverage recipe that runs go test
to create the parent directory of the configured COVERAGE_FILE before writing
coverage output, rather than relying only on COVERAGE_DIR. Preserve support for
the default COVERAGE_FILE and independently overridden paths.


.PHONY: build test integration vet run sqlc swagger swagger-validate migrate-up migrate-down migrate-down-1 fmt
.PHONY: build test coverage integration vet run sqlc swagger swagger-validate migrate-up migrate-down migrate-down-1 fmt

build:
go build ./cmd/auth-go

test:
go test ./...

coverage: migrate-up
mkdir -p "$(COVERAGE_DIR)"
go test -tags=integration ./... -covermode=atomic -coverprofile="$(COVERAGE_FILE)"

integration:
$(MAKE) migrate-up
go test -tags=integration ./...
Expand Down
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ cd auth-go
cp .env.example .env

go mod download
docker compose up -d db
docker compose up -d db redis
make migrate-up
make run
```
Expand Down Expand Up @@ -67,6 +67,8 @@ EMAIL=
PASSWORD=
```

Authentication rate limiting supports `memory`, `redis`, and `postgres` backends. Memory is intended for development or an explicitly approved single-instance deployment; production should use Redis or PostgreSQL. Set `RATE_LIMIT_KEY` to a separate secret and configure `TRUSTED_PROXIES` only for known proxy networks.

`make` loads `.env` automatically. Use another file explicitly when needed:

```bash
Expand All @@ -78,14 +80,15 @@ ENV_FILE=.env.prod make run
```bash
make migrate-up # Apply migrations
make test # Unit tests
make integration # Migrate + PostgreSQL integration tests
make integration # Migrate + PostgreSQL/Redis integration tests
make coverage # Integration tests + coverage/coverage.out
make vet # Static checks
make sqlc # Regenerate PostgreSQL code
make swagger # Regenerate Swagger artifacts
make swagger-validate # Validate the Swagger document
```

Integration tests use the `integration` build tag and require PostgreSQL:
Integration tests use the `integration` build tag and require PostgreSQL. Redis-backed tests run when `REDIS_ADDR` is configured:

```bash
go test -tags=integration ./...
Expand All @@ -97,6 +100,7 @@ go test -tags=integration ./...
- `GET /health/ready` reports database readiness with a bounded PostgreSQL ping.
- `GET /metrics` exposes Prometheus metrics only when `ENABLE_METRICS=true`; it is an operational endpoint and is intentionally excluded from Swagger.
- Security audit events are emitted as structured JSON logs with request IDs and safe categorical context.
- Rate-limit denials and backend failures use low-cardinality audit and Prometheus events without raw identifiers.

Metrics labels use route templates and avoid user-controlled values. Restrict `/metrics` to trusted monitoring systems in production. Grafana, Loki, and tracing infrastructure are intentionally not bundled.

Expand Down
13 changes: 11 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ services:
timeout: 3s
retries: 5

redis:
image: redis:7-alpine
container_name: auth-go-redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 3s
retries: 5
Comment on lines +17 to +24

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Expose Redis for host-side integration tests.

README.md Line 35 starts the Compose Redis service, and the example configuration uses REDIS_ADDR=localhost:6379. This service has no host port mapping, so host-side make integration and make coverage runs cannot reach Redis.

Proposed fix
   redis:
     image: redis:7-alpine
     container_name: auth-go-redis
+    ports:
+      - "6379:6379"
     healthcheck:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
redis:
image: redis:7-alpine
container_name: auth-go-redis
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 3s
retries: 5
redis:
image: redis:7-alpine
container_name: auth-go-redis
ports:
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 3s
timeout: 3s
retries: 5
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docker-compose.yml` around lines 17 - 24, Add a host-to-container port
mapping under the redis service so host-side integration and coverage tests can
reach Redis at localhost:6379, while preserving the existing healthcheck and
Redis image configuration.


auth-go:
image: auth-go:tidy
container_name: auth-go
Expand All @@ -24,10 +33,10 @@ services:
- "8080:8080"
env_file:
- .env.prod
environment:
DATABASE_URL: postgres://postgres:postgres@db:5432/auth_go?sslmode=disable
extra_hosts:
- "host.docker.internal:host-gateway"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ require (
github.com/jackc/pgx/v5 v5.10.0
github.com/joho/godotenv v1.5.1
github.com/markbates/goth v1.81.0
github.com/redis/go-redis/v9 v9.7.0
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.1
github.com/swaggo/swag v1.16.6
gopkg.in/gomail.v2 v2.0.0-20160411212932-81ebce5c23df
)

require github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ=
github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4=
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
Expand All @@ -22,6 +26,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
Expand Down Expand Up @@ -141,6 +147,8 @@ github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 h1:KRzFb2m7YtdldCEkzs6KqmJw4nqEVZGK7IN2kJkjTuQ=
Expand Down
34 changes: 34 additions & 0 deletions internal/adapter/inbound/http/auth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,12 @@ func (h *Handler) Register(c *gin.Context) {
return
}

if !h.allowRateLimit(c,
ipRateLimit("register_ip", c),
emailRateLimit("register_email", req.Email),
) {
return
}
user := domain.User{Email: req.Email, Password: req.Password}
if err := h.Svc.Auth.Register(ctx, user); err != nil {
h.auditFailure(c, platform.EventUserRegister, "", err, 0)
Expand Down Expand Up @@ -126,6 +132,12 @@ func (h *Handler) Login(c *gin.Context) {
return
}

if !h.allowRateLimit(c,
ipRateLimit("login_ip", c),
emailRateLimit("login_account", req.Email),
) {
return
}
user := domain.User{
Email: req.Email,
Password: req.Password,
Expand All @@ -139,6 +151,7 @@ func (h *Handler) Login(c *gin.Context) {
return
}

h.resetRateLimit(c, emailRateLimit("login_account", req.Email))
h.auditSuccess(c, platform.EventAuthLogin, "", 0)
setAccessCookie(c, accessToken)
setRefreshCookie(c, refreshToken)
Expand Down Expand Up @@ -195,6 +208,9 @@ func (h *Handler) Logout(c *gin.Context) {
func (h *Handler) Refresh(c *gin.Context) {
ctx, cancel := CtxWithTimeout(c)
defer cancel()
if !h.allowRateLimit(c, ipRateLimit("refresh_ip", c)) {
return
}
refreshToken, err := c.Cookie("refresh_token")
if err != nil || refreshToken == "" {
authErr := fmt.Errorf("refresh token not found: %w", domain.ErrUnauthenticated)
Expand Down Expand Up @@ -261,6 +277,12 @@ func (h *Handler) ResendVerifyEmail(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "Email is required"})
return
}
if !h.allowRateLimit(c,
ipRateLimit("verify_ip", c),
emailRateLimit("verify_email", email),
) {
return
}
if err := h.Svc.Auth.CreateVerifyEmail(ctx, email); err != nil {
h.auditFailure(c, platform.EventAuthEmailVerification, "", err, 0)
c.Error(err)
Expand Down Expand Up @@ -289,6 +311,12 @@ func (h *Handler) ForgotPassword(c *gin.Context) {
h.audit(c, platform.EventAuthPasswordResetRequest, platform.OutcomeFailure, platform.ReasonValidation, "", 0)
return
}
if !h.allowRateLimit(c,
ipRateLimit("recovery_ip", c),
emailRateLimit("recovery_email", req.Email),
) {
return
}
if err := h.Svc.Auth.ForgotPassword(ctx, req.Email); err != nil {
h.auditFailure(c, platform.EventAuthPasswordResetRequest, "", err, 0)
c.Error(err)
Expand Down Expand Up @@ -317,6 +345,12 @@ func (h *Handler) ResetPassword(c *gin.Context) {
h.audit(c, platform.EventAuthPasswordReset, platform.OutcomeFailure, platform.ReasonValidation, "", 0)
return
}
if !h.allowRateLimit(c,
ipRateLimit("recovery_ip", c),
tokenRateLimit("recovery_token", req.ID),
) {
return
}
if err := h.Svc.Auth.ResetPassword(ctx, req.ID, req.Password); err != nil {
h.auditFailure(c, platform.EventAuthPasswordReset, "", err, 0)
c.Error(err)
Expand Down
7 changes: 4 additions & 3 deletions internal/adapter/inbound/http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ import (
)

type Handler struct {
Svc port.Service
Tokens port.TokenService // used by middleware
Audit *platform.AuditLogger
Svc port.Service
Tokens port.TokenService // used by middleware
Audit *platform.AuditLogger
RateLimiter *platform.RateLimiter
}

func NewHandler(svc port.Service, tokens port.TokenService) *Handler {
Expand Down
6 changes: 6 additions & 0 deletions internal/adapter/inbound/http/oauth_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ import (
// @Failure 400 {object} ErrorResponse
// @Router /api/oauth/{provider}/ [get]
func (h *Handler) OAuthLogin(c *gin.Context) {
if !h.allowRateLimit(c, ipRateLimit("oauth_ip", c)) {
return
}
h.Svc.OAuth.BeginAuth(c.Writer, c.Request, c.Param("provider"))
}

Expand All @@ -36,6 +39,9 @@ func (h *Handler) OAuthLogin(c *gin.Context) {
func (h *Handler) OAuthCallback(c *gin.Context) {
ctx, cancel := CtxWithTimeout(c)
defer cancel()
if !h.allowRateLimit(c, ipRateLimit("oauth_ip", c)) {
return
}
userData, err := h.Svc.OAuth.OAuthLogin(ctx, c.Writer, c.Request, c.Param("provider"))
if err != nil {
authErr := fmt.Errorf("oauth authentication failed: %w", domain.ErrUnauthenticated)
Expand Down
Loading
Loading