Skip to content

Commit 120c7c9

Browse files
committed
feat: Implement automatic profile synchronization on social login
- Added functionality to automatically sync user profile data from social providers (Google, Facebook, GitHub) during login. - Enhanced user and social account models to store additional fields including name, first name, last name, profile picture, and locale. - Updated repository methods to handle profile updates and added a new method for updating social accounts. - Modified API responses to include new profile fields and social account data. - Regenerated Swagger documentation to reflect the updated profile structure. - Comprehensive migration documentation added to guide users through the changes.
1 parent c3aaf82 commit 120c7c9

22 files changed

+3568
-40
lines changed

CHANGELOG.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
### Added - Profile Sync on Social Login (2025-11-08)
11+
12+
#### Automatic Profile Synchronization
13+
- **Profile data now automatically syncs** from social providers on every login
14+
- System updates both `social_accounts` and `users` tables with latest provider data
15+
- **Smart update strategy**: Only updates fields that have changed
16+
- **Non-blocking**: Authentication succeeds even if profile update fails
17+
- Supports all providers: Google, Facebook, GitHub
18+
19+
#### What Gets Synced
20+
- Profile picture (avatar/photo URL)
21+
- Full name, first name, last name
22+
- Email from provider
23+
- Locale/language preference
24+
- Username (GitHub login, etc.)
25+
- Complete raw provider response (JSONB)
26+
- OAuth access token
27+
28+
#### Benefits
29+
- Users see updated profile pictures immediately after changing them on social platforms
30+
- Name changes on social accounts automatically reflected in app
31+
- No manual sync or refresh needed
32+
- Data stays current with social provider
33+
34+
#### Repository Enhancement
35+
- Added `UpdateSocialAccount()` method to social repository
36+
- Enables full social account record updates via GORM
37+
38+
#### Profile Endpoint Enhancement
39+
- Updated `UserResponse` DTO to include all new profile fields
40+
- Added `SocialAccountResponse` DTO for social account data
41+
- Modified `GetUserByID` repository to preload social accounts
42+
- Enhanced `GetProfile` handler to return complete user profile with social accounts
43+
- Profile endpoint now returns: name, first_name, last_name, profile_picture, locale, social_accounts
44+
- Regenerated Swagger documentation to reflect new profile structure
45+
46+
### Added - Social Login Data Enhancement (2025-11-08)
47+
48+
#### User Model Enhancements
49+
- Added `Name` field to store full name from social login or user input
50+
- Added `FirstName` field for first name from social login
51+
- Added `LastName` field for last name from social login
52+
- Added `ProfilePicture` field to store profile picture URL from social providers
53+
- Added `Locale` field for user's language/locale preference
54+
55+
#### Social Account Model Enhancements
56+
- Added `Email` field to store email from social provider
57+
- Added `Name` field to store name from social provider
58+
- Added `FirstName` field for first name from social provider
59+
- Added `LastName` field for last name from social provider
60+
- Added `ProfilePicture` field for profile picture URL from social provider
61+
- Added `Username` field for username/login from providers (e.g., GitHub login)
62+
- Added `Locale` field for locale from social provider
63+
- Added `RawData` JSONB field to store complete raw JSON response from provider
64+
65+
#### Service Layer Enhancements
66+
- Added `UpdateUser()` method to user repository for updating user profile data
67+
- Enhanced Google login handler to capture: email, verified_email, name, given_name, family_name, picture, locale
68+
- Enhanced Facebook login handler to capture: email, name, first_name, last_name, picture (large), locale
69+
- Enhanced GitHub login handler to capture: email, name, login, avatar_url, bio, location, company
70+
- Implemented smart profile update logic: only update user fields if currently empty when linking social accounts
71+
- Store complete provider response in `RawData` field for all providers
72+
73+
#### API Changes
74+
- Profile endpoint (`GET /profile`) now returns additional fields: name, first_name, last_name, profile_picture, locale
75+
- Social account objects now include all new fields in responses
76+
- No breaking changes - all new fields are optional and nullable
77+
78+
### Changed
79+
- Modified social login data extraction to request extended fields from providers
80+
- Updated Facebook Graph API call to request: `id,name,email,first_name,last_name,picture.type(large),locale`
81+
- Enhanced social account linking to preserve and enrich existing user profile data
82+
83+
### Technical Details
84+
- **Migration Method:** GORM AutoMigrate (automatic on application startup)
85+
- **Database Impact:** Adds 5 columns to `users` table, 8 columns to `social_accounts` table
86+
- **Backward Compatibility:** Fully backward compatible - all new fields are nullable
87+
- **Files Modified:**
88+
- `pkg/models/user.go` - User model with new profile fields
89+
- `pkg/models/social_account.go` - Social account model with extended data fields
90+
- `internal/social/service.go` - Enhanced provider handlers for Google, Facebook, GitHub
91+
- `internal/user/repository.go` - Added UpdateUser method
92+
- `docs/migrations/MIGRATION_SOCIAL_LOGIN_DATA.md` - Migration documentation
93+
94+
### Documentation
95+
- Added comprehensive migration documentation in `docs/migrations/MIGRATION_SOCIAL_LOGIN_DATA.md`
96+
- Documents data flow changes, database schema updates, and testing recommendations
97+
- Includes security considerations and rollback plan
98+
99+
---
100+
101+
## [1.0.0] - Previous Release
102+
103+
### Features
104+
- User registration and authentication
105+
- Email verification
106+
- Password reset functionality
107+
- Two-factor authentication (TOTP)
108+
- Social login integration (Google, Facebook, GitHub)
109+
- JWT-based authentication (access & refresh tokens)
110+
- Activity logging
111+
- Redis-based session management
112+
- Comprehensive API documentation with Swagger
113+

docs/CODE_FIXES_SUMMARY.md

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
# Code Fixes - Profile Endpoint Returns All Data
2+
3+
## ✅ Issues Found and Fixed
4+
5+
### Issue 1: DTO Missing New Fields ❌
6+
**File:** `pkg/dto/auth.go`
7+
8+
**Problem:**
9+
```go
10+
type UserResponse struct {
11+
ID string `json:"id"`
12+
Email string `json:"email"`
13+
EmailVerified bool `json:"email_verified"`
14+
TwoFAEnabled bool `json:"two_fa_enabled"`
15+
CreatedAt string `json:"created_at"`
16+
UpdatedAt string `json:"updated_at"`
17+
// Missing: name, first_name, last_name, profile_picture, locale, social_accounts
18+
}
19+
```
20+
21+
**Fixed:**
22+
```go
23+
type UserResponse struct {
24+
ID string `json:"id"`
25+
Email string `json:"email"`
26+
EmailVerified bool `json:"email_verified"`
27+
Name string `json:"name,omitempty"` // ✨ NEW
28+
FirstName string `json:"first_name,omitempty"` // ✨ NEW
29+
LastName string `json:"last_name,omitempty"` // ✨ NEW
30+
ProfilePicture string `json:"profile_picture,omitempty"` // ✨ NEW
31+
Locale string `json:"locale,omitempty"` // ✨ NEW
32+
TwoFAEnabled bool `json:"two_fa_enabled"`
33+
CreatedAt string `json:"created_at"`
34+
UpdatedAt string `json:"updated_at"`
35+
SocialAccounts []SocialAccountResponse `json:"social_accounts,omitempty"` // ✨ NEW
36+
}
37+
38+
// ✨ NEW: Social account DTO
39+
type SocialAccountResponse struct {
40+
ID string `json:"id"`
41+
Provider string `json:"provider"`
42+
ProviderUserID string `json:"provider_user_id"`
43+
Email string `json:"email,omitempty"`
44+
Name string `json:"name,omitempty"`
45+
FirstName string `json:"first_name,omitempty"`
46+
LastName string `json:"last_name,omitempty"`
47+
ProfilePicture string `json:"profile_picture,omitempty"`
48+
Username string `json:"username,omitempty"`
49+
Locale string `json:"locale,omitempty"`
50+
CreatedAt string `json:"created_at"`
51+
UpdatedAt string `json:"updated_at"`
52+
}
53+
```
54+
55+
---
56+
57+
### Issue 2: Repository Not Loading Social Accounts ❌
58+
**File:** `internal/user/repository.go`
59+
60+
**Problem:**
61+
```go
62+
func (r *Repository) GetUserByID(id string) (*models.User, error) {
63+
var user models.User
64+
err := r.DB.Where("id = ?", id).First(&user).Error
65+
return &user, err
66+
// Social accounts not loaded!
67+
}
68+
```
69+
70+
**Fixed:**
71+
```go
72+
func (r *Repository) GetUserByID(id string) (*models.User, error) {
73+
var user models.User
74+
err := r.DB.Preload("SocialAccounts").Where("id = ?", id).First(&user).Error // ✨ Added Preload
75+
return &user, err
76+
}
77+
```
78+
79+
---
80+
81+
### Issue 3: Handler Not Returning New Fields ❌
82+
**File:** `internal/user/handler.go`
83+
84+
**Problem:**
85+
```go
86+
c.JSON(http.StatusOK, dto.UserResponse{
87+
ID: user.ID.String(),
88+
Email: user.Email,
89+
EmailVerified: user.EmailVerified,
90+
TwoFAEnabled: user.TwoFAEnabled,
91+
CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
92+
UpdatedAt: user.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
93+
// Missing all new fields!
94+
})
95+
```
96+
97+
**Fixed:**
98+
```go
99+
// Convert social accounts to DTO
100+
socialAccounts := make([]dto.SocialAccountResponse, len(user.SocialAccounts))
101+
for i, sa := range user.SocialAccounts {
102+
socialAccounts[i] = dto.SocialAccountResponse{
103+
ID: sa.ID.String(),
104+
Provider: sa.Provider,
105+
ProviderUserID: sa.ProviderUserID,
106+
Email: sa.Email,
107+
Name: sa.Name,
108+
FirstName: sa.FirstName,
109+
LastName: sa.LastName,
110+
ProfilePicture: sa.ProfilePicture,
111+
Username: sa.Username,
112+
Locale: sa.Locale,
113+
CreatedAt: sa.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
114+
UpdatedAt: sa.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
115+
}
116+
}
117+
118+
c.JSON(http.StatusOK, dto.UserResponse{
119+
ID: user.ID.String(),
120+
Email: user.Email,
121+
EmailVerified: user.EmailVerified,
122+
Name: user.Name, // ✨ NEW
123+
FirstName: user.FirstName, // ✨ NEW
124+
LastName: user.LastName, // ✨ NEW
125+
ProfilePicture: user.ProfilePicture, // ✨ NEW
126+
Locale: user.Locale, // ✨ NEW
127+
TwoFAEnabled: user.TwoFAEnabled,
128+
CreatedAt: user.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
129+
UpdatedAt: user.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
130+
SocialAccounts: socialAccounts, // ✨ NEW
131+
})
132+
```
133+
134+
---
135+
136+
## 🎯 Summary of Changes
137+
138+
| File | Change | Status |
139+
|------|--------|--------|
140+
| `pkg/dto/auth.go` | Added `UserResponse` fields: name, first_name, last_name, profile_picture, locale, social_accounts ||
141+
| `pkg/dto/auth.go` | Created new `SocialAccountResponse` DTO ||
142+
| `internal/user/repository.go` | Added `.Preload("SocialAccounts")` to `GetUserByID()` ||
143+
| `internal/user/handler.go` | Updated `GetProfile()` to return all new fields and social accounts ||
144+
145+
---
146+
147+
## 📊 Before vs After
148+
149+
### Before (Only 6 Fields)
150+
```json
151+
{
152+
"id": "a65aec73-3c91-450c-b51f-a49391d6c3ba",
153+
"email": "[email protected]",
154+
"email_verified": true,
155+
"two_fa_enabled": true,
156+
"created_at": "2025-07-31T22:34:10Z",
157+
"updated_at": "2025-11-08T17:35:55Z"
158+
}
159+
```
160+
161+
### After (Complete Profile with Social Accounts) ✨
162+
```json
163+
{
164+
"id": "a65aec73-3c91-450c-b51f-a49391d6c3ba",
165+
"email": "[email protected]",
166+
"email_verified": true,
167+
"name": "Goran Jovanovic",
168+
"first_name": "Goran",
169+
"last_name": "Jovanovic",
170+
"profile_picture": "https://lh3.googleusercontent.com/...",
171+
"locale": "en",
172+
"two_fa_enabled": true,
173+
"created_at": "2025-07-31T22:34:10Z",
174+
"updated_at": "2025-11-08T17:35:55Z",
175+
"social_accounts": [
176+
{
177+
"id": "...",
178+
"provider": "google",
179+
"provider_user_id": "...",
180+
"email": "[email protected]",
181+
"name": "Goran Jovanovic",
182+
"first_name": "Goran",
183+
"last_name": "Jovanovic",
184+
"profile_picture": "https://lh3.googleusercontent.com/...",
185+
"locale": "en",
186+
"created_at": "2025-11-08T17:35:55Z",
187+
"updated_at": "2025-11-08T17:35:55Z"
188+
}
189+
]
190+
}
191+
```
192+
193+
---
194+
195+
## ✅ Build Verification
196+
197+
```bash
198+
✅ No linter errors
199+
✅ Compilation successful
200+
✅ Ready to deploy
201+
```
202+
203+
---
204+
205+
## 🚀 Next Steps
206+
207+
### 1. Restart Application
208+
```bash
209+
cd /c/work/AI/Cursor/auth_api/v1.0.0
210+
211+
# Stop old instance (Ctrl+C)
212+
213+
# Start with new code
214+
./auth_api.exe
215+
216+
# Wait for migration to complete
217+
```
218+
219+
### 2. Login Again (To Populate Data if Needed)
220+
```
221+
Visit: http://localhost:8080/auth/google/login
222+
```
223+
224+
### 3. Test Profile Endpoint
225+
```bash
226+
curl -H "Authorization: Bearer YOUR_TOKEN" \
227+
http://localhost:8080/profile
228+
```
229+
230+
**Expected:** Full profile with all fields! ✅
231+
232+
---
233+
234+
## 📝 Notes
235+
236+
- **`omitempty` tags**: Fields with empty values won't appear in JSON response
237+
- **Social Accounts**: Array will be empty `[]` if no social logins linked
238+
- **Preload**: GORM now loads social accounts in one query (efficient)
239+
- **DTO Conversion**: Social accounts converted from model to DTO format
240+
- **No Breaking Changes**: Existing clients still work (new fields optional)
241+
242+
---
243+
244+
## 🎉 Result
245+
246+
**Profile endpoint now returns complete user data:**
247+
- ✅ All user fields (name, profile picture, locale, etc.)
248+
- ✅ All linked social accounts with their data
249+
- ✅ Clean DTO structure
250+
- ✅ Efficient database query with preload
251+
- ✅ No sensitive data exposed (tokens hidden)
252+
253+
---
254+
255+
**The code is now complete and correct!** 🎯
256+

0 commit comments

Comments
 (0)