Skip to content

Commit

Permalink
メールアドレスの認証にverifymail.ioを使えるようにする。
Browse files Browse the repository at this point in the history
  • Loading branch information
nafu-at committed Nov 18, 2023
1 parent ceb2413 commit 971ba07
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 8 deletions.
18 changes: 18 additions & 0 deletions packages/backend/migration/1700303245007-supportVerifyMailApi.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/

export class SupportVerifyMailApi1700303245007 {
name = 'SupportVerifyMailApi1700303245007'

async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "verifymailAuthKey" character varying(1024)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "enableVerifymailApi" boolean NOT NULL DEFAULT false`);
}

async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableVerifymailApi"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "verifymailAuthKey"`);
}
}
92 changes: 84 additions & 8 deletions packages/backend/src/core/EmailService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import type Logger from '@/logger.js';
import type { UserProfilesRepository } from '@/models/_.js';
import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js';
import {URLSearchParams} from "node:url";
import { HttpRequestService } from '@/core/HttpRequestService.js';
import {SubOutputFormat} from "deep-email-validator/dist/output/output.js";

@Injectable()
export class EmailService {
Expand All @@ -27,6 +30,7 @@ export class EmailService {

private metaService: MetaService,
private loggerService: LoggerService,
private httpRequestService: HttpRequestService,
) {
this.logger = this.loggerService.getLogger('email');
}
Expand Down Expand Up @@ -160,14 +164,25 @@ export class EmailService {
email: emailAddress,
});

const validated = meta.enableActiveEmailValidation ? await validateEmail({
email: emailAddress,
validateRegex: true,
validateMx: true,
validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
validateDisposable: true, // 捨てアドかどうかチェック
validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
}) : { valid: true, reason: null };
const verifymailApi = meta.enableVerifymailApi && meta.verifymailAuthKey != null;
let validated;

if (meta.enableActiveEmailValidation) {
if (verifymailApi) {
validated = await this.verifyMail(emailAddress, meta.verifymailAuthKey);
} else {
validated = meta.enableActiveEmailValidation ? await validateEmail({
email: emailAddress,
validateRegex: true,
validateMx: true,
validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
validateDisposable: true, // 捨てアドかどうかチェック
validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
}) : { valid: true, reason: null };
}
} else {
validated = { valid: true, reason: null };
}

const available = exist === 0 && validated.valid;

Expand All @@ -182,4 +197,65 @@ export class EmailService {
null,
};
}

private async verifyMail(emailAddress: string, verifymailAuthKey: string): Promise<{
valid: boolean;
reason: 'used' | 'format' | 'disposable' | 'mx' | 'smtp' | null;
}> {
const endpoint = 'https://verifymail.io/api/' + emailAddress + '?key=' + verifymailAuthKey;
const res = await this.httpRequestService.send(endpoint, {
method: 'GET',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json, */*',
},
});

const json = (await res.json()) as {
block: boolean;
catch_all: boolean;
deliverable_email: boolean;
disposable: boolean;
domain: string;
email_address: string;
email_provider: string;
mx: boolean;
mx_fallback: boolean;
mx_host: string[];
mx_ip: string[];
mx_priority: { [key: string]: number };
privacy: boolean;
related_domains: string[];
};

if (json.email_address === undefined) {
return {
valid: false,
reason: 'format',
};
}
if (json.deliverable_email !== undefined && !json.deliverable_email) {
return {
valid: false,
reason: 'smtp',
};
}
if (json.disposable) {
return {
valid: false,
reason: 'disposable',
};
}
if (json.mx !== undefined && !json.mx) {
return {
valid: false,
reason: 'mx',
};
}

return {
valid: true,
reason: null,
};
}
}
11 changes: 11 additions & 0 deletions packages/backend/src/models/Meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -446,6 +446,17 @@ export class MiMeta {
})
public enableActiveEmailValidation: boolean;

@Column('boolean', {
default: false,
})
public enableVerifymailApi: boolean;

@Column('varchar', {
length: 1024,
nullable: true,
})
public verifymailAuthKey: string | null;

@Column('boolean', {
default: true,
})
Expand Down
14 changes: 14 additions & 0 deletions packages/backend/src/server/api/endpoints/admin/update-meta.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export const paramDef = {
objectStorageS3ForcePathStyle: { type: 'boolean' },
enableIpLogging: { type: 'boolean' },
enableActiveEmailValidation: { type: 'boolean' },
enableVerifymailApi: { type: 'boolean' },
verifymailAuthKey: { type: 'string', nullable: true },
enableChartsForRemoteUser: { type: 'boolean' },
enableChartsForFederatedInstances: { type: 'boolean' },
enableServerMachineStats: { type: 'boolean' },
Expand Down Expand Up @@ -454,6 +456,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.enableActiveEmailValidation = ps.enableActiveEmailValidation;
}

if (ps.enableVerifymailApi !== undefined) {
set.enableVerifymailApi = ps.enableVerifymailApi;
}

if (ps.verifymailAuthKey !== undefined) {
if (ps.verifymailAuthKey === '') {
set.verifymailAuthKey = null;
} else {
set.verifymailAuthKey = ps.verifymailAuthKey;
}
}

if (ps.enableChartsForRemoteUser !== undefined) {
set.enableChartsForRemoteUser = ps.enableChartsForRemoteUser;
}
Expand Down
13 changes: 13 additions & 0 deletions packages/frontend/src/pages/admin/security.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="enableActiveEmailValidation" @update:modelValue="save">
<template #label>Enable</template>
</MkSwitch>
<MkSwitch v-model="enableVerifymailApi" @update:modelValue="save">
<template #label>Use Verifymail API</template>
</MkSwitch>
<MkInput v-model="verifymailAuthKey" @update:modelValue="save">
<template #prefix><i class="ti ti-key"></i></template>
<template #label>Verifymail API Auth Key</template>
</MkInput>
</div>
</MkFolder>

Expand Down Expand Up @@ -132,6 +139,8 @@ let setSensitiveFlagAutomatically: boolean = $ref(false);
let enableSensitiveMediaDetectionForVideos: boolean = $ref(false);
let enableIpLogging: boolean = $ref(false);
let enableActiveEmailValidation: boolean = $ref(false);
let enableVerifymailApi: boolean = $ref(false);
let verifymailAuthKey: string | null = $ref(null);

async function init() {
const meta = await os.api('admin/meta');
Expand All @@ -150,6 +159,8 @@ async function init() {
enableSensitiveMediaDetectionForVideos = meta.enableSensitiveMediaDetectionForVideos;
enableIpLogging = meta.enableIpLogging;
enableActiveEmailValidation = meta.enableActiveEmailValidation;
enableVerifymailApi = meta.enableVerifymailApi;
verifymailAuthKey = meta.verifymailAuthKey;
}

function save() {
Expand All @@ -167,6 +178,8 @@ function save() {
enableSensitiveMediaDetectionForVideos,
enableIpLogging,
enableActiveEmailValidation,
enableVerifymailApi,
verifymailAuthKey,
}).then(() => {
fetchInstance();
});
Expand Down

0 comments on commit 971ba07

Please sign in to comment.