feat: add Terms & Conditions checkbox to signup (refs #4)
All checks were successful
Deploy to Staging / Build Images (pull_request) Successful in 4m38s
Deploy to Staging / Deploy to Staging (pull_request) Successful in 28s
Deploy to Staging / Verify Staging (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Ready (pull_request) Successful in 6s
Deploy to Staging / Notify Staging Failure (pull_request) Has been skipped

- Add terms_agreements table for legal audit trail
- Create terms-agreement feature capsule with repository
- Modify signup to create terms agreement atomically
- Add checkbox with PDF link to SignupForm
- Capture IP, User-Agent, terms version, content hash
- Update CLAUDE.md documentation index

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Eric Gullickson
2026-01-03 12:27:45 -06:00
parent 0391a23bb6
commit dec91ccfc2
14 changed files with 390 additions and 15 deletions

View File

@@ -5,6 +5,8 @@
import { auth0ManagementClient } from '../../../core/auth/auth0-management.client';
import { UserProfileRepository } from '../../user-profile/data/user-profile.repository';
import { TermsAgreementRepository } from '../../terms-agreement/data/terms-agreement.repository';
import { pool } from '../../../core/config/database';
import { logger } from '../../../core/logging/logger';
import {
SignupRequest,
@@ -13,17 +15,21 @@ import {
ResendVerificationResponse,
SecurityStatusResponse,
PasswordResetResponse,
TermsData,
} from './auth.types';
export class AuthService {
constructor(private userProfileRepository: UserProfileRepository) {}
constructor(
private userProfileRepository: UserProfileRepository,
private termsAgreementRepository: TermsAgreementRepository
) {}
/**
* Create a new user account
* 1. Create user in Auth0 (which automatically sends verification email)
* 2. Create local user profile with emailVerified=false
* 2. Create local user profile and terms agreement atomically
*/
async signup(request: SignupRequest): Promise<SignupResponse> {
async signup(request: SignupRequest, termsData: TermsData): Promise<SignupResponse> {
const { email, password } = request;
try {
@@ -36,14 +42,42 @@ export class AuthService {
logger.info('Auth0 user created', { auth0UserId, email });
// Create local user profile
const userProfile = await this.userProfileRepository.create(
auth0UserId,
email,
undefined // displayName is optional
);
// Create local user profile and terms agreement in a transaction
const client = await pool.connect();
try {
await client.query('BEGIN');
logger.info('User profile created', { userId: userProfile.id, email });
// Create user profile
const profileQuery = `
INSERT INTO user_profiles (auth0_sub, email, subscription_tier)
VALUES ($1, $2, 'free')
RETURNING id
`;
const profileResult = await client.query(profileQuery, [auth0UserId, email]);
const profileId = profileResult.rows[0].id;
logger.info('User profile created', { userId: profileId, email });
// Create terms agreement
await this.termsAgreementRepository.create({
userId: auth0UserId,
ipAddress: termsData.ipAddress,
userAgent: termsData.userAgent,
termsVersion: termsData.termsVersion,
termsUrl: termsData.termsUrl,
termsContentHash: termsData.termsContentHash,
}, client);
logger.info('Terms agreement created', { userId: auth0UserId });
await client.query('COMMIT');
} catch (error) {
await client.query('ROLLBACK');
logger.error('Transaction failed, rolling back', { error, email });
throw error;
} finally {
client.release();
}
return {
userId: auth0UserId,