Skip to content

Commit

Permalink
Import friends on fb limited login auth (#1220)
Browse files Browse the repository at this point in the history
  • Loading branch information
sesposito authored May 29, 2024
1 parent 24c07ac commit 5c848b3
Show file tree
Hide file tree
Showing 6 changed files with 21 additions and 22 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ The format is based on [keep a changelog](http://keepachangelog.com) and this pr
- Add confirm dialog to console delete operations.
- Reduce Console Storage View memory usage.
- Upgraded pgx to v5.
- Attempt to import Facebook friends on Limited Login authentication.

### Fixed
- Ensure Apple receipts with duplicate transaction identifiers are processed cleanly.
Expand Down
4 changes: 2 additions & 2 deletions server/api_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,13 +392,13 @@ func (s *ApiServer) AuthenticateFacebook(ctx context.Context, in *api.Authentica

create := in.Create == nil || in.Create.Value

dbUserID, dbUsername, created, importFriendsPossible, err := AuthenticateFacebook(ctx, s.logger, s.db, s.socialClient, s.config.GetSocial().FacebookLimitedLogin.AppId, in.Account.Token, username, create)
dbUserID, dbUsername, created, err := AuthenticateFacebook(ctx, s.logger, s.db, s.socialClient, s.config.GetSocial().FacebookLimitedLogin.AppId, in.Account.Token, username, create)
if err != nil {
return nil, err
}

// Import friends if requested.
if in.Sync != nil && in.Sync.Value && importFriendsPossible {
if in.Sync != nil && in.Sync.Value {
_ = importFacebookFriends(ctx, s.logger, s.db, s.tracker, s.router, s.socialClient, uuid.FromStringOrNil(dbUserID), dbUsername, in.Account.Token, false)
}

Expand Down
26 changes: 12 additions & 14 deletions server/core_authenticate.go
Original file line number Diff line number Diff line change
Expand Up @@ -392,10 +392,9 @@ func AuthenticateUsername(ctx context.Context, logger *zap.Logger, db *sql.DB, u
return dbUserID, nil
}

func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, client *social.Client, appId, accessToken, username string, create bool) (string, string, bool, bool, error) {
func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, client *social.Client, appId, accessToken, username string, create bool) (string, string, bool, error) {
var facebookProfile *social.FacebookProfile
var err error
var importFriendsPossible bool

// Try Facebook Limited Login first.
facebookProfile, err = client.CheckFacebookLimitedLoginToken(ctx, appId, accessToken)
Expand All @@ -404,9 +403,8 @@ func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, c
facebookProfile, err = client.GetFacebookProfile(ctx, accessToken)
if err != nil {
logger.Info("Could not authenticate Facebook profile.", zap.Error(err))
return "", "", false, false, status.Error(codes.Unauthenticated, "Could not authenticate Facebook profile.")
return "", "", false, status.Error(codes.Unauthenticated, "Could not authenticate Facebook profile.")
}
importFriendsPossible = true
}
found := true

Expand All @@ -421,7 +419,7 @@ func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, c
found = false
} else {
logger.Error("Error looking up user by Facebook ID.", zap.Error(err), zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create))
return "", "", false, false, status.Error(codes.Internal, "Error finding user account.")
return "", "", false, status.Error(codes.Internal, "Error finding user account.")
}
}

Expand All @@ -430,15 +428,15 @@ func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, c
// Check if it's disabled.
if dbDisableTime.Valid && dbDisableTime.Time.Unix() != 0 {
logger.Info("User account is disabled.", zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create))
return "", "", false, false, status.Error(codes.PermissionDenied, "User account banned.")
return "", "", false, status.Error(codes.PermissionDenied, "User account banned.")
}

return dbUserID, dbUsername, false, importFriendsPossible, nil
return dbUserID, dbUsername, false, nil
}

if !create {
// No user account found, and creation is not allowed.
return "", "", false, false, status.Error(codes.NotFound, "User account not found.")
return "", "", false, status.Error(codes.NotFound, "User account not found.")
}

// Create a new account.
Expand All @@ -450,20 +448,20 @@ func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, c
if errors.As(err, &pgErr) && pgErr.Code == dbErrorUniqueViolation {
if strings.Contains(pgErr.Message, "users_username_key") {
// Username is already in use by a different account.
return "", "", false, false, status.Error(codes.AlreadyExists, "Username is already in use.")
return "", "", false, status.Error(codes.AlreadyExists, "Username is already in use.")
} else if strings.Contains(pgErr.Message, "users_facebook_id_key") {
// A concurrent write has inserted this Facebook ID.
logger.Info("Did not insert new user as Facebook ID already exists.", zap.Error(err), zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create))
return "", "", false, false, status.Error(codes.Internal, "Error finding or creating user account.")
return "", "", false, status.Error(codes.Internal, "Error finding or creating user account.")
}
}
logger.Error("Cannot find or create user with Facebook ID.", zap.Error(err), zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create))
return "", "", false, false, status.Error(codes.Internal, "Error finding or creating user account.")
return "", "", false, status.Error(codes.Internal, "Error finding or creating user account.")
}

if rowsAffectedCount, _ := result.RowsAffected(); rowsAffectedCount != 1 {
logger.Error("Did not insert new user.", zap.Int64("rows_affected", rowsAffectedCount))
return "", "", false, false, status.Error(codes.Internal, "Error finding or creating user account.")
return "", "", false, status.Error(codes.Internal, "Error finding or creating user account.")
}

// Import email address, if it exists.
Expand All @@ -475,12 +473,12 @@ func AuthenticateFacebook(ctx context.Context, logger *zap.Logger, db *sql.DB, c
logger.Warn("Skipping facebook account email import as it is already set in another user.", zap.Error(err), zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create), zap.String("created_user_id", userID))
} else {
logger.Error("Failed to import facebook account email.", zap.Error(err), zap.String("facebookID", facebookProfile.ID), zap.String("username", username), zap.Bool("create", create), zap.String("created_user_id", userID))
return "", "", false, false, status.Error(codes.Internal, "Error importing facebook account email.")
return "", "", false, status.Error(codes.Internal, "Error importing facebook account email.")
}
}
}

return userID, username, true, importFriendsPossible, nil
return userID, username, true, nil
}

func AuthenticateFacebookInstantGame(ctx context.Context, logger *zap.Logger, db *sql.DB, client *social.Client, appSecret string, signedPlayerInfo string, username string, create bool) (string, string, bool, error) {
Expand Down
4 changes: 2 additions & 2 deletions server/runtime_go_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ func (n *RuntimeGoNakamaModule) AuthenticateFacebook(ctx context.Context, token
return "", "", false, errors.New("expects id to be valid, must be 1-128 bytes")
}

dbUserID, dbUsername, created, importFriendsPossible, err := AuthenticateFacebook(ctx, n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
if err == nil && importFriends && importFriendsPossible {
dbUserID, dbUsername, created, err := AuthenticateFacebook(ctx, n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
if err == nil && importFriends {
// Errors are logged before this point and failure here does not invalidate the whole operation.
_ = importFacebookFriends(ctx, n.logger, n.db, n.tracker, n.router, n.socialClient, uuid.FromStringOrNil(dbUserID), dbUsername, token, false)
}
Expand Down
4 changes: 2 additions & 2 deletions server/runtime_javascript_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -1561,12 +1561,12 @@ func (n *runtimeJavascriptNakamaModule) authenticateFacebook(r *goja.Runtime) fu
create = getJsBool(r, f.Argument(3))
}

dbUserID, dbUsername, created, importFriendsPossible, err := AuthenticateFacebook(n.ctx, n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
dbUserID, dbUsername, created, err := AuthenticateFacebook(n.ctx, n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
if err != nil {
panic(r.NewGoError(fmt.Errorf("error authenticating: %v", err.Error())))
}

if importFriends && importFriendsPossible {
if importFriends {
// Errors are logged before this point and failure here does not invalidate the whole operation.
_ = importFacebookFriends(n.ctx, n.logger, n.db, n.tracker, n.router, n.socialClient, uuid.FromStringOrNil(dbUserID), dbUsername, token, false)
}
Expand Down
4 changes: 2 additions & 2 deletions server/runtime_lua_nakama.go
Original file line number Diff line number Diff line change
Expand Up @@ -1952,14 +1952,14 @@ func (n *RuntimeLuaNakamaModule) authenticateFacebook(l *lua.LState) int {
// Parse create flag, if any.
create := l.OptBool(4, true)

dbUserID, dbUsername, created, importFriendsPossible, err := AuthenticateFacebook(l.Context(), n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
dbUserID, dbUsername, created, err := AuthenticateFacebook(l.Context(), n.logger, n.db, n.socialClient, n.config.GetSocial().FacebookLimitedLogin.AppId, token, username, create)
if err != nil {
l.RaiseError("error authenticating: %v", err.Error())
return 0
}

// Import friends if requested.
if importFriends && importFriendsPossible {
if importFriends {
// Errors are logged before this point and failure here does not invalidate the whole operation.
_ = importFacebookFriends(l.Context(), n.logger, n.db, n.tracker, n.router, n.socialClient, uuid.FromStringOrNil(dbUserID), dbUsername, token, false)
}
Expand Down

0 comments on commit 5c848b3

Please sign in to comment.