Skip to content

Commit 094371a

Browse files
dylanjeffersclaude
andcommitted
feat(identity): add email verification flow
Add native email verification to identity-service so we can stop relying on Bouncer's deliverability check. New columns isEmailVerified, emailVerificationToken (sha256-hashed), and emailVerificationTokenCreatedAt are added to the Users table. Signup now sends a verification email with a 24h-expiring token, exposes GET /email/verify and authed POST /email/resend-verification, and rejects signups from disposable-email domains (open-source blocklist embedded as a static file). Recovery and welcome email suppression now honors isEmailVerified, falling back to the legacy isEmailDeliverable flag so existing accounts keep working. Bouncer code is intentionally left in place; removal will follow once verification has rolled out. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0c9b26f commit 094371a

11 files changed

Lines changed: 5840 additions & 3 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
'use strict'
2+
3+
module.exports = {
4+
up: async (queryInterface, Sequelize) => {
5+
await queryInterface.addColumn('Users', 'isEmailVerified', {
6+
type: Sequelize.BOOLEAN,
7+
allowNull: false,
8+
defaultValue: false
9+
})
10+
await queryInterface.addColumn('Users', 'emailVerificationToken', {
11+
type: Sequelize.STRING,
12+
allowNull: true
13+
})
14+
await queryInterface.addColumn('Users', 'emailVerificationTokenCreatedAt', {
15+
type: Sequelize.DATE,
16+
allowNull: true
17+
})
18+
await queryInterface.addIndex('Users', ['emailVerificationToken'], {
19+
name: 'users_email_verification_token_idx'
20+
})
21+
},
22+
23+
down: async (queryInterface) => {
24+
await queryInterface.removeIndex(
25+
'Users',
26+
'users_email_verification_token_idx'
27+
)
28+
await queryInterface.removeColumn(
29+
'Users',
30+
'emailVerificationTokenCreatedAt'
31+
)
32+
await queryInterface.removeColumn('Users', 'emailVerificationToken')
33+
await queryInterface.removeColumn('Users', 'isEmailVerified')
34+
}
35+
}

packages/identity-service/src/config.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -773,6 +773,12 @@ const config = convict({
773773
env: 'websiteHost',
774774
default: 'https://audius.co'
775775
},
776+
identityServiceHost: {
777+
doc: 'Publicly reachable base URL of this identity service instance',
778+
format: String,
779+
env: 'identityServiceHost',
780+
default: 'https://identityservice.audius.co'
781+
},
776782
amplitudeAPIKey: {
777783
doc: 'Amplitude API key',
778784
format: String,

0 commit comments

Comments
 (0)