useraccounts
- https://github.com/meteor-useraccounts/core/blob/master/Guide.md
- https://atmospherejs.com/useraccounts/core
- https://github.com/meteor-useraccounts/core/
- https://useraccounts-semantic-ui.meteor.com/
-
https://github.com/meteor-useraccounts/semantic-ui
-
Démo: http://useraccounts.meteor.com/
- Boilerplate pour chaque besoin: https://github.com/meteor-useraccounts/boilerplates
TODO
- Définition d'un schéma pour la collection Meteor.users
- Rendre obligatoire l'auth sur les routes /admin/* par exemple
- Usage de la collection meteor_accounts_loginServiceConfiguration
- loginServiceConfiguration (DEFAULT_LOGIN_EXPIRATION_DAYS, ...)
- https://github.com/meteor/meteor/blob/master/packages/accounts-base/accounts_common.js#L55
- Hooks standard: Accounts.onLogin, Accounts.onLoginFailure
Introduction
useraccounts:core fournit toutes les fonctions de base mais aucun template.
il faut utiliser une des extensions comme useraccounts:semantic-ui pour obtenir des templates.
Exemple avec le bouton de connection:
{{> atNavButton}}
- Le code correspondant se trouve dans:
- https://github.com/meteor-useraccounts/semantic-ui/blob/master/lib/at_nav_button.html
- https://github.com/meteor-useraccounts/semantic-ui/blob/master/lib/at_nav_button.js
Relation avec les packages standard Meteor
-
https://www.meteor.com/accounts
A priori, appartient à accounts-ui-unstyled - https://github.com/meteor/meteor/tree/devel/packages/accounts-ui-unstyled passwordSignupFields: 'EMAIL_ONLY'
Accounts.ui.config({ requestPermissions: { facebook: ['user_likes'], github: ['user', 'repo'] }, requestOfflineToken: { google: true }, passwordSignupFields: 'USERNAME_AND_OPTIONAL_EMAIL' // 'USERNAME_AND_EMAIL', 'USERNAME_AND_OPTIONAL_EMAIL', 'USERNAME_ONLY', or 'EMAIL_ONLY' (default). });
Accounts.validateLoginAttempt Accounts.onLoginFailure Accounts.onLogin Meteor.logoutOtherClients //locked-account or unauthorized-ip. Meteor.user() Meteor.loggingIn()
accounts-ui Accounts.loginWithPassword, Accounts.loginWithFacebook, Accounts.createUser and Accounts.forgotPassword
Exemple d'Installation - Authentification par mot de passe - Templates Semantic-ui
- Ne pas installer accounts-ui ?
- Le package installe déjà useraccounts:core accounts-base
- Il ne faut pas installer directement le package useraccounts:core mais son implémentation selon le framework utilisé
- Installer les packages providers comme accounts-password, accounts-github, ... selon les besoins
$ meteor add accounts-password
$ meteor add semantic:ui-css
$ meteor add useraccounts:semantic-ui
Plugin ensureSignedIn
- Rend obligatoire l'authentification pour toutes les routes
Router.plugin('ensureSignedIn');
-
Rend obligatoire l'authentification pour les routes placés dans only[]
-
Voir différence avec AccountsTemplates.configureRoute('ensureSignedIn') ?
Router.plugin('ensureSignedIn', {
only: ['private']
});
- Rend obligatoire l'authentification pour toutes les routes sauf celles placés dans except[]
Router.plugin('ensureSignedIn', {
except: ['home', 'atSignIn', 'atSignUp', 'atForgotPassword']
});
- Personnalisation du template et layout
AccountsTemplates.configureRoute('ensureSignedIn', {
template: 'myLogin',
layoutTemplate: 'myLayout',
});
Api
- Package inclus avec Meteor: Account, AccountsServer
- Coté client, Accounts = new AccountsClient();
- Coté server, Accounts = new AccountsServer(Meteor.server);
- Meteor.users est remplacé par Accounts.users
Template atForm
- Par exemple pour customiser le template d'authentification, placez dans un template
{{> atForm state='signUp'}}
OU
{{> atForm}} car le state par défaut est signUp
States
changePwd Formulaire de changement de mot de passe
enrollAccount Formulaire d'inscription ???
forgotPwd Formulaire pour saisir une email à laquelle sera envoyé le lien de récupération de mot de passe
hide Aucun
resendVerificationEmail Envoi d'un nouveau mail de vérification
resetPwd Réinitialisation du mot de passe
signIn Formulaire d'authentification
signUp ?
verifyEmail Résultat après avoir cliqué sur le lien de récupération de mot de passe reçu par mail
Bouton atNavButton
Configuration
-
https://github.com/meteor-useraccounts/core/blob/master/Guide.md
-
Placez la configuration dans lib/config/at_config.js
Routage
Il n'y a aucune route par défaut.
La définition des routes par AccountsTemplates.configureRoute() s'utilise comme les routes habituels d'Iron Router
Chaque route peut être définit avec ses paramètres par défaut ou personnalisé
AccountsTemplates.configureRoute('signIn');
AccountsTemplates.configureRoute('signIn', {
name: 'signin',
path: '/login',
template: 'myLogin',
layoutTemplate: 'myLayout',
redirect: '/user-profile',
});
AccountsTemplates.configureRoute('signIn', {
// Redirige vers /user/ae8WQQk6DrtDzA2AZ après une authentification réussie
redirect: function(){
var user = Meteor.user();
if (user)
Router.go('/user/' + user._id);
}
});
Exemple de configuration complète
// lib/config/at_config.js
AccountsTemplates.configure({
// Behaviour
confirmPassword: true, // (default: true) - Demande la confirmation du mot de passe (register)
defaultState: "signIn", // (default: signIn) - State par défaut
enablePasswordChange: true, // (default: false) - Active la fonction de changement de mot de passe
enforceEmailVerification: false, // (default: false) - experimental - A utiliser seulement avec le service accounts-password
forbidClientAccountCreation: false, // (default: false) - Si true, désactive l'enregistrement
// Remplace Accounts.config({forbidClientAccountCreation : true});
overrideLoginErrors: true, // (default: true) - Si true, affiche erreur générique de connection sans préciser si mauvais login ou pass
sendVerificationEmail: false, // (default: false) - Si true, envoi un lien de confirmation par mail pour chaque nouvel utilisateur
redirectTimeout: 2000, // (default: 2000) - Timeout de redirection après: enrollAccount, forgotPwd, resetPwd, ou verifyEmail
lowercaseUsername: false, // (default: false) - A REVOIR
socialLoginStyle: 'popup', // (default: popup) - Voir LoginStyle - choix (redirect, popup)
// Si popup, ouvre la page d'authentification du service dans un modal
// Appearance
defaultLayout: undefined, // (default: undefined)
hideSignInLink: false, // (default: false) - Si true, n'affiche jamais le lien de connection
hideSignUpLink: false, // (default: false) - Si true, n'affiche jamais le lien de déconnection
showAddRemoveServices: false, // (default: false) - A revoir - en rapport avec synchro comptes sociaux - https://atmospherejs.com/splendido/accounts-meld
showForgotPasswordLink: false, // (default: false) - Si true, affiche lien de récupération de mot de passe
showLabels: true, // (default: true) - si true, affiche les label au dessous des champs dans les formulaires
showPlaceholders: true, // (default: false) - si true, Affiche les placeholders dans les champs
showResendVerificationEmailLink: false, // (default: false) - Si true, affiche le lien pour envoyer à nouveau le mail de vérification
// Client-side Validation
continuousValidation: false, // (default: false) - Active la validation coté client pendant la saisie
negativeFeedback: false, // (default: false) - continuousValidation
negativeValidation: true, // (default: false) - continuousValidation
positiveValidation: true, // (default: false) - continuousValidation
positiveFeedback: true, // (default: false) - continuousValidation
showValidating: true, // (default: false) -
// Links
homeRoutePath: '/', // (default: /) - Pas Home pour les redirections
//privacyUrl: 'privacy', // (default: undefined) - Affiche lien ?
//termsUrl: 'terms-of-use', // (default: undefined) - Affiche lien ?
// Hooks
//onLogoutHook: myLogoutFunc, // Hook de routage appellé par AccountsTemplates.logout
//onSubmitHook: mySubmitFunc, // Hook appellé quand submit d'un des formulaire est ok - func(error, state)
//preSignUpHook: myPreSubmitFunc, // Comme ci-dessous mais appellé avant submit - func(password, info)
// Texts customization
/*
texts: {
button: {
signUp: "Register Now!"
},
socialSignUp: "Register",
socialIcons: {
"meteor-developer": "fa fa-rocket"
},
title: {
forgotPwd: "Recover Your Password"
},
},
*/
});
Configuration ReCapcha
Les clés peuvent se configurer ici ou dans Meteor.Settings ou dans settings.json
AccountsTemplates.configure({
reCaptcha: {
siteKey: YOUR SITE KEY,
secretKey: YOUR SECRET KEY,
theme: "light", // light ou dark
data_type: "image" // image ou audio
},
showReCaptcha: true
});
Exemple de SubmitHook
var mySubmitFunc = function(error, state){
if (!error) {
if (state === "signIn") {
// Successfully logged in
// ...
}
if (state === "signUp") {
// Successfully registered
// ...
}
}
};
AccountsTemplates.configure({
onSubmitHook: mySubmitFunc
});
Personnalisation de la structure des champs de formulaire
Exemple de personnalisation des champs avec aldeed:template-extension pour le package useraccounts:semantic-ui
Champs spéciaux qu'il faut supprimer avant de redéfinir
current_password
email
password
password_again
username
username_and_email
Exemple avec le champs password
AccountsTemplates.removeField('password');
AccountsTemplates.addField({
_id: 'password',
type: 'password',
required: true,
minLength: 6,
re: /(?=.*\\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,
errStr: 'At least 1 digit, 1 lower-case and 1 upper-case',
});
Particularité de la customisation des champs username ou email
Il faut impérativement supprimer le champs password avant de rédéfinir username ou password
Pour ne pas avoir à définir à nouveau password, procédez comme ci-dessous:
var pwd = AccountsTemplates.removeField('password');
AccountsTemplates.removeField('email');
AccountsTemplates.addFields([
{
_id: "username",
type: "text",
displayName: "username",
required: true,
minLength: 5,
},
{
_id: 'email',
type: 'email',
required: true,
displayName: "email",
re: /.+@(.+){2,}\.(.+){2,}/,
errStr: 'Invalid email',
},
pwd
]);
Changement de la politique de mot de passe
**Demande un mot de passe d'au minimum 6 caractères
AccountsTemplates.addField({
_id: 'password',
type: 'password',
placeholder: {
signUp: "At least six characters"
},
required: true,
minLength: 6,
re: /(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{6,}/,
errStr: 'At least 1 digit, 1 lowercase and 1 uppercase',
});
Ajoute un champs téléphone
Champs phone avec validation coté serveur (fonction isValidPhone à définir):
AccountsTemplates.addField({
_id: 'phone',
type: 'tel',
displayName: "Phone",
required: true,
func: function (number) {
if (Meteor.isServer){
if (isValidPhone(number))
return false; // meaning no error!
return true; // Validation error!
}
},
errStr: 'Invalid Phone number!',
});
Vérifie si le username existe déjà
// server/methods.js
if (Meteor.isServer){
Meteor.methods({
"userExists": function(username){
return !!Meteor.users.findOne({username: username});
},
});
}
// lib/config/at_config.js
AccountsTemplates.addField({
_id: 'username',
type: 'text',
required: true,
func: function(value){
if (Meteor.isClient) {
console.log("Validating username...");
var self = this;
Meteor.call("userExists", value, function(err, userExists){
if (!userExists)
self.setSuccess();
else
self.setError(userExists);
self.setValidating(false);
});
return;
}
// Server
return Meteor.call("userExists", value);
},
});
VOIR AUSSI:
//Dans la définition d'un schéma
username: {
type: String,
regEx: /^[a-z0-9A-Z_]{3,15}$/,
unique: true,
custom: function () {
if (Meteor.isClient && this.isSet) {
Meteor.call("accountsIsUsernameAvailable", this.value, function (error, result) {
if (!result) {
Meteor.users.simpleSchema().namedContext("createUserForm").addInvalidKeys([{name: "username", type: "notUnique"}]);
}
});
}
}
}
Ajout d'un champs Genre
AccountsTemplates.addField({
_id: "gender",
type: "select",
displayName: "Gender",
select: [
{
text: "Male",
value: "male",
},
{
text: "Female",
value: "female",
},
],
});
Champs de souscription à la mailing list
AccountsTemplates.addField({
_id: "mailing_list",
type: "checkbox",
displayName: "Subscribe me to mailing List",
});
Champs caché contenant un code de validation
// http://my.splendido.site/sign-up?email=giorgio@example.com®_code=123
AccountsTemplates.addField({
_id: 'reg_code',
type: 'hidden'
});
Internationalisation - i18n
$ meteor add softwarerero:accounts-t9n
// /lib/config/at_config.js
T9n.setLanguage("fr");
Personnalisation des formulaires UI
Requis: template-extension
$ meteor add aldeed:template-extension
<template name="appAtInput">
{{#if options.dividerBefore}}<hr>{{/if}}
{{> Template.dynamic template=templateName}}
</template>
AccountsTemplates.addField({
_id: "address",
type: "text",
// Options qui seront passés au formulaire
options: {
dividerBefore: true
}
});
Template.appAtInput.replaces("atInput");
Solution complète avec semantic-ui - hors authentification sociale
Installation
$ meteor add accounts-ui-unstyled
$ meteor add accounts-password
$ meteor add semantic:ui-css
$ meteor add softwarerero:accounts-t9n
$ meteor add useraccounts:semantic-ui
# OU
$ meteor add accounts-ui-unstyled accounts-password semantic:ui-css softwarerero:accounts-t9n useraccounts:semantic-ui
# OU pour iron cli
$ iron add accounts-ui-unstyled accounts-password semantic:ui-css softwarerero:accounts-t9n useraccounts:semantic-ui
Dépendances:
less
reactive-var
http
underscore
accounts-base
reactive-dict
check
meteor
templating
blaze
useraccounts:core
iron:router
softwarerero:accounts-t9n
Configuration
Tips - Solution dédié mobile avec Ratchet
- https://atmospherejs.com/useraccounts/ratchet
Structure d'un enregistrement d'un compte utilisateur
- La structure peut varier mais il y a des éléments important à noter:
- emails est une liste d'objet
- services est une collections avec comme clé, le nom du service
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
{ address: "cool@example.com", verified: true },
{ address: "another@different.com", verified: false }
],
createdAt: Wed Aug 21 2013 15:16:52 GMT-0700 (PDT),
profile: {
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
Tips - Publish - Liste des utilisateurs
- Source meteor-cookbook
// server/publish.js
Meteor.publish("usersDirectory", function () {
try{
return Meteor.users.find({}, {fields: {
'_id': true,
'username': true,
'profile': true,
'profile.name': true,
'profile.avatar': true,
'profile.username': true,
'emails': true,
'emails[0].address': true,
'emails.address': true
}});
}catch(error){
console.log(error);
}
});
Tips - Autres solutions
- https://atmospherejs.com/juliancwirko/s-id
- Solution Manuelle
- accounts-entry
Tips - Profile Utilisateur et préférences
// A VOIR
Meteor.publish("UserProfile", function(profileUserId) {
var permissions;
check(profileUserId, Match.OneOf(String, null));
permissions = ['dashboard/orders', 'owner', 'admin', 'dashboard/customers'];
if (profileUserId !== this.userId) {
if (this.userId && (Roles.userIsInRole(this.userId, permissions, ReactionCore.getCurrentShop(this)._id || Roles.userIsInRole(this.userId, permissions, Roles.GLOBAL_GROUP)))) {
return Meteor.users.find({
_id: profileUserId
}, {
fields: {
"emails": true,
"profile.firstName": true,
"profile.lastName": true,
"profile.familyName": true,
"profile.secondName": true,
"profile.name": true,
"services.twitter.profile_image_url_https": true,
"services.facebook.id": true,
"services.google.picture": true,
"services.github.username": true,
"services.instagram.profile_picture": true
}
});
} else {
ReactionCore.Events.info("user profile access denied");
return [];
}
} else if (this.userId) {
return Meteor.users.find({
_id: this.userId
});
} else {
return [];
}
});
Tips - Intégration de iron-router-auth
- https://github.com/zimme/meteor-iron-router-auth
$ meteor add zimme:iron-router-auth
{
authenticate: {
home: 'home',
layout: undefined,
logout: 'logout',
replaceState: undefined,
route: 'login',
template: undefined
},
authorize: {
allow: function() {return true},
deny: function() {return false}, // deny overrides allow
layout: undefined,
replaceState: undefined,
route: undefined,
template: 'notAuthorized'
},
except: ['enroll', 'forgotPassword', 'home', 'login', 'reset', 'verify'],
noAuth: {
dashboard: 'dashboard',
home: 'home',
replaceState: undefined
},
only: ['enroll', 'login']
}