# Authentication Audit - 20251123-104211 == JWT Implementation == /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:20: "github.com/golang-jwt/jwt/v5" /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:153: jwtSecret []byte /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:267: jwtSecret = []byte(strings.TrimSpace(os.Getenv("JWT_SECRET"))) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:268: if len(jwtSecret) < 32 { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:269: log.Fatal("JWT_SECRET must be set and at least 32 characters") /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:298: tokenString := strings.TrimPrefix(authHeader, "Bearer ") /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:299: if tokenString == authHeader { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:304: token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:305: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:308: return jwtSecret, nil /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:311: if err != nil || !token.Valid { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:312: http.Error(w, "Invalid or expired token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:316: claims, ok := token.Claims.(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:318: http.Error(w, "Invalid token claims", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:335: tokenString := strings.TrimPrefix(authHeader, "Bearer ") /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:336: if tokenString == authHeader { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:341: token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:342: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:345: return jwtSecret, nil /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:348: if err != nil || !token.Valid { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:353: claims, ok := token.Claims.(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:366: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:386:func extractRoles(claims jwt.MapClaims) ([]string, error) { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:410:func hasRole(claims jwt.MapClaims, role string) bool { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:427:func getUserID(claims jwt.MapClaims) int { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:437:func getUserName(claims jwt.MapClaims) string { /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:650: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:690: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:734: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:773: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:839: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:883: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:923: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:969: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:996: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:1050: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:1103: claims := r.Context().Value(userContextKey).(jwt.MapClaims) /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:17: "github.com/golang-jwt/jwt/v5" /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:97: ensureJWTSecret() /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:244:func ensureJWTSecret() { /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:245: secret := strings.TrimSpace(os.Getenv("JWT_SECRET")) /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:247: log.Fatal("JWT_SECRET must be set and at least 32 characters") /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:251:// authMiddleware validates JWT token and extracts user info /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:260: tokenString := strings.TrimPrefix(authHeader, "Bearer ") /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:261: if tokenString == authHeader { /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:262: http.Error(w, "Invalid authorization format. Expected: Bearer ", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:266: // Parse and validate JWT token /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:267: token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:269: if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:270: return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) == Token Expiration Settings == /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:306: return nil, fmt.Errorf("unexpected signing method") /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:312: http.Error(w, "Invalid or expired token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/forum-service/main.go:343: return nil, fmt.Errorf("unexpected signing method") /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main_test.go:27: expected := "ok\n" /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main_test.go:28: if rr.Body.String() != expected { /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main_test.go:29: t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) /home/administrator/projects/coppertone.tech/backend/functions/payment-service/main.go:270: return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) /home/administrator/projects/coppertone.tech/backend/functions/work-management-service/main_test.go:27: expected := "ok\n" /home/administrator/projects/coppertone.tech/backend/functions/work-management-service/main_test.go:28: if rr.Body.String() != expected { /home/administrator/projects/coppertone.tech/backend/functions/work-management-service/main_test.go:29: t.Errorf("handler returned unexpected body: got %v want %v", rr.Body.String(), expected) /home/administrator/projects/coppertone.tech/backend/functions/work-management-service/main.go:377: return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main_test.go:57: // This will fail if JWT_SECRET is not set, which is expected in test environment /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main_test.go:61: // If JWT_SECRET is not set, we expect an error /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:16: "regexp" /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:37:// Token expiration configuration /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:39: accessTokenExpiry = 15 * time.Minute // Short-lived access tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:40: refreshTokenExpiry = 7 * 24 * time.Hour // 7 day refresh tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:86: // Reset window if expired /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:117: // Reset window if expired /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:174:var emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:313: ExpiresIn int64 `json:"expiresIn"` // Access token expiry in seconds /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:514: // Default to restrictive in production - set CORS_ALLOW_ORIGIN explicitly /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:961: http.Error(w, "Invalid or expired refresh token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1328: return nil, fmt.Errorf("unexpected signing method") /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1334: http.Error(w, "Invalid or expired token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1400: "exp": time.Now().Add(accessTokenExpiry).Unix(), /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1422: expiresAt := time.Now().Add(refreshTokenExpiry) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1426: INSERT INTO refresh_tokens (user_id, token_hash, expires_at, client_ip, created_at) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1428: `, userID, string(tokenHash), expiresAt, clientIP) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1433: log.Printf("AUDIT: Refresh token created for user_id %d from IP %s, expires %s", /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1434: userID, clientIP, expiresAt.Format(time.RFC3339)) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1441: // Get all non-expired tokens and check against provided token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1445: WHERE expires_at > NOW() AND revoked_at IS NULL /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1465: return 0, 0, errors.New("invalid or expired refresh token") /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1509: ExpiresIn: int64(accessTokenExpiry.Seconds()), /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1539: return nil, errors.New("roles claim has unexpected type") /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1586: "exp": time.Now().Add(time.Hour).Unix(), /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:361: return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:367: http.Error(w, "Invalid or expired token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:578: // Don't expose review details to public /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:623: // Don't expose review details to public /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:1095: // Don't expose internal details to public /home/administrator/projects/coppertone.tech/backend/functions/blog-service/main.go:1140: // Don't expose internal details to public /home/administrator/projects/coppertone.tech/backend/functions/contact-service/main.go:13: "regexp" /home/administrator/projects/coppertone.tech/backend/functions/contact-service/main.go:351: return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) /home/administrator/projects/coppertone.tech/backend/functions/contact-service/main.go:644: emailRegex := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`) == Password Hashing == /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:26: "golang.org/x/crypto/bcrypt" /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:170: maxPasswordLength = 72 // bcrypt limit /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:619: passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:848: if err := bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(req.Password)); err != nil { /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1088: passwordHash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1417: tokenHash, err := bcrypt.GenerateFromPassword([]byte(token), bcrypt.DefaultCost) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1460: if bcrypt.CompareHashAndPassword([]byte(tokenHash), []byte(token)) == nil { /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1592: hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1600: return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) == nil == Weak Hashing (MD5, SHA1) == None found - good == Session Management == /home/administrator/projects/coppertone.tech/frontend/src/stores/trustBusiness.ts:865: options: ['Terms of Use', 'Privacy Policy', 'Cookie Policy', 'Refund Policy', 'Shipping Policy'], /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:75: 'session expired': { /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:77: message: 'Your session has expired. Please sign in again.' /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/types.ts:69: * If `false`, will also ignore cookies in the CORS response. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/types.ts:149: * a CORS request, _OR_ when cookies are to be ignored by the CORS response, set to `false`. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/types.ts:156: * The name of your site's XSRF cookie. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/types.ts:158: xsrfCookieName?: string; /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/types.ts:161: * The name of a custom header that you can use to send your XSRF cookie. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:370: // Allow users to provide their XSRF cookie name and the name of a custom header to use to /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:371: // send the cookie. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:372: const { withCredentials, xsrfCookieName, xsrfHeaderName } = config; /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:373: if ((withCredentials || !crossDomain) && xsrfCookieName && xsrfHeaderName) { /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:374: const xsrfCookie = document?.cookie.match(new RegExp(`(^|;\\s*)(${xsrfCookieName})=([^;]*)`))?.pop() ?? ''; /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:375: if (xsrfCookie) { /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/src/internal/ajax/ajax.ts:376: headers[xsrfHeaderName] = xsrfCookie; /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/dist/types/internal/ajax/types.d.ts:56: * If `false`, will also ignore cookies in the CORS response. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/dist/types/internal/ajax/types.d.ts:125: * a CORS request, _OR_ when cookies are to be ignored by the CORS response, set to `false`. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/dist/types/internal/ajax/types.d.ts:131: * The name of your site's XSRF cookie. /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/dist/types/internal/ajax/types.d.ts:133: xsrfCookieName?: string; /home/administrator/projects/coppertone.tech/frontend/node_modules/rxjs/dist/types/internal/ajax/types.d.ts:135: * The name of a custom header that you can use to send your XSRF cookie. /home/administrator/projects/coppertone.tech/frontend/node_modules/globals/index.d.ts:922: readonly 'sessionStorage': false; /home/administrator/projects/coppertone.tech/frontend/node_modules/axios/index.d.ts:77: getSetCookie(): string[]; /home/administrator/projects/coppertone.tech/frontend/node_modules/axios/index.d.ts:99: "set-cookie": string[]; /home/administrator/projects/coppertone.tech/frontend/node_modules/axios/index.d.ts:336: xsrfCookieName?: string; /home/administrator/projects/coppertone.tech/frontend/node_modules/axios/index.d.ts:374: sessionTimeout?: number; /home/administrator/projects/coppertone.tech/frontend/node_modules/ansi-escapes/index.d.ts:11: - `N%`: N percent of the session's width or height. /home/administrator/projects/coppertone.tech/frontend/node_modules/ansi-escapes/index.d.ts:21: - `N%`: N percent of the session's width or height. /home/administrator/projects/coppertone.tech/frontend/node_modules/log-update/index.d.ts:38: Persist the logged output. Useful if you want to start a new log session below the current one. /home/administrator/projects/coppertone.tech/frontend/node_modules/tough-cookie/dist/getPublicSuffix.d.ts:11: * In testing scenarios it's common to configure the cookie store with so that `http://localhost` can be used as a domain: /home/administrator/projects/coppertone.tech/frontend/node_modules/tough-cookie/dist/getPublicSuffix.d.ts:31: * name upon which a cookie can be set. == Refresh Token Implementation == /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:168: refreshToken: string /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:178: const refreshToken = ref(localStorage.getItem('refresh_token')) /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:208: refreshToken.value = response.refreshToken /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:214: localStorage.setItem('refresh_token', response.refreshToken) /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:221: refreshToken.value = null /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:224: localStorage.removeItem('refresh_token') /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:234: // Refresh the access token using the refresh token /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:241: if (!refreshToken.value) { /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:250: body: JSON.stringify({ refreshToken: refreshToken.value }) /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:525: // Call backend to revoke refresh token /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:526: if (refreshToken.value) { /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:531: body: JSON.stringify({ refreshToken: refreshToken.value }) /home/administrator/projects/coppertone.tech/frontend/src/stores/auth.ts:583: refreshToken, /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:40: refreshTokenExpiry = 7 * 24 * time.Hour // 7 day refresh tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:41: refreshTokenLength = 32 // 256-bit refresh token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:304:// RefreshTokenRequest for refreshing access tokens. /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:306: RefreshToken string `json:"refreshToken"` /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:309:// AuthTokenResponse includes both access and refresh tokens. /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:312: RefreshToken string `json:"refreshToken"` /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:317:// LogoutRequest for revoking refresh tokens. /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:319: RefreshToken string `json:"refreshToken"` /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:937:// handleRefreshToken exchanges a valid refresh token for a new access token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:957: // Validate the refresh token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:960: log.Printf("SECURITY: Invalid refresh token attempt from IP %s: %v", clientIP, err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:961: http.Error(w, "Invalid or expired refresh token", http.StatusUnauthorized) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:965: // Revoke the old refresh token (rotation for security) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:967: log.Printf("Warning: Failed to revoke old refresh token: %v", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:974: http.Error(w, "Failed to refresh tokens", http.StatusInternalServerError) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:984:// handleLogout revokes a specific refresh token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1014: // Revoke the refresh token /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1016: log.Printf("Error revoking refresh token: %v", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1027:// handleLogoutAll revokes all refresh tokens for the authenticated user /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1039: log.Printf("Error revoking all refresh tokens for user %d: %v", userID, err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1407:// generateRefreshToken creates a secure random refresh token and stores it in the database /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1410: tokenBytes := make([]byte, refreshTokenLength) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1419: return "", fmt.Errorf("failed to hash refresh token: %w", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1422: expiresAt := time.Now().Add(refreshTokenExpiry) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1426: INSERT INTO refresh_tokens (user_id, token_hash, expires_at, client_ip, created_at) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1430: return "", fmt.Errorf("failed to store refresh token: %w", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1439:// validateRefreshToken validates a refresh token and returns the user ID /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1444: FROM refresh_tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1448: return 0, 0, fmt.Errorf("failed to query refresh tokens: %w", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1465: return 0, 0, errors.New("invalid or expired refresh token") /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1468:// revokeRefreshToken marks a refresh token as revoked /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1471: UPDATE refresh_tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1478:// revokeAllUserRefreshTokens revokes all refresh tokens for a user /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1481: UPDATE refresh_tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1490: log.Printf("AUDIT: Revoked %d refresh tokens for user_id %d", rowsAffected, userID) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1494:// generateTokenPair creates both access and refresh tokens /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1501: refreshToken, err := generateRefreshToken(userID, clientIP) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1503: return nil, fmt.Errorf("failed to generate refresh token: %w", err) /home/administrator/projects/coppertone.tech/backend/functions/auth-service/main.go:1508: RefreshToken: refreshToken,