diff --git a/compose.yaml b/compose.yaml index e740fd5..b2a918e 100644 --- a/compose.yaml +++ b/compose.yaml @@ -57,6 +57,12 @@ services: - ARGON_MEMORY=${ARGON_MEMORY} - ARGON_ITERATIONS=${ARGON_ITERATIONS} - ARGON_PARALLELISM=${ARGON_PARALLELISM} + - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID} + - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET} + - GOOGLE_REDIRECT_URL=${GOOGLE_REDIRECT_URL} + - GITHUB_CLIENT_ID=${GITHUB_CLIENT_ID} + - GITHUB_CLIENT_SECRET=${GITHUB_CLIENT_SECRET} + - GITHUB_REDIRECT_URL=${GITHUB_REDIRECT_URL} depends_on: migration: condition: service_completed_successfully diff --git a/server/config/oauth.go b/server/config/oauth.go index c1abd0c..ba7fe27 100644 --- a/server/config/oauth.go +++ b/server/config/oauth.go @@ -1,11 +1,11 @@ package config import ( - "os" - "golang.org/x/oauth2" "golang.org/x/oauth2/github" "golang.org/x/oauth2/google" + + "github.com/commandlinecoding/elephant/server/env" ) var GoogleConfig *oauth2.Config @@ -13,9 +13,9 @@ var GithubConfig *oauth2.Config func InitOAuth() { GoogleConfig = &oauth2.Config{ - ClientID: os.Getenv("GOOGLE_CLIENT_ID"), - ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"), - RedirectURL: os.Getenv("GOOGLE_REDIRECT_URL"), + ClientID: env.GOOGLE_CLIENT_ID, + ClientSecret: env.GOOGLE_CLIENT_SECRET, + RedirectURL: env.GOOGLE_REDIRECT_URL, Scopes: []string{ "https://www.googleapis.com/auth/userinfo.profile", "https://www.googleapis.com/auth/userinfo.email", @@ -24,10 +24,10 @@ func InitOAuth() { } GithubConfig = &oauth2.Config{ - ClientID: os.Getenv("GITHUB_CLIENT_ID"), - ClientSecret: os.Getenv("GITHUB_CLIENT_SECRET"), - RedirectURL: os.Getenv("GITHUB_REDIRECT_URL"), + ClientID: env.GITHUB_CLIENT_ID, + ClientSecret: env.GITHUB_CLIENT_SECRET, + RedirectURL: env.GITHUB_REDIRECT_URL, Scopes: []string{"user:email", "read:user"}, Endpoint: github.Endpoint, } -} \ No newline at end of file +} diff --git a/server/controllers/auth.go b/server/controllers/auth.go index d42714f..8fd3e08 100644 --- a/server/controllers/auth.go +++ b/server/controllers/auth.go @@ -1,8 +1,12 @@ package controllers import ( + "crypto/rand" "encoding/json" + "fmt" + "math/big" "net/http" + "regexp" "strings" "time" @@ -137,6 +141,7 @@ func HandleOAuthRedirect(w http.ResponseWriter, r *http.Request) { Path: "/", HttpOnly: true, Secure: false, + SameSite: http.SameSiteLaxMode, MaxAge: 300, } http.SetCookie(w, cookie) @@ -183,8 +188,21 @@ func HandleOAuthCallback(w http.ResponseWriter, r *http.Request) { if err == nil { _ = userRepo.LinkProviderAccount(r.Context(), u.ID, provider, profile.ID) } else { - fallbackUsername := strings.Split(profile.Email, "@")[0] - u, err = userRepo.CreateOAuthUser(r.Context(), fallbackUsername, profile.Email, profile.Name, provider, profile.ID) + emailHandle := strings.Split(profile.Email, "@")[0] + baseUsername := cleanAlphanumeric(emailHandle) + + var username string + for { + randomDigits := generateRandom4Digits() + username = fmt.Sprintf("%s.%s", baseUsername, randomDigits) + + exists, err := userRepo.UsernameExists(r.Context(), username) + if err == nil && !exists { + break + } + } + + u, err = userRepo.CreateOAuthUser(r.Context(), username, profile.Email, profile.Name, provider, profile.ID) if err != nil { w.WriteHeader(http.StatusInternalServerError) _ = json.NewEncoder(w).Encode(models.JSONResponse{Success: false, Error: "Failed to allocate user credentials"}) @@ -212,3 +230,20 @@ func HandleOAuthCallback(w http.ResponseWriter, r *http.Request) { }, }) } + +func cleanAlphanumeric(s string) string { + reg := regexp.MustCompile("[^a-zA-Z0-9]") + processed := reg.ReplaceAllString(s, "") + if processed == "" { + return "user" + } + return processed +} + +func generateRandom4Digits() string { + n, err := rand.Int(rand.Reader, big.NewInt(10000)) + if err != nil { + return "1729" + } + return fmt.Sprintf("%04d", n.Int64()) +} diff --git a/server/env/env.go b/server/env/env.go index 095f48c..718344c 100644 --- a/server/env/env.go +++ b/server/env/env.go @@ -23,6 +23,14 @@ var ( ARGON_MEMORY string ARGON_ITERATIONS string ARGON_PARALLELISM string + + GOOGLE_CLIENT_ID string + GOOGLE_CLIENT_SECRET string + GOOGLE_REDIRECT_URL string + + GITHUB_CLIENT_ID string + GITHUB_CLIENT_SECRET string + GITHUB_REDIRECT_URL string ) func init() { @@ -56,6 +64,14 @@ func init() { ARGON_MEMORY = getEnv("ARGON_MEMORY", "65536") ARGON_ITERATIONS = getEnv("ARGON_ITERATIONS", "3") ARGON_PARALLELISM = getEnv("ARGON_PARALLELISM", "2") + + GOOGLE_CLIENT_ID = getEnv("GOOGLE_CLIENT_ID", "") + GOOGLE_CLIENT_SECRET = getEnv("GOOGLE_CLIENT_SECRET", "") + GOOGLE_REDIRECT_URL = getEnv("GOOGLE_REDIRECT_URL", "") + + GITHUB_CLIENT_ID = getEnv("GITHUB_CLIENT_ID", "") + GITHUB_CLIENT_SECRET = getEnv("GITHUB_CLIENT_SECRET", "") + GITHUB_REDIRECT_URL = getEnv("GITHUB_REDIRECT_URL", "") } func getEnv(key, fallback string) string { @@ -63,4 +79,4 @@ func getEnv(key, fallback string) string { return value } return fallback -} +} \ No newline at end of file