Hi Auth0 Community,
I’m having trouble ensuring email uniqueness across social and database connections on my Auth0 setup. I need to prevent users from registering or logging in with a social connection (like Google) if their email is already used in a Username-Password database connection, and vice versa.
Scenario:
- Setup:
- Node.js + Typescript web platform.
- Auth0 for authentication.
- Users can register with both Username-Password and Social Connections.
- Requirements:
- Email Uniqueness: Email must be unique across all connection types.
- Block Social Login if Email Exists in Database Connection: If a user tries to log in with a social connection and their email is already registered in a Username-Password database connection, they should be blocked.
- Block Database Registration if Email Exists in Social Connection: If a user tries to register with a Username-Password database connection and their email is already registered via a social connection, they should be blocked.
- Current Implementation:
- Pre-User Registration Action: Checks for existing emails before completing registration with a Username-Password database connection.
- Post-Login Action: Intended to check if an email exists in any connection type when a user logs in with a social connection.
- Problems Encountered:
- Using the Auth0 Management API in actions to fetch users by email.
- Handling API tokens within Auth0 Actions.
- Consistently getting errors (Extensibility error in all LOGIN/SINGUP when I deploy the Actions) and unable to achieve the desired email uniqueness enforcement.
- Code Snippets:
Pre-User Registration Action:
- I have the respective Secrets and Dependencies configured.
- The function is doing 2 things: preventing repeated emails for being registered within Auth0 & having an encrypted connection with my database to store the user information.
const axios = require("axios");
const crypto = require("crypto");
function encryptToken(token, key, iv) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(token, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted; // Include the IV with the encrypted data
}
exports.onExecutePreUserRegistration = async (event, api) => {
const token = event.secrets.SECRET;
const key = Buffer.from(event.secrets.ENCRYPTION_KEY, 'hex');
const iv = crypto.randomBytes(16);
const encryptedToken = encryptToken(token, key, iv);
const userData = {
email: event.user.email,
ip: event.request.ip,
country: event.request.geoip.countryName,
city: event.request.geoip.cityName,
image: event.user.picture,
};
const config = {
headers: {
'Authorization': `Bearer ${encryptedToken}`
}
};
const email = event.user.email;
const ManagementClient = require('auth0').ManagementClient;
const management = new ManagementClient({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
});
try {
const res = await management.usersByEmail.getByEmail(email);
if (res.data.length > 0) {
// Email already exists
api.access.deny("email already used", "email already used");
}
const response = await axios.post('https://backend.fanz.com.ar/public/users', userData, config);
console.log('Data sent to server:', response.data);
} catch (err) {
console.error('Failed to send data to server:', err);
api.access.deny("email already used","email already used");
throw new Error('Failed to communicate with server');
}
};
Post-Login Action:
- Dependencies and Secrets are correct.
const axios = require('axios');
const crypto = require('crypto');
const { ManagementClient } = require('auth0');
function encryptToken(token, key, iv) {
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
let encrypted = cipher.update(token, 'utf8', 'hex');
encrypted += cipher.final('hex');
return iv.toString('hex') + ':' + encrypted; // Include the IV with the encrypted data
}
exports.onExecutePostLogin = async (event, api) => {
if (event.connection.strategy === 'auth0') {
// If the user is logging in with a database connection, we don't need to check anything.
return;
}
const token = event.secrets.SECRET;
const key = Buffer.from(event.secrets.ENCRYPTION_KEY, 'hex');
const iv = crypto.randomBytes(16);
const encryptedToken = encryptToken(token, key, iv);
const userData = {
email: event.user.email,
ip: event.request.ip,
country: event.request.geoip.countryName,
city: event.request.geoip.cityName,
image: event.user.picture,
};
const config = {
headers: {
'Authorization': `Bearer ${encryptedToken}`
}
};
const email = event.user.email;
const management = new ManagementClient({
domain: event.secrets.domain,
clientId: event.secrets.clientId,
clientSecret: event.secrets.clientSecret,
});
try {
const users = await management.getUsersByEmail(email);
const userExistsInDatabase = users.some(user =>
user.identities.some(identity => identity.provider === 'auth0')
);
if (userExistsInDatabase) {
// User exists with database connection
api.access.deny("email already used", "email already used");
}
// Continue with logging in the user
const response = await axios.post('https://backend.fanz.com.ar/public/users', userData, config);
console.log('Data sent to server:', response.data);
} catch (err) {
console.error('Failed to send data to server:', err);
api.access.deny("email already used", "email already used");
throw new Error('Failed to communicate with server');
}
};
Any help or guidance would be greatly appreciated!
Thanks in advance,
Julian
Pd: Tried this Action for Pre User Registration but didn’t work