@bernierllc/sender-identity-verification 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.test +8 -0
- package/.eslintrc.js +30 -0
- package/README.md +376 -0
- package/__tests__/SenderIdentityVerification.test.ts +461 -0
- package/__tests__/__mocks__/fetch-mock.ts +156 -0
- package/__tests__/additional-coverage.test.ts +129 -0
- package/__tests__/additional-error-coverage.test.ts +483 -0
- package/__tests__/branch-coverage.test.ts +509 -0
- package/__tests__/config.test.ts +119 -0
- package/__tests__/error-handling.test.ts +321 -0
- package/__tests__/final-branch-coverage.test.ts +372 -0
- package/__tests__/integration.real-api.test.ts +295 -0
- package/__tests__/providers.test.ts +331 -0
- package/__tests__/service-coverage.test.ts +412 -0
- package/dist/SenderIdentityVerification.d.ts +72 -0
- package/dist/SenderIdentityVerification.js +643 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +38 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +61 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/providers/MailgunProvider.d.ts +13 -0
- package/dist/providers/MailgunProvider.js +35 -0
- package/dist/providers/SESProvider.d.ts +12 -0
- package/dist/providers/SESProvider.js +47 -0
- package/dist/providers/SMTPProvider.d.ts +12 -0
- package/dist/providers/SMTPProvider.js +30 -0
- package/dist/providers/SendGridProvider.d.ts +19 -0
- package/dist/providers/SendGridProvider.js +98 -0
- package/dist/templates/verification-email.d.ts +9 -0
- package/dist/templates/verification-email.js +67 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.js +33 -0
- package/dist/utils/domain-extractor.d.ts +4 -0
- package/dist/utils/domain-extractor.js +20 -0
- package/jest.config.cjs +33 -0
- package/package.json +60 -0
- package/src/SenderIdentityVerification.ts +796 -0
- package/src/config.ts +81 -0
- package/src/errors.ts +64 -0
- package/src/global.d.ts +24 -0
- package/src/index.ts +24 -0
- package/src/providers/MailgunProvider.ts +35 -0
- package/src/providers/SESProvider.ts +51 -0
- package/src/providers/SMTPProvider.ts +29 -0
- package/src/providers/SendGridProvider.ts +108 -0
- package/src/templates/verification-email.ts +67 -0
- package/src/types.ts +163 -0
- package/src/utils/domain-extractor.ts +18 -0
- package/tsconfig.json +22 -0
package/.env.test
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
|
|
2
|
+
NAMECOM_TESTING_API_KEY=b5944374cc5398daea865ba0780906bca8bf4b14
|
|
3
|
+
NAMECOM_TESTING_USERNAME=mkbernier-test
|
|
4
|
+
NAMECOM_API_KEY=43bb0dbe1e762ce1900a459e8ebd4076e006418f
|
|
5
|
+
NAMECOM_KEY_NAME=domain_testing_key
|
|
6
|
+
SENDGRID_API_KEY=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
|
|
7
|
+
|
|
8
|
+
TESTING_DOMAIN=bernierllc.com
|
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2025 Bernier LLC
|
|
3
|
+
|
|
4
|
+
This file is licensed to the client under a limited-use license.
|
|
5
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
6
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
module.exports = {
|
|
10
|
+
parser: '@typescript-eslint/parser',
|
|
11
|
+
extends: [
|
|
12
|
+
'eslint:recommended',
|
|
13
|
+
'plugin:@typescript-eslint/recommended'
|
|
14
|
+
],
|
|
15
|
+
parserOptions: {
|
|
16
|
+
ecmaVersion: 2020,
|
|
17
|
+
sourceType: 'module',
|
|
18
|
+
project: './tsconfig.json'
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
'@typescript-eslint/no-explicit-any': 'error',
|
|
22
|
+
'@typescript-eslint/explicit-function-return-type': 'warn',
|
|
23
|
+
'@typescript-eslint/no-unused-vars': ['error', { 'argsIgnorePattern': '^_' }],
|
|
24
|
+
'no-console': 'off'
|
|
25
|
+
},
|
|
26
|
+
env: {
|
|
27
|
+
node: true,
|
|
28
|
+
jest: true
|
|
29
|
+
}
|
|
30
|
+
};
|
package/README.md
ADDED
|
@@ -0,0 +1,376 @@
|
|
|
1
|
+
# @bernierllc/sender-identity-verification
|
|
2
|
+
|
|
3
|
+
Individual sender email address verification service for email production compliance. Manages sender identity creation, email address verification workflows, provider compliance validation, and administrative interfaces.
|
|
4
|
+
|
|
5
|
+
## 📋 Overview
|
|
6
|
+
|
|
7
|
+
This package enables production email sending by verifying individual sender identities and ensuring provider compliance for email addresses. Works in conjunction with `@bernierllc/email-domain-verification` to ensure complete email sending compliance.
|
|
8
|
+
|
|
9
|
+
**Version**: 1.0.0
|
|
10
|
+
**Category**: service
|
|
11
|
+
**Priority**: critical
|
|
12
|
+
|
|
13
|
+
## 🚀 Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @bernierllc/sender-identity-verification
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 📦 Dependencies
|
|
20
|
+
|
|
21
|
+
This package requires the following dependencies:
|
|
22
|
+
|
|
23
|
+
- `@bernierllc/email-sender` - For sending verification emails
|
|
24
|
+
- `@bernierllc/logger` - For audit logging
|
|
25
|
+
- `@bernierllc/neverhub-adapter` - For service mesh integration
|
|
26
|
+
- `@bernierllc/crypto-utils` - For secure token generation
|
|
27
|
+
- `@bernierllc/retry-policy` - For retry logic
|
|
28
|
+
- `@bernierllc/validators-email` - For email validation
|
|
29
|
+
|
|
30
|
+
**Note**: `@bernierllc/email-domain-verification` is required but not yet published. Domain verification features will be available when this dependency is released.
|
|
31
|
+
|
|
32
|
+
## 🎯 Quick Start
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import { SenderIdentityVerification, EmailProvider } from '@bernierllc/sender-identity-verification';
|
|
36
|
+
|
|
37
|
+
// Initialize the service
|
|
38
|
+
const service = new SenderIdentityVerification({
|
|
39
|
+
verificationBaseUrl: 'https://app.example.com',
|
|
40
|
+
verificationFromEmail: 'noreply@example.com',
|
|
41
|
+
verificationFromName: 'Example Verification',
|
|
42
|
+
sendgridApiKey: process.env.SENDGRID_API_KEY,
|
|
43
|
+
emailSenderConfig: {},
|
|
44
|
+
domainVerificationConfig: {}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
await service.initialize();
|
|
48
|
+
|
|
49
|
+
// Create a sender
|
|
50
|
+
const result = await service.createSender({
|
|
51
|
+
email: 'hello@example.com',
|
|
52
|
+
name: 'Hello Team',
|
|
53
|
+
replyToEmail: 'support@example.com',
|
|
54
|
+
provider: EmailProvider.SENDGRID,
|
|
55
|
+
isDefault: true
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
console.log('Sender created:', result.data);
|
|
59
|
+
// Verification email automatically sent to hello@example.com
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## 📚 API Reference
|
|
63
|
+
|
|
64
|
+
### SenderIdentityVerification
|
|
65
|
+
|
|
66
|
+
Main service class for managing sender identities.
|
|
67
|
+
|
|
68
|
+
#### Constructor
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
new SenderIdentityVerification(config: SenderIdentityConfig)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Configuration Options**:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
interface SenderIdentityConfig {
|
|
78
|
+
// Verification settings
|
|
79
|
+
verificationBaseUrl: string; // Base URL for verification links
|
|
80
|
+
verificationFromEmail: string; // From email for verification emails
|
|
81
|
+
verificationFromName: string; // From name for verification emails
|
|
82
|
+
|
|
83
|
+
// Provider API keys
|
|
84
|
+
sendgridApiKey?: string; // SendGrid API key
|
|
85
|
+
mailgunApiKey?: string; // Mailgun API key
|
|
86
|
+
sesAccessKey?: string; // AWS SES access key
|
|
87
|
+
sesSecretKey?: string; // AWS SES secret key
|
|
88
|
+
sesRegion?: string; // AWS SES region (default: 'us-east-1')
|
|
89
|
+
|
|
90
|
+
// Email sender configuration
|
|
91
|
+
emailSenderConfig: object;
|
|
92
|
+
|
|
93
|
+
// Domain verification configuration
|
|
94
|
+
domainVerificationConfig: object;
|
|
95
|
+
|
|
96
|
+
// Retry configuration
|
|
97
|
+
retryConfig?: object;
|
|
98
|
+
|
|
99
|
+
// Database configuration
|
|
100
|
+
databaseUrl?: string;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
#### Methods
|
|
105
|
+
|
|
106
|
+
##### `createSender(input: CreateSenderInput): Promise<SenderIdentityResult<SenderIdentity>>`
|
|
107
|
+
|
|
108
|
+
Create a new sender identity.
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
const result = await service.createSender({
|
|
112
|
+
email: 'hello@example.com',
|
|
113
|
+
name: 'Hello Team',
|
|
114
|
+
replyToEmail: 'support@example.com',
|
|
115
|
+
replyToName: 'Support Team',
|
|
116
|
+
provider: EmailProvider.SENDGRID,
|
|
117
|
+
isDefault: true,
|
|
118
|
+
skipVerification: false // Set to true for testing
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
##### `verifySender(token: string): Promise<VerificationResult>`
|
|
123
|
+
|
|
124
|
+
Verify a sender using the verification token from the email.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
const result = await service.verifySender('abc123token');
|
|
128
|
+
|
|
129
|
+
if (result.success) {
|
|
130
|
+
console.log('Sender verified:', result.senderId);
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
##### `getSender(senderId: string): Promise<SenderIdentityResult<SenderIdentity>>`
|
|
135
|
+
|
|
136
|
+
Get sender by ID.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const result = await service.getSender('sender-id-123');
|
|
140
|
+
console.log('Sender:', result.data);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
##### `listSenders(options?: ListSendersOptions): Promise<SenderIdentityResult<SenderIdentity[]>>`
|
|
144
|
+
|
|
145
|
+
List senders with optional filtering.
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const result = await service.listSenders({
|
|
149
|
+
provider: EmailProvider.SENDGRID,
|
|
150
|
+
status: SenderStatus.VERIFIED,
|
|
151
|
+
isActive: true,
|
|
152
|
+
limit: 50,
|
|
153
|
+
offset: 0
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
##### `updateSender(senderId: string, input: UpdateSenderInput): Promise<SenderIdentityResult<SenderIdentity>>`
|
|
158
|
+
|
|
159
|
+
Update sender details.
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const result = await service.updateSender('sender-id-123', {
|
|
163
|
+
name: 'Updated Name',
|
|
164
|
+
replyToEmail: 'new-support@example.com',
|
|
165
|
+
isDefault: true
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
##### `deleteSender(senderId: string): Promise<SenderIdentityResult<void>>`
|
|
170
|
+
|
|
171
|
+
Soft delete a sender.
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const result = await service.deleteSender('sender-id-123');
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
##### `checkCompliance(senderId: string): Promise<SenderIdentityResult<ComplianceCheckResult>>`
|
|
178
|
+
|
|
179
|
+
Check provider compliance for a sender.
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
const result = await service.checkCompliance('sender-id-123');
|
|
183
|
+
|
|
184
|
+
if (result.success) {
|
|
185
|
+
console.log('Compliance status:', result.data?.isCompliant);
|
|
186
|
+
console.log('Domain verified:', result.data?.checks.domainVerified);
|
|
187
|
+
console.log('SPF valid:', result.data?.checks.spfValid);
|
|
188
|
+
console.log('DKIM valid:', result.data?.checks.dkimValid);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
##### `getDefaultSender(provider: EmailProvider): Promise<SenderIdentityResult<SenderIdentity>>`
|
|
193
|
+
|
|
194
|
+
Get the default sender for a provider.
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
const result = await service.getDefaultSender(EmailProvider.SENDGRID);
|
|
198
|
+
console.log('Default sender:', result.data?.email);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
##### `resendVerification(senderId: string): Promise<SenderIdentityResult<void>>`
|
|
202
|
+
|
|
203
|
+
Resend verification email.
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
const result = await service.resendVerification('sender-id-123');
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
## 🔌 Integration Status
|
|
210
|
+
|
|
211
|
+
- **Logger**: integrated - Critical for audit trail of sender verification events
|
|
212
|
+
- **Docs-Suite**: ready - Complete API documentation with TypeScript interfaces
|
|
213
|
+
- **NeverHub**: required - Service mesh integration for event publishing
|
|
214
|
+
|
|
215
|
+
## 🔒 Security Considerations
|
|
216
|
+
|
|
217
|
+
### Verification Token Security
|
|
218
|
+
- Tokens are cryptographically secure (32 bytes, URL-safe)
|
|
219
|
+
- Single-use tokens (cleared after successful verification)
|
|
220
|
+
- Time-limited (24 hour expiration)
|
|
221
|
+
- Stored securely in database
|
|
222
|
+
|
|
223
|
+
### Rate Limiting
|
|
224
|
+
- Max 3 verification emails per hour per sender
|
|
225
|
+
- Prevents abuse and spam
|
|
226
|
+
- Locks sender after too many failed attempts
|
|
227
|
+
|
|
228
|
+
### Provider API Key Storage
|
|
229
|
+
- API keys stored in environment variables (never in code)
|
|
230
|
+
- API keys never logged or exposed in responses
|
|
231
|
+
- Encrypted at rest in database (if persisting config)
|
|
232
|
+
|
|
233
|
+
## 📊 Monitoring & Observability
|
|
234
|
+
|
|
235
|
+
### Key Metrics to Track
|
|
236
|
+
|
|
237
|
+
- `sender.created` - Senders created
|
|
238
|
+
- `sender.verified` - Senders verified
|
|
239
|
+
- `sender.failed` - Verification failures
|
|
240
|
+
- `sender.locked` - Senders locked
|
|
241
|
+
- `verification.email_sent` - Verification emails sent
|
|
242
|
+
- `verification.token_validated` - Token validations
|
|
243
|
+
- `compliance.checked` - Compliance checks
|
|
244
|
+
- `compliance.passed` - Compliance passes
|
|
245
|
+
|
|
246
|
+
### Critical Events to Log
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
logger.info('sender.created', { senderId, email, provider });
|
|
250
|
+
logger.info('sender.verified', { senderId, email, verifiedAt });
|
|
251
|
+
logger.warn('sender.verification_failed', { senderId, reason });
|
|
252
|
+
logger.error('sender.locked', { senderId, attempts });
|
|
253
|
+
logger.audit('sender.deleted', { senderId, email, deletedBy });
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## 🔗 Related Packages
|
|
257
|
+
|
|
258
|
+
### Dependencies
|
|
259
|
+
- [@bernierllc/email-sender](../email-sender) - Email sending functionality
|
|
260
|
+
- [@bernierllc/logger](../logger) - Logging infrastructure
|
|
261
|
+
- [@bernierllc/neverhub-adapter](../neverhub-adapter) - Service mesh integration
|
|
262
|
+
- [@bernierllc/crypto-utils](../crypto-utils) - Cryptographic utilities
|
|
263
|
+
- [@bernierllc/retry-policy](../retry-policy) - Retry logic
|
|
264
|
+
- [@bernierllc/validators-email](../validators-email) - Email validation
|
|
265
|
+
|
|
266
|
+
### Dependent Packages
|
|
267
|
+
- [@bernierllc/email-manager](../email-manager) - Email management suite
|
|
268
|
+
- [@bernierllc/email-admin-ui](../email-admin-ui) - Email administration UI
|
|
269
|
+
- [@bernierllc/email-webhook-events](../email-webhook-events) - Email event tracking
|
|
270
|
+
|
|
271
|
+
## 📝 Examples
|
|
272
|
+
|
|
273
|
+
### Basic Sender Creation
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { SenderIdentityVerification, EmailProvider } from '@bernierllc/sender-identity-verification';
|
|
277
|
+
|
|
278
|
+
const service = new SenderIdentityVerification({
|
|
279
|
+
verificationBaseUrl: 'https://app.example.com',
|
|
280
|
+
verificationFromEmail: 'noreply@example.com',
|
|
281
|
+
verificationFromName: 'Example Verification',
|
|
282
|
+
sendgridApiKey: process.env.SENDGRID_API_KEY,
|
|
283
|
+
emailSenderConfig: {},
|
|
284
|
+
domainVerificationConfig: {}
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
await service.initialize();
|
|
288
|
+
|
|
289
|
+
// Create sender
|
|
290
|
+
const result = await service.createSender({
|
|
291
|
+
email: 'hello@example.com',
|
|
292
|
+
name: 'Hello Team',
|
|
293
|
+
replyToEmail: 'support@example.com',
|
|
294
|
+
provider: EmailProvider.SENDGRID,
|
|
295
|
+
isDefault: true
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
console.log('Sender created:', result.data);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Verify Sender Email
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// User clicks verification link in email
|
|
305
|
+
// Link contains token: https://app.example.com/verify-sender?token=abc123
|
|
306
|
+
|
|
307
|
+
const verifyResult = await service.verifySender('abc123');
|
|
308
|
+
|
|
309
|
+
if (verifyResult.success) {
|
|
310
|
+
console.log('Sender verified:', verifyResult.senderId);
|
|
311
|
+
console.log('Verified at:', verifyResult.verifiedAt);
|
|
312
|
+
} else {
|
|
313
|
+
console.error('Verification failed:', verifyResult.message);
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### List Verified Senders
|
|
318
|
+
|
|
319
|
+
```typescript
|
|
320
|
+
// List all verified senders for SendGrid
|
|
321
|
+
const sendersResult = await service.listSenders({
|
|
322
|
+
provider: EmailProvider.SENDGRID,
|
|
323
|
+
status: SenderStatus.VERIFIED,
|
|
324
|
+
isActive: true
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
console.log('Verified SendGrid senders:', sendersResult.data);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Check Compliance
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
// Check compliance for a sender
|
|
334
|
+
const complianceResult = await service.checkCompliance('sender-id-123');
|
|
335
|
+
|
|
336
|
+
if (complianceResult.success) {
|
|
337
|
+
const compliance = complianceResult.data!;
|
|
338
|
+
|
|
339
|
+
console.log('Compliance Status:', compliance.isCompliant ? 'PASS' : 'FAIL');
|
|
340
|
+
console.log('Domain Verified:', compliance.checks.domainVerified);
|
|
341
|
+
console.log('SPF Valid:', compliance.checks.spfValid);
|
|
342
|
+
console.log('DKIM Valid:', compliance.checks.dkimValid);
|
|
343
|
+
|
|
344
|
+
if (!compliance.isCompliant) {
|
|
345
|
+
console.log('Errors:', compliance.errors);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
## 🐛 Troubleshooting
|
|
351
|
+
|
|
352
|
+
### Common Issues
|
|
353
|
+
|
|
354
|
+
**Issue**: Domain not verified error
|
|
355
|
+
**Solution**: Ensure the domain is verified using `@bernierllc/email-domain-verification` before creating senders
|
|
356
|
+
|
|
357
|
+
**Issue**: Sender already exists
|
|
358
|
+
**Solution**: Check for existing senders with `listSenders()` before creating new ones
|
|
359
|
+
|
|
360
|
+
**Issue**: Verification token expired
|
|
361
|
+
**Solution**: Use `resendVerification()` to send a new verification email
|
|
362
|
+
|
|
363
|
+
**Issue**: Too many verification emails
|
|
364
|
+
**Solution**: Rate limiting prevents more than 3 emails per hour - wait before retrying
|
|
365
|
+
|
|
366
|
+
## 📜 License
|
|
367
|
+
|
|
368
|
+
Copyright (c) 2025 Bernier LLC
|
|
369
|
+
|
|
370
|
+
This file is licensed to the client under a limited-use license.
|
|
371
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
372
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
373
|
+
|
|
374
|
+
## 🤝 Support
|
|
375
|
+
|
|
376
|
+
For issues, questions, or contributions, please contact Bernier LLC.
|