Skip to content

Commit e1a2d58

Browse files
committed
feat: Add logout functionality and security scanning tools
- Implemented user logout endpoint to revoke refresh tokens. - Added new Makefile commands for security scanning with gosec and nancy. - Created configuration file for gosec and updated README with new commands. - Introduced scripts for running TOTP tests securely without hardcoded credentials. - Enhanced API documentation to include logout functionality and related DTOs.
1 parent b9e73b7 commit e1a2d58

File tree

19 files changed

+582
-60
lines changed

19 files changed

+582
-60
lines changed

.gosec.json

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"severity": "medium",
3+
"confidence": "medium",
4+
"exclude-rules": [],
5+
"include-rules": [],
6+
"exclude-dirs": ["vendor/", "node_modules/"],
7+
"tests": true,
8+
"exclude-generated": true,
9+
"nosec": false,
10+
"nosec-tag": "nosec",
11+
"fmt": "json",
12+
"stdout": true,
13+
"verbose": false,
14+
"output": "",
15+
"log": ""
16+
}

Makefile

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,16 @@ dev:
1818
test:
1919
go test -v ./...
2020

21+
# Run the TOTP test (requires TEST_TOTP_SECRET environment variable)
22+
test-totp:
23+
@if [ -z "$$TEST_TOTP_SECRET" ]; then \
24+
echo "Error: TEST_TOTP_SECRET environment variable is required"; \
25+
echo "Set it with: export TEST_TOTP_SECRET=your_secret_here"; \
26+
echo "Or run: ./run_test_secret.sh"; \
27+
exit 1; \
28+
fi
29+
go run test_specific_secret.go
30+
2131
# Clean build artifacts and temporary files
2232
clean:
2333
rm -rf bin/
@@ -41,6 +51,22 @@ fmt:
4151
lint:
4252
golangci-lint run
4353

54+
# Install security scanning tools
55+
install-security-tools:
56+
go install github.com/securego/gosec/v2/cmd/gosec@latest
57+
go install github.com/sonatype-nexus-community/nancy@latest
58+
59+
# Run security scan with gosec
60+
security-scan:
61+
gosec -conf .gosec.json ./...
62+
63+
# Run vulnerability scan with nancy
64+
vulnerability-scan:
65+
go list -json -deps ./... | nancy sleuth
66+
67+
# Run all security checks
68+
security: security-scan vulnerability-scan
69+
4470
# Build for production
4571
build-prod:
4672
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o bin/api ./cmd/api
@@ -82,11 +108,16 @@ help:
82108
@echo " run - Run the application"
83109
@echo " dev - Run with hot reload (Air)"
84110
@echo " test - Run tests"
111+
@echo " test-totp - Run TOTP test (requires TEST_TOTP_SECRET env var)"
85112
@echo " clean - Clean build artifacts"
86113
@echo " install-air - Install Air for hot reloading"
87114
@echo " setup - Setup development environment"
88115
@echo " fmt - Format code"
89116
@echo " lint - Run linter"
117+
@echo " install-security-tools - Install gosec and nancy security scanners"
118+
@echo " security-scan - Run gosec security scanner"
119+
@echo " vulnerability-scan - Run nancy vulnerability scanner"
120+
@echo " security - Run all security checks"
90121
@echo " build-prod - Build for production"
91122
@echo " docker-dev - Run development environment with Docker"
92123
@echo " docker-compose-build - Build Docker images using docker-compose"

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,16 @@ The following `make` commands are available for development, testing, building,
106106
| `make run` | Run the application without hot reload. |
107107
| `make dev` | Run with hot reload using [Air](https://github.com/air-verse/air).|
108108
| `make test` | Run all Go tests with verbose output. |
109+
| `make test-totp` | Run TOTP test (requires `TEST_TOTP_SECRET` environment variable). |
109110
| `make clean` | Remove build artifacts and temporary files. |
110111
| `make install-air` | Install Air for hot reloading. |
111112
| `make setup` | Setup development environment (installs Air, tidy/download deps). |
112113
| `make fmt` | Format code using `go fmt`. |
113114
| `make lint` | Run linter (`golangci-lint`). |
115+
| `make install-security-tools` | Install security scanning tools (`gosec` and `nancy`). |
116+
| `make security-scan` | Run gosec security scanner. |
117+
| `make vulnerability-scan` | Run nancy vulnerability scanner. |
118+
| `make security` | Run all security checks (gosec + nancy). |
114119
| `make build-prod` | Build for production (Linux, static binary). |
115120
| `make docker-dev` | Run development environment with Docker (`./dev.sh`). |
116121
| `make docker-compose-build` | Build Docker images using docker-compose. |
@@ -128,6 +133,7 @@ The following `make` commands are available for development, testing, building,
128133
### Authentication
129134
- `POST /register` — User registration
130135
- `POST /login` — User login (with 2FA support)
136+
- `POST /logout` — User logout and token revocation (protected)
131137
- `POST /refresh-token` — Refresh JWT tokens
132138
- `GET /verify-email` — Email verification
133139
- `POST /forgot-password` — Request password reset

cmd/api/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func main() {
109109
protected.Use(middleware.AuthMiddleware())
110110
{
111111
protected.GET("/profile", userHandler.GetProfile)
112+
protected.POST("/logout", userHandler.Logout)
112113

113114
// 2FA management routes
114115
protected.POST("/2fa/generate", twofaHandler.Generate2FA)

docs/API.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,12 @@
1212
- Request: `{ "email": "[email protected]", "password": "..." }`
1313
- Response: `{ "success": true, "data": { "token": "..." } }`
1414

15+
### Logout
16+
- `POST /logout`
17+
- Header: `Authorization: Bearer <access_token>`
18+
- Request: `{ "refresh_token": "..." }`
19+
- Response: `{ "message": "Successfully logged out" }`
20+
1521
### Refresh Token
1622
- `POST /refresh-token`
1723
- Request: `{ "refresh_token": "..." }`

docs/docs.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,63 @@ const docTemplate = `{
683683
}
684684
}
685685
},
686+
"/logout": {
687+
"post": {
688+
"security": [
689+
{
690+
"ApiKeyAuth": []
691+
}
692+
],
693+
"description": "Logout user and revoke refresh token",
694+
"consumes": [
695+
"application/json"
696+
],
697+
"produces": [
698+
"application/json"
699+
],
700+
"tags": [
701+
"Auth"
702+
],
703+
"summary": "User logout",
704+
"parameters": [
705+
{
706+
"description": "Logout Data",
707+
"name": "logout",
708+
"in": "body",
709+
"required": true,
710+
"schema": {
711+
"$ref": "#/definitions/dto.LogoutRequest"
712+
}
713+
}
714+
],
715+
"responses": {
716+
"200": {
717+
"description": "OK",
718+
"schema": {
719+
"$ref": "#/definitions/dto.MessageResponse"
720+
}
721+
},
722+
"400": {
723+
"description": "Bad Request",
724+
"schema": {
725+
"$ref": "#/definitions/dto.ErrorResponse"
726+
}
727+
},
728+
"401": {
729+
"description": "Unauthorized",
730+
"schema": {
731+
"$ref": "#/definitions/dto.ErrorResponse"
732+
}
733+
},
734+
"500": {
735+
"description": "Internal Server Error",
736+
"schema": {
737+
"$ref": "#/definitions/dto.ErrorResponse"
738+
}
739+
}
740+
}
741+
}
742+
},
686743
"/profile": {
687744
"get": {
688745
"security": [
@@ -970,6 +1027,17 @@ const docTemplate = `{
9701027
}
9711028
}
9721029
},
1030+
"dto.LogoutRequest": {
1031+
"type": "object",
1032+
"required": [
1033+
"refresh_token"
1034+
],
1035+
"properties": {
1036+
"refresh_token": {
1037+
"type": "string"
1038+
}
1039+
}
1040+
},
9731041
"dto.MessageResponse": {
9741042
"type": "object",
9751043
"properties": {

docs/implementation_phases/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ This folder contains detailed plans for each phase of the project:
99
- Phase 5: API Endpoints and Middleware Implementation
1010
- Phase 6: Testing and Deployment Strategy
1111
- Phase 7: Documentation and Deployment
12+
- Phase 8: Two Factor Authentication Implementation

docs/swagger.json

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,63 @@
677677
}
678678
}
679679
},
680+
"/logout": {
681+
"post": {
682+
"security": [
683+
{
684+
"ApiKeyAuth": []
685+
}
686+
],
687+
"description": "Logout user and revoke refresh token",
688+
"consumes": [
689+
"application/json"
690+
],
691+
"produces": [
692+
"application/json"
693+
],
694+
"tags": [
695+
"Auth"
696+
],
697+
"summary": "User logout",
698+
"parameters": [
699+
{
700+
"description": "Logout Data",
701+
"name": "logout",
702+
"in": "body",
703+
"required": true,
704+
"schema": {
705+
"$ref": "#/definitions/dto.LogoutRequest"
706+
}
707+
}
708+
],
709+
"responses": {
710+
"200": {
711+
"description": "OK",
712+
"schema": {
713+
"$ref": "#/definitions/dto.MessageResponse"
714+
}
715+
},
716+
"400": {
717+
"description": "Bad Request",
718+
"schema": {
719+
"$ref": "#/definitions/dto.ErrorResponse"
720+
}
721+
},
722+
"401": {
723+
"description": "Unauthorized",
724+
"schema": {
725+
"$ref": "#/definitions/dto.ErrorResponse"
726+
}
727+
},
728+
"500": {
729+
"description": "Internal Server Error",
730+
"schema": {
731+
"$ref": "#/definitions/dto.ErrorResponse"
732+
}
733+
}
734+
}
735+
}
736+
},
680737
"/profile": {
681738
"get": {
682739
"security": [
@@ -964,6 +1021,17 @@
9641021
}
9651022
}
9661023
},
1024+
"dto.LogoutRequest": {
1025+
"type": "object",
1026+
"required": [
1027+
"refresh_token"
1028+
],
1029+
"properties": {
1030+
"refresh_token": {
1031+
"type": "string"
1032+
}
1033+
}
1034+
},
9671035
"dto.MessageResponse": {
9681036
"type": "object",
9691037
"properties": {

docs/swagger.yaml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ definitions:
2929
refresh_token:
3030
type: string
3131
type: object
32+
dto.LogoutRequest:
33+
properties:
34+
refresh_token:
35+
type: string
36+
required:
37+
- refresh_token
38+
type: object
3239
dto.MessageResponse:
3340
properties:
3441
message:
@@ -582,6 +589,42 @@ paths:
582589
summary: User login
583590
tags:
584591
- Auth
592+
/logout:
593+
post:
594+
consumes:
595+
- application/json
596+
description: Logout user and revoke refresh token
597+
parameters:
598+
- description: Logout Data
599+
in: body
600+
name: logout
601+
required: true
602+
schema:
603+
$ref: '#/definitions/dto.LogoutRequest'
604+
produces:
605+
- application/json
606+
responses:
607+
"200":
608+
description: OK
609+
schema:
610+
$ref: '#/definitions/dto.MessageResponse'
611+
"400":
612+
description: Bad Request
613+
schema:
614+
$ref: '#/definitions/dto.ErrorResponse'
615+
"401":
616+
description: Unauthorized
617+
schema:
618+
$ref: '#/definitions/dto.ErrorResponse'
619+
"500":
620+
description: Internal Server Error
621+
schema:
622+
$ref: '#/definitions/dto.ErrorResponse'
623+
security:
624+
- ApiKeyAuth: []
625+
summary: User logout
626+
tags:
627+
- Auth
585628
/profile:
586629
get:
587630
description: Retrieve authenticated user's profile information

internal/twofa/handler.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package twofa
22

33
import (
4+
"fmt"
45
"net/http"
56

67
"github.com/gin-gonic/gin"
@@ -272,5 +273,8 @@ func generateTokensForUser(userID string) (string, string, error) {
272273

273274
// clearTempSession clears the temporary 2FA session
274275
func clearTempSession(tempToken string) {
275-
redis.DeleteTempUserSession(tempToken)
276+
if err := redis.DeleteTempUserSession(tempToken); err != nil {
277+
// Log the error but don't fail the operation since the user is already authenticated
278+
fmt.Printf("Warning: Failed to delete temporary user session %s: %v\n", tempToken, err)
279+
}
276280
}

0 commit comments

Comments
 (0)