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
30 changes: 30 additions & 0 deletions certs/localhost-cert.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
-----BEGIN CERTIFICATE-----
MIIFKjCCAxICCQDBJ81DFfWunTANBgkqhkiG9w0BAQsFADBXMQswCQYDVQQGEwJV
UzEOMAwGA1UECAwFU3RhdGUxDTALBgNVBAcMBENpdHkxFTATBgNVBAoMDE9yZ2Fu
aXphdGlvbjESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI1MDkyMjExMzExN1oXDTI2
MDkyMjExMzExN1owVzELMAkGA1UEBhMCVVMxDjAMBgNVBAgMBVN0YXRlMQ0wCwYD
VQQHDARDaXR5MRUwEwYDVQQKDAxPcmdhbml6YXRpb24xEjAQBgNVBAMMCWxvY2Fs
aG9zdDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANB2Cgp1arb2uA+X
VyLtX5elqiDkzr5yReGlJqKSoXY7aDUKXAXjUQozcsfL3OQdrejWKHk7rEn9zVCt
87TWsZgb6QDPN5m2BnJ2pQm7AwBaugsfX8aA5FYILZrV2JfCgZp3NMltsuxps+9M
mwTPmKe2DU/59LCQdyRFThrpjc0bu5iTe7jfI1qYlVdWO3I9uP6OedLMY0TLNc6k
UA9Fr0RmBhlS1CU4HZxt0txXOMgxdI3wDx8qHrUG9bCV1LM9JeBcdHQ82kKwdGKh
gKxe7xPcRrbwa0g8/tPZuG5rToBiikw3Np306T8BRl96F5SLlFVGbn8eKvStTSsk
7PM/wFlak0huFfeq+96zW0I8mBhBiDdIK6eV7T8YBxe2dF9E5tDGyYfaD12AIPlt
zbggmYMSsq4KXVTPIw2PxvDqgFNufYKK+NaIzIf1an6ZOdB4AACs5S6yHEdo/5gR
saqEo8gxAzKyyUdDQnrR37UV4QWvC4HOuPBLtuaVk/RjHOqjtyEjzqrXhhh/LYw6
vdDCnckiCZjlML7bp0UmBQ4l93jrfUFYzNPkTqWlq8oncHabME5gpDpySL5oAhST
RvqQcnLMiqAce6UKVy2Cq5NwSnZwp3Jrn/mjiSkJZMYGobv6WVkhXbTnYJNLUQ5y
EYM7cuk2Ue8H+fhZKn5sn1wnYgMXAgMBAAEwDQYJKoZIhvcNAQELBQADggIBAMJu
Kj98QFRrQlfrDvyr27hEHwAxUB+ptgOtdOvnJhq/2fZ1rZpN/O4/B/HpxmjUc3a3
+yJD9QBwY2+JHwbXdVC7guKhOrxu1rQxoKXeaT4bYjXnrxGKNcEDiQN1CDZ3sDGI
D4gtAbQsYgPsXk67UeJbOKMkN7GoFNQfl3NFlAEMH0CWrXIHRY9tCORgggbxxssJ
y6CPfS8GD65j1kGrlMySYbe3gP6kTFkQvIokqW5dkhZHTxcFF/8s6XaoJ7C3b821
kl16W/vjTaskBehE+GSTjn2ykpCgLr+cA2vJh24LP9QOO21odSYyr1S8OHn2bS06
XJe+o7GzD0mw0IZ7e6WRG8Mf8W40e8BJa2qIaJujiw9FSGkYxIH/FAjznEIw9X9s
yDgcRN3ZaNGyyGuGNYXzCTid8OgL2z3Bvo1oa6ird0QxlUS7XRkusTHcEbWvyPqC
g+Cwh7OBXxkwzMjq+j8jMyvOVhLNwkuQDAIslhqY53mX04U2EHoKD8YEuiE+O1QH
XIxp5b6/ynPXc8qZ0l9ZNbpwao0G73gwXs5ydhBwaOKJ/+eY966x8QiaPi2qrchG
JnN9Sxeyf09IOsK4vtsbpEIeo+kNA5oiW+ZlaxzRRkXyWFlKtljdz+5YycfZiUZF
JhLRxbWoZZBTf9Aa6k5X3THCz62v4U4UBqxkCi2j
-----END CERTIFICATE-----
52 changes: 52 additions & 0 deletions certs/localhost-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDQdgoKdWq29rgP
l1ci7V+Xpaog5M6+ckXhpSaikqF2O2g1ClwF41EKM3LHy9zkHa3o1ih5O6xJ/c1Q
rfO01rGYG+kAzzeZtgZydqUJuwMAWroLH1/GgORWCC2a1diXwoGadzTJbbLsabPv
TJsEz5intg1P+fSwkHckRU4a6Y3NG7uYk3u43yNamJVXVjtyPbj+jnnSzGNEyzXO
pFAPRa9EZgYZUtQlOB2cbdLcVzjIMXSN8A8fKh61BvWwldSzPSXgXHR0PNpCsHRi
oYCsXu8T3Ea28GtIPP7T2bhua06AYopMNzad9Ok/AUZfeheUi5RVRm5/Hir0rU0r
JOzzP8BZWpNIbhX3qvves1tCPJgYQYg3SCunle0/GAcXtnRfRObQxsmH2g9dgCD5
bc24IJmDErKuCl1UzyMNj8bw6oBTbn2CivjWiMyH9Wp+mTnQeAAArOUushxHaP+Y
EbGqhKPIMQMysslHQ0J60d+1FeEFrwuBzrjwS7bmlZP0Yxzqo7chI86q14YYfy2M
Or3Qwp3JIgmY5TC+26dFJgUOJfd4631BWMzT5E6lpavKJ3B2mzBOYKQ6cki+aAIU
k0b6kHJyzIqgHHulClctgquTcEp2cKdya5/5o4kpCWTGBqG7+llZIV2052CTS1EO
chGDO3LpNlHvB/n4WSp+bJ9cJ2IDFwIDAQABAoICAQCMZrQkjyisykb12UyK7a1w
ideoB/NnObfvXjhDTtcItsJc1vlbzdqLMFOiVaAU1BiJtZPU82f6/cIHEOIPbnp2
pHWuYeJk2MBG37oQb7B50KF1VFBBdhZUC3YLzvPuYsa/roITGJtlt1vFVKcj+l4q
tucIcqVnNiXIfBU68wralk8nWE4AGenJ3vLWu/GV5BRw/qz2WUqSzvsSjoCNiLWf
L8fzzmGsH7tx3FYkqWpJC2YhIwpMRyYfbuyTXpa/kfOsxRh6IXIvcIEy3Ou5LMeB
bN8D37GiGdLYBM+/Lu7UcYoMAzP59zYRhV5MeALhvZDOTn2liEAOQ5qx8FDXFJ0L
WlKy2tjlb9vKKolnEUS1mSXQBbyl/CPPsayr8xmNCKeQJngOJXCpVR9S+Hq4Z6IV
m67PlcIOPdZp0mcOe6RrTFMMpZcvzXXY0NjWUZQ03e9iGMPRr12y+Av8ReYnSISQ
Xow5CseihKd0DYldt4rKdJ6eaVWPIRwHZK8hgxNVaAWu5fb/tFcrc4gwNaFZ2ybh
WIotlhPCitksW8AEuM7J6WkCPF1j91AJeykYTNBUOyfs9Jbqm99b5oPIVQkv5LQm
2U0U3oR74GmNwPre8CyRkm1tgBkypkDlUMjLR5sFQrztbmfVcpBkSjUr8UeitZKl
ejwI1ajjHBIUCptD9P32wQKCAQEA58mAz0JH9FWXSyZTrgwOcFakp5k+GnwnONw1
d6wGXRD8TjEd46+2+wKw6YiMvDbvMfNvF6V8PO7JZb3nllnvvcRrtwpisvFaWcmW
rw0GpvrZyAWVHV3Yc09r0IaRIK6zXuTAkdGPv+JFggyDrR/MsX05HmQ8J9I1xTSN
WmgvAAjM8oBZ/QaEKktvNUHq3Xy4/ORl8kXc6Vq5UqXGJYtNjXAmxZvdQrowaZVo
HrNcpiqZfrPa0X7Gm+JuDQJNwe4Gmil6bdIW6UyiR1sdtbm30qEJYMHL8lOI+Odm
eKvxv4RKrkhC8PQrGWmzCb7hBXcdyqrnV71PTwFxKE9P6Pa0TwKCAQEA5jy/KsOh
jslj7aBPcXT2Rd1Iyac/C/vYzvj5Nqd/MbhouTnxwpm/Zu7Fd5gB9+psXCO/cY9u
+GQ+XBfszTVP/Xl+zTpZ6x64XkIBBnCVpyUG7NHOOV3vhJl7g6pif3jehGweP07S
PJWhmT3F7GHo7zZvbmZVCEqfsGPSG34BAgNWARB7JiIiqhhOueSTEm6vCa45AJjY
//EN5YjlueyDYgZDHmMMRYzAVZFu4BE8390ta8+TLt7w62u59d6SO6UK/CQNR4MZ
yATHKbnUbqlTSCARmM5vBgb/eMeh8o96TKLmP+w+p+hdjgSlErKzTSy0E8oWx/We
615Wza1uiT9quQKCAQACcaJ1DPrbPafJuA670A666n7z8W6xMsvrEo9RwrOSeaJf
ZK8u/mRMgzvASptJRL8C5NEwC6OitNXUx8rQUARyGMcV9/sQbfEI71IyaE3ApvZy
4HQxChNFK/o0CacwFAd6IalSIEaGvGkFNQKSabpryKk4g9AKj3HXsXUoFd8g/fbD
O803GMF9/KuggSBr02vT5siYZFou77DyiSLstGpN6nfOL5WGpZXrFxMw960rZ1mU
92qAOPj5HcsRQgv11n+lUnii/csWDKPNYs2OY/XnN6F0rrZqyKyyCyaOcSmsXkW3
TrW9+qUVsIKdwLB4sUfUIjcsOqfCan558VQjXa6LAoIBAQDMsKsggOasRD20MHcQ
nnSorlAc/7TcmZ4qfE1MGuPJN3LbYjme0gPJpnQmnBz75Q1LaXi1pFh7Otv6Ekc0
NbaU2qQyHzN8BkbRfgrdR9C01gVvHCQk+m+MSsv1xdBmFfZ2coz9qbzdogYWcEX7
nxxxN8sfspjck0OflU9ho8ePm5mlvppNz8FTyeKMADwFAiRaDeudrUYXXZ8GN4xN
nIk95+VaKaLqXnVMXczeJlUhjcjo9ZWb8Rbtpkug3KzTnCrE+eRYdKTCIAVVAmJr
s9xX3jLm0HkCOcx8+7buKEMJyTW9FPKrYrlyHo0Hr5oa/ahng553TdZo2OmBWDRS
ju/5AoIBAFbnkvSt71E/8k1vOpDLUOcuaYm011wURvddgomqxiqhsdaLw1A05PGN
8ZuJ1wWKOR2pg6eU60uLjh0+kyBmNwZ7UCVwfCqtS25fvFq/aLyCgJHTnUi23K/S
5ch22ANGhAOPOA++Dx2Fj845+gL5Pyd6CS7P1w0p/OoE1FhgMKhhl0pNtJRVdGiI
vnWKK+O5QfjiusU7nfbLSaurmOHFXzma9+aJD8DKZdNqxJB65BZZRUAuplXFb1rf
gVljUMgNm3OcaB4MyINKKE+yw/gbkCnxm5fETZNi9mEEbIjrj6OyoCuVk6O8ZlU+
awUM4jMlovTrXzA+x2O/IFjdUGix0uU=
-----END PRIVATE KEY-----
17 changes: 16 additions & 1 deletion cmd/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"log"
"net/http"
"os"

_ "github.com/aleksandr/strive-api/docs"
"github.com/aleksandr/strive-api/internal/config"
Expand Down Expand Up @@ -53,10 +54,24 @@ func main() {

// Start server
server := httphandler.NewServer(cfg, handler, logger)
server.Start()

// Check if SSL certificates exist for HTTPS (only for localhost development)
if fileExists("certs/localhost-cert.pem") && fileExists("certs/localhost-key.pem") {
server.StartTLS("certs/localhost-cert.pem", "certs/localhost-key.pem")
logger.Info("Starting with HTTPS (localhost development certificates found)")
} else {
server.Start()
logger.Info("Starting with HTTP (production mode - SSL handled by platform)")
}

server.WaitForShutdown()
}

func fileExists(filename string) bool {
_, err := os.Stat(filename)
return !os.IsNotExist(err)
}

func loadConfig() *config.Config {
cfg, err := config.Load()
if err != nil {
Expand Down
9 changes: 9 additions & 0 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,15 @@ CORS_ALLOW_CREDENTIALS=true
# Max age for preflight requests in seconds
CORS_MAX_AGE=86400

# Cookie Configuration (for production deployment)
# Secure cookies (true/false) - set to true for HTTPS in production
COOKIE_SECURE=true
# SameSite attribute (None, Lax, Strict)
# Use "Strict" for production (maximum security)
# Use "None" for cross-site cookies (requires Secure=true and HTTPS)
# Use "Lax" for same-site cookies (default, recommended for most cases)
COOKIE_SAMESITE=Strict

# Environment Configuration
# Set to 'production' for HTTPS cookies, leave empty for development
ENVIRONMENT=
Expand Down
17 changes: 15 additions & 2 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ func Load() (*Config, error) {
XSSProtection: getEnv("SECURITY_XSS_PROTECTION", "1; mode=block"),
},
Cookie: CookieConfig{
Secure: false,
SameSite: http.SameSiteNoneMode,
Secure: getEnv("COOKIE_SECURE", "true") == trueStr,
SameSite: parseSameSite(getEnv("COOKIE_SAMESITE", "Strict")),
},
}

Expand Down Expand Up @@ -238,3 +238,16 @@ func getEnvSlice(key string, defaultValue []string) []string {
}
return defaultValue
}

func parseSameSite(value string) http.SameSite {
switch value {
case "None":
return http.SameSiteNoneMode
case "Strict":
return http.SameSiteStrictMode
case "Lax":
return http.SameSiteLaxMode
default:
return http.SameSiteStrictMode
}
}
10 changes: 10 additions & 0 deletions internal/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ func (s *Server) Start() {
}()
}

func (s *Server) StartTLS(certFile, keyFile string) {
s.logger.Info("Starting HTTPS server", "addr", s.httpServer.Addr)
go func() {
if err := s.httpServer.ListenAndServeTLS(certFile, keyFile); err != nil && err != http.ErrServerClosed {
s.logger.Error("HTTPS server error", "error", err)
log.Fatalf("HTTPS server error: %v", err)
}
}()
}

func (s *Server) Stop(ctx context.Context) error {
return s.httpServer.Shutdown(ctx)
}
Expand Down
4 changes: 4 additions & 0 deletions render.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,8 @@ services:
value: require
- key: JWT_SECRET
sync: false
- key: COOKIE_SECURE
value: "true"
- key: COOKIE_SAMESITE
value: "Strict"