angularjs - Aplicação de nomes de usuário exclusivos com o Firebase simplelogin




angularfire (2)

Recentemente, segui um tutorial sobre o Thinkster para criar um aplicativo da Web usando Angular e Firebase.

O tutorial usa o método simpleLogin do Firebase permite a criação de um 'perfil' que inclui um nome de usuário.

Fábrica:

app.factory('Auth', function($firebaseSimpleLogin, $firebase, FIREBASE_URL, $rootScope) {
var ref = new Firebase(FIREBASE_URL);


var auth = $firebaseSimpleLogin(ref);

var Auth = {
    register: function(user) {
       return auth.$createUser(user.email, user.password);
    },
    createProfile: function(user) {
        var profile = {
            username: user.username,
            md5_hash: user.md5_hash
        };

        var profileRef = $firebase(ref.child('profile'));
        return profileRef.$set(user.uid, profile);
    },
    login: function(user) {
        return auth.$login('password', user);
    },
    logout: function() {
        auth.$logout();
    },
    resolveUser: function() {
        return auth.$getCurrentUser();
    },
    signedIn: function() {
        return !!Auth.user.provider;
    },
    user: {}
};

$rootScope.$on('$firebaseSimpleLogin:login', function(e, user) {
    angular.copy(user, Auth.user);
    Auth.user.profile = $firebase(ref.child('profile').child(Auth.user.uid)).$asObject();

    console.log(Auth.user);
});
$rootScope.$on('$firebaseSimpleLogin:logout', function() {
    console.log('logged out');

    if (Auth.user && Auth.user.profile) {
        Auth.user.profile.$destroy();
    }
    angular.copy({}, Auth.user);
});

return Auth;
});

Controlador:

$scope.register = function() {
    Auth.register($scope.user).then(function(user) {
        return Auth.login($scope.user).then(function() {
            user.username = $scope.user.username;
            return Auth.createProfile(user);
        }).then(function() {
            $location.path('/');
        });
    }, function(error) {
        $scope.error = error.toString();
    });
};

No final do tutorial, há uma seção 'próximas etapas' que inclui:

Aplicar exclusividade de nome de usuário - este é complicado, verifique as prioridades do Firebase e veja se você pode usá-los para consultar perfis de usuário por nome de usuário

Pesquisei e pesquisei, mas não consigo encontrar uma explicação clara de como fazer isso, principalmente em termos da função setPriority() do Firebase

Eu sou o novato do Firebase, então qualquer ajuda aqui seria recebida com gratidão.

Existem algumas perguntas semelhantes , mas não consigo entender como resolver isso.

Agradecimentos desde já.

EDITAR

Da resposta de Marein, atualizei a função de registro no meu controlador para:

$scope.register = function() {

    var ref = new Firebase(FIREBASE_URL);
    var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username);
    q.once('value', function(snapshot) {
        if (snapshot.val() === null) {
            Auth.register($scope.user).then(function(user) {
                return Auth.login($scope.user).then(function() {
                    user.username = $scope.user.username;
                    return Auth.createProfile(user);
                }).then(function() {
                    $location.path('/');
                });
            }, function(error) {
                $scope.error = error.toString();
            });
        } else {
            // username already exists, ask user for a different name
        }
    });
};

Mas está lançando um erro 'indefinido não é uma função' na linha var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username); . Eu comentei o código depois e tentei apenas console.log (q), mas ainda sem alegria.

EDIT 2

O problema acima foi que o tutorial do Thinkster usa o Firebase 0.8 e o orderByChild está disponível apenas em versões posteriores. Atualizado e a resposta de Marein é perfeita.


Eu tive um problema parecido. Mas foi depois de registrar o usuário com senha e email. No perfil do usuário pode salvar um nome de usuário que deve ser exclusivo e eu encontrei uma solução, talvez isso possa atendê-lo.

Consulta para nome de usuário exclusivo no Firebase

        var ref = new Firebase(FIREBASE_URL + '/users');

        ref.orderByChild("username").equalTo(profile.username).on("child_added", function(snapshot) {
            if (currentUser != snapshot.key()) {
                scope.used = true;
            }   
        }); 

        ref.orderByChild("username").equalTo(profile.username).once("value", function(snap) {
            //console.log("initial data loaded!", Object.keys(snap.val()).length === count);
            if (scope.used) {
                console.log('username already exists');
                scope.used = false;
            }else{
                console.log('username doesnt exists, update it');
                userRef.child('username').set(profile.username);
            }   
        }); 
    };  

Há duas coisas a fazer aqui, uma verificação do lado do cliente e uma regra do lado do servidor.

No lado do cliente, você deseja verificar se o nome de usuário já existe, para poder informar ao usuário que sua entrada é inválida antes de enviá-la ao servidor. Onde exatamente você implementa isso, mas o código seria algo como isto:

var ref = new Firebase('https://YourFirebase.firebaseio.com');
var q = ref.child('profiles').orderByChild('username').equalTo(newUsername);
q.once('value', function(snapshot) {
  if (snapshot.val() === null) {
    // username does not yet exist, go ahead and add new user
  } else {
    // username already exists, ask user for a different name
  }
});

Você pode usar isso para verificar antes de gravar no servidor. No entanto, e se um usuário for mal-intencionado e decidir usar o console JS para gravar no servidor de qualquer maneira? Para evitar isso, você precisa de segurança no servidor.

Tentei encontrar um exemplo de solução, mas tive um problema. Espero que alguém mais experiente venha. Meu problema é o seguinte. Digamos que sua estrutura de banco de dados seja assim:

{
  "profiles" : {
    "profile1" : {
      "username" : "Nick",
      "md5_hash" : "..."
    },
    "profile2" : {
      "username" : "Marein",
      "md5_hash" : "..."
    }
  }
}

Ao adicionar um novo perfil, você deseja ter uma regra para garantir que não exista nenhum objeto de perfil com a mesma propriedade de username . No entanto, até onde eu sei, a linguagem de segurança do Firebase não suporta isso, com essa estrutura de dados.

Uma solução seria alterar a estrutura de dados para usar o username como a chave para cada perfil (em vez de profile1 , profile1 , ...). Dessa forma, só pode haver um objeto com esse nome de usuário automaticamente. A estrutura do banco de dados seria:

{
  "profiles" : {
    "Nick" : {
      "md5_hash" : "..."
    },
    "Marein" : {
      "md5_hash" : "..."
    }
  }
}

Esta pode ser uma solução viável neste caso. No entanto, e se não apenas o nome de usuário, mas por exemplo também o email tiver que ser exclusivo? Eles não podem ser a chave do objeto (a menos que usemos concatenação de strings ...).

Mais uma coisa que vem à mente é, além da lista de perfis, manter uma lista separada de nomes de usuário e uma lista separada de emails também. Em seguida, eles podem ser usados ​​facilmente nas regras de segurança para verificar se o nome de usuário e o email já existem. As regras ficariam assim:

{
  "rules" : {
    ".write" : true,
    ".read" : true,
    "profiles" : {
      "$profile" : {
        "username" : {
          ".validate" : "!root.child('usernames').child(newData.val()).exists()"
        }
      }
    },
    "usernames" : {
      "$username" : {
        ".validate" : "newData.isString()"
      }
    }
  }
}

No entanto, agora encontramos outro problema; como garantir que, quando um novo perfil for criado, o nome de usuário (e o email) também sejam colocados nessas novas listas? [1]

Por sua vez, isso pode ser resolvido retirando o código de criação de perfil do cliente e colocando-o em um servidor. O cliente precisaria pedir ao servidor para criar um novo perfil, e o servidor garantiria que todas as tarefas necessárias fossem executadas.

No entanto, parece que fomos muito longe para responder a essa pergunta. Talvez tenha esquecido algo e as coisas sejam mais simples do que parecem. Quaisquer pensamentos são apreciados.

Além disso, desculpas se esta resposta for mais uma pergunta do que uma resposta, sou novo no SO e ainda não tenho certeza do que é apropriado como resposta.

[1] Embora você possa argumentar que isso não precisa ser garantido, como um usuário mal-intencionado só se machucaria se não reivindicasse sua identidade única?





angularfire