feat: delete users - not tested
This commit is contained in:
200
backend/src/core/auth/auth0-management.client.ts
Normal file
200
backend/src/core/auth/auth0-management.client.ts
Normal file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* Auth0 Management API Client
|
||||
* Provides methods for user management operations via Auth0 Management API
|
||||
*/
|
||||
import { ManagementClient } from 'auth0';
|
||||
import { appConfig } from '../config/config-loader';
|
||||
import { logger } from '../logging/logger';
|
||||
|
||||
interface CreateUserParams {
|
||||
email: string;
|
||||
password: string;
|
||||
}
|
||||
|
||||
interface UserDetails {
|
||||
userId: string;
|
||||
email: string;
|
||||
emailVerified: boolean;
|
||||
}
|
||||
|
||||
class Auth0ManagementClientSingleton {
|
||||
private client: ManagementClient | null = null;
|
||||
private readonly CONNECTION_NAME = 'Username-Password-Authentication';
|
||||
|
||||
/**
|
||||
* Lazy initialization of ManagementClient to avoid startup issues
|
||||
*/
|
||||
private getClient(): ManagementClient {
|
||||
if (!this.client) {
|
||||
const config = appConfig.getAuth0ManagementConfig();
|
||||
|
||||
this.client = new ManagementClient({
|
||||
domain: config.domain,
|
||||
clientId: config.clientId,
|
||||
clientSecret: config.clientSecret,
|
||||
});
|
||||
|
||||
logger.info('Auth0 Management API client initialized');
|
||||
}
|
||||
|
||||
return this.client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new user in Auth0
|
||||
* @param email User's email address
|
||||
* @param password User's password
|
||||
* @returns Auth0 user ID
|
||||
*/
|
||||
async createUser({ email, password }: CreateUserParams): Promise<string> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
|
||||
const response = await client.users.create({
|
||||
connection: this.CONNECTION_NAME,
|
||||
email,
|
||||
password,
|
||||
email_verified: false,
|
||||
});
|
||||
|
||||
const user = response.data;
|
||||
if (!user.user_id) {
|
||||
throw new Error('Auth0 did not return a user_id');
|
||||
}
|
||||
|
||||
logger.info('User created in Auth0', { userId: user.user_id, email });
|
||||
return user.user_id;
|
||||
} catch (error) {
|
||||
logger.error('Failed to create user in Auth0', { email, error });
|
||||
throw new Error(`Auth0 user creation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get user details from Auth0
|
||||
* @param userId Auth0 user ID (format: auth0|xxx or google-oauth2|xxx)
|
||||
* @returns User details including email and verification status
|
||||
*/
|
||||
async getUser(userId: string): Promise<UserDetails> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
|
||||
const response = await client.users.get({ id: userId });
|
||||
const user = response.data;
|
||||
|
||||
if (!user.email) {
|
||||
throw new Error('User email not found in Auth0 response');
|
||||
}
|
||||
|
||||
logger.info('Retrieved user from Auth0', { userId, email: user.email });
|
||||
|
||||
return {
|
||||
userId: user.user_id || userId,
|
||||
email: user.email,
|
||||
emailVerified: user.email_verified || false,
|
||||
};
|
||||
} catch (error) {
|
||||
logger.error('Failed to get user from Auth0', { userId, error });
|
||||
throw new Error(`Auth0 user retrieval failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resend email verification to user
|
||||
* @param userId Auth0 user ID
|
||||
*/
|
||||
async resendVerificationEmail(userId: string): Promise<void> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
|
||||
await client.jobs.verifyEmail({
|
||||
user_id: userId,
|
||||
});
|
||||
|
||||
logger.info('Verification email sent', { userId });
|
||||
} catch (error) {
|
||||
logger.error('Failed to send verification email', { userId, error });
|
||||
throw new Error(`Failed to send verification email: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if user's email is verified
|
||||
* @param userId Auth0 user ID
|
||||
* @returns true if email is verified, false otherwise
|
||||
*/
|
||||
async checkEmailVerified(userId: string): Promise<boolean> {
|
||||
try {
|
||||
const user = await this.getUser(userId);
|
||||
return user.emailVerified;
|
||||
} catch (error) {
|
||||
logger.error('Failed to check email verification status', { userId, error });
|
||||
throw new Error(`Failed to check email verification: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a user from Auth0 (permanent deletion)
|
||||
* @param userId Auth0 user ID (format: auth0|xxx or google-oauth2|xxx)
|
||||
*/
|
||||
async deleteUser(userId: string): Promise<void> {
|
||||
try {
|
||||
const client = this.getClient();
|
||||
|
||||
await client.users.delete({ id: userId });
|
||||
|
||||
logger.info('User deleted from Auth0', { userId });
|
||||
} catch (error) {
|
||||
logger.error('Failed to delete user from Auth0', { userId, error });
|
||||
throw new Error(`Auth0 user deletion failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify user password using Resource Owner Password Grant
|
||||
* @param email User's email address
|
||||
* @param password User's password to verify
|
||||
* @returns true if password is valid, false otherwise
|
||||
*/
|
||||
async verifyPassword(email: string, password: string): Promise<boolean> {
|
||||
try {
|
||||
const config = appConfig.getAuth0ManagementConfig();
|
||||
|
||||
const response = await fetch(`https://${config.domain}/oauth/token`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
grant_type: 'password',
|
||||
username: email,
|
||||
password,
|
||||
client_id: config.clientId,
|
||||
client_secret: config.clientSecret,
|
||||
audience: `https://${config.domain}/api/v2/`,
|
||||
scope: 'openid profile email',
|
||||
}),
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
logger.info('Password verification successful', { email });
|
||||
return true;
|
||||
}
|
||||
|
||||
if (response.status === 403 || response.status === 401) {
|
||||
logger.info('Password verification failed - invalid credentials', { email });
|
||||
return false;
|
||||
}
|
||||
|
||||
const errorBody = await response.text();
|
||||
logger.error('Password verification request failed', { email, status: response.status, error: errorBody });
|
||||
throw new Error(`Password verification failed with status ${response.status}`);
|
||||
} catch (error) {
|
||||
logger.error('Failed to verify password', { email, error });
|
||||
throw new Error(`Password verification failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Export singleton instance
|
||||
export const auth0ManagementClient = new Auth0ManagementClientSingleton();
|
||||
Reference in New Issue
Block a user