diff options
author | Puyodead1 <puyodead@protonmail.com> | 2023-02-24 01:54:10 -0500 |
---|---|---|
committer | Puyodead1 <puyodead@protonmail.com> | 2023-02-24 01:54:10 -0500 |
commit | 05453ec14880732c5d0d20fd3575bb2b3952760d (patch) | |
tree | c3b7272d7eafff2e988702b9bf3b93f24a101881 | |
parent | add SendGrid transport (diff) | |
download | server-05453ec14880732c5d0d20fd3575bb2b3952760d.tar.xz |
implement password reset
-rw-r--r-- | assets/email_templates/new_login_location.html | 2 | ||||
-rw-r--r-- | assets/email_templates/password_reset_request.html | 2 | ||||
-rw-r--r-- | assets/email_templates/verify_email.html | 2 | ||||
-rw-r--r-- | assets/locales/en/auth.json | 4 | ||||
-rw-r--r-- | assets/schemas.json | 2543 | ||||
-rw-r--r-- | src/api/middlewares/Authentication.ts | 2 | ||||
-rw-r--r-- | src/api/routes/auth/forgot.ts | 92 | ||||
-rw-r--r-- | src/api/routes/auth/reset.ts | 57 | ||||
-rw-r--r-- | src/api/routes/auth/verify/resend.ts | 2 | ||||
-rw-r--r-- | src/util/config/Config.ts | 3 | ||||
-rw-r--r-- | src/util/config/types/PasswordResetConfiguration.ts | 21 | ||||
-rw-r--r-- | src/util/config/types/index.ts | 1 | ||||
-rw-r--r-- | src/util/entities/User.ts | 2 | ||||
-rw-r--r-- | src/util/schemas/ForgotPasswordSchema.ts | 22 | ||||
-rw-r--r-- | src/util/schemas/PasswordResetSchema.ts | 22 | ||||
-rw-r--r-- | src/util/schemas/index.ts | 20 | ||||
-rw-r--r-- | src/util/util/Email.ts | 106 | ||||
-rw-r--r-- | src/util/util/Token.ts | 9 |
18 files changed, 2198 insertions, 714 deletions
diff --git a/assets/email_templates/new_login_location.html b/assets/email_templates/new_login_location.html index e597ac6c..ff262e99 100644 --- a/assets/email_templates/new_login_location.html +++ b/assets/email_templates/new_login_location.html @@ -104,7 +104,7 @@ Alternatively, you can directly paste this link into your browser: </p> - <a href="{verifyUrl}" target="_blank">{verifyUrl}</a> + <a href="{verifyUrl}" target="_blank" style="word-wrap: break-word;">{verifyUrl}</a> </div> </div> </div> diff --git a/assets/email_templates/password_reset_request.html b/assets/email_templates/password_reset_request.html index ab8f4d23..b770e7ba 100644 --- a/assets/email_templates/password_reset_request.html +++ b/assets/email_templates/password_reset_request.html @@ -90,7 +90,7 @@ Alternatively, you can directly paste this link into your browser: </p> - <a href="{passwordResetUrl}" target="_blank" + <a href="{passwordResetUrl}" target="_blank" style="word-wrap: break-word;" >{passwordResetUrl}</a > </div> diff --git a/assets/email_templates/verify_email.html b/assets/email_templates/verify_email.html index 604242c4..481a46d4 100644 --- a/assets/email_templates/verify_email.html +++ b/assets/email_templates/verify_email.html @@ -91,7 +91,7 @@ Alternatively, you can directly paste this link into your browser: </p> - <a href="{emailVerificationUrl}" target="_blank" + <a href="{emailVerificationUrl}" target="_blank" style="word-wrap: break-word;" >{emailVerificationUrl}</a > </div> diff --git a/assets/locales/en/auth.json b/assets/locales/en/auth.json index 2178548e..0521a902 100644 --- a/assets/locales/en/auth.json +++ b/assets/locales/en/auth.json @@ -16,5 +16,9 @@ "USERNAME_TOO_MANY_USERS": "Too many users have this username, please try another", "GUESTS_DISABLED": "Guest users are disabled", "TOO_MANY_REGISTRATIONS": "Too many registrations, please try again later" + }, + "password_reset": { + "EMAIL_DOES_NOT_EXIST": "Email does not exist.", + "INVALID_TOKEN": "Invalid token." } } diff --git a/assets/schemas.json b/assets/schemas.json index 2bfb525d..1fdfa361 100644 --- a/assets/schemas.json +++ b/assets/schemas.json @@ -4584,6 +4584,599 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, + "ForgotPasswordSchema": { + "type": "object", + "properties": { + "login": { + "type": "string" + }, + "captcha_key": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "login" + ], + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "number" + }, + "ChannelModifySchema": { + "type": "object", + "properties": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 2, + 255, + 3, + 33, + 34, + 35, + 4, + 5, + 6, + 64, + 7, + 8, + 9 + ], + "type": "number" + }, + "topic": { + "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "default_reaction_emoji": { + "type": [ + "null", + "string" + ] + }, + "flags": { + "type": "integer" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + }, + "video_quality_mode": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "ActivitySchema": { + "type": "object", + "properties": { + "afk": { + "type": "boolean" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "activities": { + "type": "array", + "items": { + "$ref": "#/definitions/Activity" + } + }, + "since": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "status" + ] + }, + "Status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], + "type": "string" + }, + "Activity": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ActivityType" + }, + "url": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "timestamps": { + "type": "object", + "properties": { + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "end", + "start" + ] + }, + "application_id": { + "type": "string" + }, + "details": { + "type": "string" + }, + "state": { + "type": "string" + }, + "emoji": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "animated", + "name" + ] + }, + "party": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "size": { + "type": "array", + "items": [ + { + "type": "integer" + } + ], + "minItems": 1, + "maxItems": 1 + } + }, + "additionalProperties": false + }, + "assets": { + "type": "object", + "properties": { + "large_image": { + "type": "string" + }, + "large_text": { + "type": "string" + }, + "small_image": { + "type": "string" + }, + "small_text": { + "type": "string" + } + }, + "additionalProperties": false + }, + "secrets": { + "type": "object", + "properties": { + "join": { + "type": "string" + }, + "spectate": { + "type": "string" + }, + "match": { + "type": "string" + } + }, + "additionalProperties": false + }, + "instance": { + "type": "boolean" + }, + "flags": { + "type": "string" + }, + "id": { + "type": "string" + }, + "sync_id": { + "type": "string" + }, + "metadata": { + "type": "object", + "properties": { + "context_uri": { + "type": "string" + }, + "album_id": { + "type": "string" + }, + "artist_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "album_id", + "artist_ids" + ] + }, + "session_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "flags", + "name", + "session_id", + "type" + ] + }, + "ActivityType": { + "enum": [ + 0, + 1, + 2, + 4, + 5 + ], + "type": "number" + }, + "Record<string,[number,number][]>": { + "type": "object", + "additionalProperties": false + }, + "Embed": { + "type": "object", + "properties": { + "title": { + "type": "string" + }, + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" + }, + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "fields": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name", + "value" + ] + } + } + }, + "additionalProperties": false + }, + "EmbedImage": { + "type": "object", + "properties": { + "url": { + "type": "string" + }, + "proxy_url": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "Partial<ChannelOverride>": { + "type": "object", + "properties": { + "message_notifications": { + "type": "integer" + }, + "mute_config": { + "$ref": "#/definitions/MuteConfig" + }, + "muted": { + "type": "boolean" + }, + "channel_id": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false + }, + "MuteConfig": { + "type": "object", + "properties": { + "end_time": { + "type": "integer" + }, + "selected_time_window": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "end_time", + "selected_time_window" + ] + }, + "CustomStatus": { + "type": "object", + "properties": { + "emoji_id": { + "type": "string" + }, + "emoji_name": { + "type": "string" + }, + "expires_at": { + "type": "integer" + }, + "text": { + "type": "string" + } + }, + "additionalProperties": false + }, + "FriendSourceFlags": { + "type": "object", + "properties": { + "all": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "all" + ] + }, + "GuildFolder": { + "type": "object", + "properties": { + "color": { + "type": "integer" + }, + "guild_ids": { + "type": "array", + "items": { + "type": "string" + } + }, + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "color", + "guild_ids", + "id", + "name" + ] + }, + "Partial<GenerateWebAuthnCredentialsSchema>": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Partial<CreateWebAuthnCredentialSchema>": { + "type": "object", + "properties": { + "credential": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ticket": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, "GuildCreateSchema": { "type": "object", "properties": { @@ -12155,20 +12748,125 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "MfaCodesSchema": { + "MessageEditSchema": { "type": "object", "properties": { - "password": { + "file": { + "type": "object", + "properties": { + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename" + ] + }, + "embed": { + "$ref": "#/definitions/Embed" + }, + "flags": { "type": "string" }, - "regenerate": { + "content": { + "type": "string" + }, + "nonce": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "tts": { "type": "boolean" + }, + "embeds": { + "type": "array", + "items": { + "$ref": "#/definitions/Embed" + } + }, + "allowed_mentions": { + "type": "object", + "properties": { + "parse": { + "type": "array", + "items": { + "type": "string" + } + }, + "roles": { + "type": "array", + "items": { + "type": "string" + } + }, + "users": { + "type": "array", + "items": { + "type": "string" + } + }, + "replied_user": { + "type": "boolean" + } + }, + "additionalProperties": false + }, + "message_reference": { + "type": "object", + "properties": { + "message_id": { + "type": "string" + }, + "channel_id": { + "type": "string" + }, + "guild_id": { + "type": "string" + }, + "fail_if_not_exists": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "channel_id", + "message_id" + ] + }, + "payload_json": { + "type": "string" + }, + "attachments": { + "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "filename": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "filename", + "id" + ] + } + }, + "sticker_ids": { + "type": "array", + "items": { + "type": "string" + } } }, "additionalProperties": false, - "required": [ - "password" - ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -12748,27 +13446,19 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "ModifyGuildStickerSchema": { + "MfaCodesSchema": { "type": "object", "properties": { - "name": { - "minLength": 2, - "maxLength": 30, - "type": "string" - }, - "description": { - "maxLength": 100, + "password": { "type": "string" }, - "tags": { - "maxLength": 200, - "type": "string" + "regenerate": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "name", - "tags" + "password" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -13349,20 +14039,27 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "PurgeSchema": { + "ModifyGuildStickerSchema": { "type": "object", "properties": { - "before": { + "name": { + "minLength": 2, + "maxLength": 30, "type": "string" }, - "after": { + "description": { + "maxLength": 100, + "type": "string" + }, + "tags": { + "maxLength": 200, "type": "string" } }, "additionalProperties": false, "required": [ - "after", - "before" + "name", + "tags" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -13943,49 +14640,20 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RegisterSchema": { + "PasswordResetSchema": { "type": "object", "properties": { - "username": { - "minLength": 2, - "maxLength": 32, - "type": "string" - }, "password": { - "minLength": 1, - "maxLength": 72, "type": "string" }, - "consent": { - "type": "boolean" - }, - "email": { - "format": "email", - "type": "string" - }, - "fingerprint": { - "type": "string" - }, - "invite": { - "type": "string" - }, - "date_of_birth": { - "type": "string" - }, - "gift_code_sku_id": { - "type": "string" - }, - "captcha_key": { + "token": { "type": "string" - }, - "promotional_email_opt_in": { - "type": "boolean" } }, "additionalProperties": false, "required": [ - "consent", - "username" + "password", + "token" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -14566,20 +15234,20 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipPostSchema": { + "PurgeSchema": { "type": "object", "properties": { - "discriminator": { + "before": { "type": "string" }, - "username": { + "after": { "type": "string" } }, "additionalProperties": false, "required": [ - "discriminator", - "username" + "after", + "before" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -15160,20 +15828,50 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RelationshipPutSchema": { + "RegisterSchema": { "type": "object", "properties": { - "type": { - "enum": [ - 1, - 2, - 3, - 4 - ], - "type": "number" + "username": { + "minLength": 2, + "maxLength": 32, + "type": "string" + }, + "password": { + "minLength": 1, + "maxLength": 72, + "type": "string" + }, + "consent": { + "type": "boolean" + }, + "email": { + "format": "email", + "type": "string" + }, + "fingerprint": { + "type": "string" + }, + "invite": { + "type": "string" + }, + "date_of_birth": { + "type": "string" + }, + "gift_code_sku_id": { + "type": "string" + }, + "captcha_key": { + "type": "string" + }, + "promotional_email_opt_in": { + "type": "boolean" } }, "additionalProperties": false, + "required": [ + "consent", + "username" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -15753,35 +16451,21 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RoleModifySchema": { + "RelationshipPostSchema": { "type": "object", "properties": { - "name": { - "type": "string" - }, - "permissions": { - "type": "string" - }, - "color": { - "type": "integer" - }, - "hoist": { - "type": "boolean" - }, - "mentionable": { - "type": "boolean" - }, - "position": { - "type": "integer" - }, - "icon": { + "discriminator": { "type": "string" }, - "unicode_emoji": { + "username": { "type": "string" } }, "additionalProperties": false, + "required": [ + "discriminator", + "username" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -16361,24 +17045,20 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "RolePositionUpdateSchema": { - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "position": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "id", - "position" - ] + "RelationshipPutSchema": { + "type": "object", + "properties": { + "type": { + "enum": [ + 1, + 2, + 3, + 4 + ], + "type": "number" + } }, + "additionalProperties": false, "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -16958,98 +17638,35 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "SelectProtocolSchema": { + "RoleModifySchema": { "type": "object", "properties": { - "protocol": { - "enum": [ - "udp", - "webrtc" - ], + "name": { "type": "string" }, - "data": { - "anyOf": [ - { - "type": "object", - "properties": { - "address": { - "type": "string" - }, - "port": { - "type": "integer" - }, - "mode": { - "type": "string" - } - }, - "additionalProperties": false, - "required": [ - "address", - "mode", - "port" - ] - }, - { - "type": "string" - } - ] - }, - "sdp": { + "permissions": { "type": "string" }, - "codecs": { - "type": "array", - "items": { - "type": "object", - "properties": { - "name": { - "enum": [ - "H264", - "VP8", - "VP9", - "opus" - ], - "type": "string" - }, - "type": { - "enum": [ - "audio", - "video" - ], - "type": "string" - }, - "priority": { - "type": "integer" - }, - "payload_type": { - "type": "integer" - }, - "rtx_payload_type": { - "type": [ - "null", - "integer" - ] - } - }, - "additionalProperties": false, - "required": [ - "name", - "payload_type", - "priority", - "type" - ] - } + "color": { + "type": "integer" }, - "rtc_connection_id": { + "hoist": { + "type": "boolean" + }, + "mentionable": { + "type": "boolean" + }, + "position": { + "type": "integer" + }, + "icon": { + "type": "string" + }, + "unicode_emoji": { "type": "string" } }, "additionalProperties": false, - "required": [ - "data", - "protocol" - ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -17629,20 +18246,24 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TemplateCreateSchema": { - "type": "object", - "properties": { - "name": { - "type": "string" + "RolePositionUpdateSchema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "position": { + "type": "integer" + } }, - "description": { - "type": "string" - } + "additionalProperties": false, + "required": [ + "id", + "position" + ] }, - "additionalProperties": false, - "required": [ - "name" - ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -18222,19 +18843,97 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TemplateModifySchema": { + "SelectProtocolSchema": { "type": "object", "properties": { - "name": { + "protocol": { + "enum": [ + "udp", + "webrtc" + ], "type": "string" }, - "description": { + "data": { + "anyOf": [ + { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "port": { + "type": "integer" + }, + "mode": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "address", + "mode", + "port" + ] + }, + { + "type": "string" + } + ] + }, + "sdp": { + "type": "string" + }, + "codecs": { + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "enum": [ + "H264", + "VP8", + "VP9", + "opus" + ], + "type": "string" + }, + "type": { + "enum": [ + "audio", + "video" + ], + "type": "string" + }, + "priority": { + "type": "integer" + }, + "payload_type": { + "type": "integer" + }, + "rtx_payload_type": { + "type": [ + "null", + "integer" + ] + } + }, + "additionalProperties": false, + "required": [ + "name", + "payload_type", + "priority", + "type" + ] + } + }, + "rtc_connection_id": { "type": "string" } }, "additionalProperties": false, "required": [ - "name" + "data", + "protocol" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -18815,16 +19514,19 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TotpDisableSchema": { + "TemplateCreateSchema": { "type": "object", "properties": { - "code": { + "name": { + "type": "string" + }, + "description": { "type": "string" } }, "additionalProperties": false, "required": [ - "code" + "name" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -19405,22 +20107,19 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TotpEnableSchema": { + "TemplateModifySchema": { "type": "object", "properties": { - "password": { - "type": "string" - }, - "code": { + "name": { "type": "string" }, - "secret": { + "description": { "type": "string" } }, "additionalProperties": false, "required": [ - "password" + "name" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -20001,32 +20700,16 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "TotpSchema": { + "TotpDisableSchema": { "type": "object", "properties": { "code": { "type": "string" - }, - "ticket": { - "type": "string" - }, - "gift_code_sku_id": { - "type": [ - "null", - "string" - ] - }, - "login_source": { - "type": [ - "null", - "string" - ] } }, "additionalProperties": false, "required": [ - "code", - "ticket" + "code" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -20607,16 +21290,22 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserDeleteSchema": { + "TotpEnableSchema": { "type": "object", "properties": { - "user_id": { + "password": { + "type": "string" + }, + "code": { + "type": "string" + }, + "secret": { "type": "string" } }, "additionalProperties": false, "required": [ - "user_id" + "password" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -21197,66 +21886,33 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserGuildSettingsSchema": { + "TotpSchema": { "type": "object", "properties": { - "channel_overrides": { - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Partial<ChannelOverride>" - } + "code": { + "type": "string" }, - "version": { - "type": "integer" + "ticket": { + "type": "string" }, - "guild_id": { + "gift_code_sku_id": { "type": [ "null", "string" ] }, - "flags": { - "type": "integer" - }, - "message_notifications": { - "type": "integer" - }, - "mobile_push": { - "type": "boolean" - }, - "mute_config": { - "anyOf": [ - { - "$ref": "#/definitions/MuteConfig" - }, - { - "type": "null" - } + "login_source": { + "type": [ + "null", + "string" ] - }, - "muted": { - "type": "boolean" - }, - "suppress_everyone": { - "type": "boolean" - }, - "suppress_roles": { - "type": "boolean" - }, - "mute_scheduled_events": { - "type": "boolean" - }, - "hide_muted_channels": { - "type": "boolean" - }, - "notify_highlights": { - "enum": [ - 0 - ], - "type": "number" } }, "additionalProperties": false, + "required": [ + "code", + "ticket" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -21836,52 +22492,17 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserModifySchema": { + "UserDeleteSchema": { "type": "object", "properties": { - "username": { - "minLength": 1, - "maxLength": 100, - "type": "string" - }, - "avatar": { - "type": [ - "null", - "string" - ] - }, - "bio": { - "maxLength": 1024, - "type": "string" - }, - "accent_color": { - "type": "integer" - }, - "banner": { - "type": [ - "null", - "string" - ] - }, - "password": { - "type": "string" - }, - "new_password": { - "type": "string" - }, - "code": { - "type": "string" - }, - "email": { - "type": "string" - }, - "discriminator": { - "minLength": 4, - "maxLength": 4, + "user_id": { "type": "string" } }, "additionalProperties": false, + "required": [ + "user_id" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -22461,39 +23082,63 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserProfileModifySchema": { + "UserGuildSettingsSchema": { "type": "object", "properties": { - "bio": { - "type": "string" + "channel_overrides": { + "type": "object", + "additionalProperties": { + "$ref": "#/definitions/Partial<ChannelOverride>" + } }, - "accent_color": { - "type": [ - "null", - "integer" - ] + "version": { + "type": "integer" }, - "banner": { + "guild_id": { "type": [ "null", "string" ] }, - "pronouns": { - "type": "string" + "flags": { + "type": "integer" }, - "theme_colors": { - "type": "array", - "items": [ + "message_notifications": { + "type": "integer" + }, + "mobile_push": { + "type": "boolean" + }, + "mute_config": { + "anyOf": [ { - "type": "integer" + "$ref": "#/definitions/MuteConfig" }, { - "type": "integer" + "type": "null" } + ] + }, + "muted": { + "type": "boolean" + }, + "suppress_everyone": { + "type": "boolean" + }, + "suppress_roles": { + "type": "boolean" + }, + "mute_scheduled_events": { + "type": "boolean" + }, + "hide_muted_channels": { + "type": "boolean" + }, + "notify_highlights": { + "enum": [ + 0 ], - "minItems": 2, - "maxItems": 2 + "type": "number" } }, "additionalProperties": false, @@ -23076,128 +23721,49 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "UserSettingsSchema": { + "UserModifySchema": { "type": "object", "properties": { - "afk_timeout": { - "type": "integer" - }, - "allow_accessibility_detection": { - "type": "boolean" - }, - "animate_emoji": { - "type": "boolean" - }, - "animate_stickers": { - "type": "integer" - }, - "contact_sync_enabled": { - "type": "boolean" - }, - "convert_emoticons": { - "type": "boolean" + "username": { + "minLength": 1, + "maxLength": 100, + "type": "string" }, - "custom_status": { - "anyOf": [ - { - "$ref": "#/definitions/CustomStatus" - }, - { - "type": "null" - } + "avatar": { + "type": [ + "null", + "string" ] }, - "default_guilds_restricted": { - "type": "boolean" - }, - "detect_platform_accounts": { - "type": "boolean" - }, - "developer_mode": { - "type": "boolean" - }, - "disable_games_tab": { - "type": "boolean" - }, - "enable_tts_command": { - "type": "boolean" + "bio": { + "maxLength": 1024, + "type": "string" }, - "explicit_content_filter": { + "accent_color": { "type": "integer" }, - "friend_source_flags": { - "$ref": "#/definitions/FriendSourceFlags" - }, - "gateway_connected": { - "type": "boolean" - }, - "gif_auto_play": { - "type": "boolean" - }, - "guild_folders": { - "type": "array", - "items": { - "$ref": "#/definitions/GuildFolder" - } - }, - "guild_positions": { - "type": "array", - "items": { - "type": "string" - } - }, - "inline_attachment_media": { - "type": "boolean" - }, - "inline_embed_media": { - "type": "boolean" + "banner": { + "type": [ + "null", + "string" + ] }, - "locale": { + "password": { "type": "string" }, - "message_display_compact": { - "type": "boolean" - }, - "native_phone_integration_enabled": { - "type": "boolean" - }, - "render_embeds": { - "type": "boolean" - }, - "render_reactions": { - "type": "boolean" - }, - "restricted_guilds": { - "type": "array", - "items": { - "type": "string" - } - }, - "show_current_game": { - "type": "boolean" - }, - "status": { - "enum": [ - "dnd", - "idle", - "invisible", - "offline", - "online" - ], + "new_password": { "type": "string" }, - "stream_notifications_enabled": { - "type": "boolean" + "code": { + "type": "string" }, - "theme": { - "enum": [ - "dark", - "light" - ], + "email": { "type": "string" }, - "timezone_offset": { - "type": "integer" + "discriminator": { + "minLength": 4, + "maxLength": 4, + "type": "string" } }, "additionalProperties": false, @@ -23780,13 +24346,39 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VanityUrlSchema": { + "UserProfileModifySchema": { "type": "object", "properties": { - "code": { - "minLength": 1, - "maxLength": 20, + "bio": { + "type": "string" + }, + "accent_color": { + "type": [ + "null", + "integer" + ] + }, + "banner": { + "type": [ + "null", + "string" + ] + }, + "pronouns": { "type": "string" + }, + "theme_colors": { + "type": "array", + "items": [ + { + "type": "integer" + }, + { + "type": "integer" + } + ], + "minItems": 2, + "maxItems": 2 } }, "additionalProperties": false, @@ -24369,55 +24961,131 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceIdentifySchema": { + "UserSettingsSchema": { "type": "object", "properties": { - "server_id": { - "type": "string" + "afk_timeout": { + "type": "integer" }, - "user_id": { - "type": "string" + "allow_accessibility_detection": { + "type": "boolean" }, - "session_id": { - "type": "string" + "animate_emoji": { + "type": "boolean" }, - "token": { + "animate_stickers": { + "type": "integer" + }, + "contact_sync_enabled": { + "type": "boolean" + }, + "convert_emoticons": { + "type": "boolean" + }, + "custom_status": { + "anyOf": [ + { + "$ref": "#/definitions/CustomStatus" + }, + { + "type": "null" + } + ] + }, + "default_guilds_restricted": { + "type": "boolean" + }, + "detect_platform_accounts": { + "type": "boolean" + }, + "developer_mode": { + "type": "boolean" + }, + "disable_games_tab": { + "type": "boolean" + }, + "enable_tts_command": { + "type": "boolean" + }, + "explicit_content_filter": { + "type": "integer" + }, + "friend_source_flags": { + "$ref": "#/definitions/FriendSourceFlags" + }, + "gateway_connected": { + "type": "boolean" + }, + "gif_auto_play": { + "type": "boolean" + }, + "guild_folders": { + "type": "array", + "items": { + "$ref": "#/definitions/GuildFolder" + } + }, + "guild_positions": { + "type": "array", + "items": { + "type": "string" + } + }, + "inline_attachment_media": { + "type": "boolean" + }, + "inline_embed_media": { + "type": "boolean" + }, + "locale": { "type": "string" }, - "video": { + "message_display_compact": { "type": "boolean" }, - "streams": { + "native_phone_integration_enabled": { + "type": "boolean" + }, + "render_embeds": { + "type": "boolean" + }, + "render_reactions": { + "type": "boolean" + }, + "restricted_guilds": { "type": "array", "items": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "rid": { - "type": "string" - }, - "quality": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "quality", - "rid", - "type" - ] + "type": "string" } + }, + "show_current_game": { + "type": "boolean" + }, + "status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], + "type": "string" + }, + "stream_notifications_enabled": { + "type": "boolean" + }, + "theme": { + "enum": [ + "dark", + "light" + ], + "type": "string" + }, + "timezone_offset": { + "type": "integer" } }, "additionalProperties": false, - "required": [ - "server_id", - "session_id", - "token", - "user_id" - ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -24997,40 +25665,16 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceStateUpdateSchema": { + "VanityUrlSchema": { "type": "object", "properties": { - "guild_id": { - "type": "string" - }, - "channel_id": { - "type": "string" - }, - "self_mute": { - "type": "boolean" - }, - "self_deaf": { - "type": "boolean" - }, - "self_video": { - "type": "boolean" - }, - "preferred_region": { + "code": { + "minLength": 1, + "maxLength": 20, "type": "string" - }, - "request_to_speak_timestamp": { - "type": "string", - "format": "date-time" - }, - "suppress": { - "type": "boolean" } }, "additionalProperties": false, - "required": [ - "self_deaf", - "self_mute" - ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ @@ -25610,85 +26254,43 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "VoiceVideoSchema": { + "VoiceIdentifySchema": { "type": "object", "properties": { - "audio_ssrc": { - "type": "integer" + "server_id": { + "type": "string" }, - "video_ssrc": { - "type": "integer" + "user_id": { + "type": "string" }, - "rtx_ssrc": { - "type": "integer" + "session_id": { + "type": "string" }, - "user_id": { + "token": { "type": "string" }, + "video": { + "type": "boolean" + }, "streams": { "type": "array", "items": { "type": "object", "properties": { "type": { - "enum": [ - "audio", - "video" - ], "type": "string" }, "rid": { "type": "string" }, - "ssrc": { - "type": "integer" - }, - "active": { - "type": "boolean" - }, "quality": { "type": "integer" - }, - "rtx_ssrc": { - "type": "integer" - }, - "max_bitrate": { - "type": "integer" - }, - "max_framerate": { - "type": "integer" - }, - "max_resolution": { - "type": "object", - "properties": { - "type": { - "type": "string" - }, - "width": { - "type": "integer" - }, - "height": { - "type": "integer" - } - }, - "additionalProperties": false, - "required": [ - "height", - "type", - "width" - ] } }, "additionalProperties": false, "required": [ - "active", - "max_bitrate", - "max_framerate", - "max_resolution", "quality", "rid", - "rtx_ssrc", - "ssrc", "type" ] } @@ -25696,8 +26298,10 @@ }, "additionalProperties": false, "required": [ - "audio_ssrc", - "video_ssrc" + "server_id", + "session_id", + "token", + "user_id" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -26278,16 +26882,39 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "GenerateWebAuthnCredentialsSchema": { + "VoiceStateUpdateSchema": { "type": "object", "properties": { - "password": { + "guild_id": { "type": "string" + }, + "channel_id": { + "type": "string" + }, + "self_mute": { + "type": "boolean" + }, + "self_deaf": { + "type": "boolean" + }, + "self_video": { + "type": "boolean" + }, + "preferred_region": { + "type": "string" + }, + "request_to_speak_timestamp": { + "type": "string", + "format": "date-time" + }, + "suppress": { + "type": "boolean" } }, "additionalProperties": false, "required": [ - "password" + "self_deaf", + "self_mute" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -26868,24 +27495,94 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "CreateWebAuthnCredentialSchema": { + "VoiceVideoSchema": { "type": "object", "properties": { - "credential": { - "type": "string" + "audio_ssrc": { + "type": "integer" }, - "name": { - "type": "string" + "video_ssrc": { + "type": "integer" }, - "ticket": { + "rtx_ssrc": { + "type": "integer" + }, + "user_id": { "type": "string" + }, + "streams": { + "type": "array", + "items": { + "type": "object", + "properties": { + "type": { + "enum": [ + "audio", + "video" + ], + "type": "string" + }, + "rid": { + "type": "string" + }, + "ssrc": { + "type": "integer" + }, + "active": { + "type": "boolean" + }, + "quality": { + "type": "integer" + }, + "rtx_ssrc": { + "type": "integer" + }, + "max_bitrate": { + "type": "integer" + }, + "max_framerate": { + "type": "integer" + }, + "max_resolution": { + "type": "object", + "properties": { + "type": { + "type": "string" + }, + "width": { + "type": "integer" + }, + "height": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "height", + "type", + "width" + ] + } + }, + "additionalProperties": false, + "required": [ + "active", + "max_bitrate", + "max_framerate", + "max_resolution", + "quality", + "rid", + "rtx_ssrc", + "ssrc", + "type" + ] + } } }, "additionalProperties": false, "required": [ - "credential", - "name", - "ticket" + "audio_ssrc", + "video_ssrc" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -27466,14 +28163,16 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebAuthnPostSchema": { - "anyOf": [ - { - "$ref": "#/definitions/Partial<GenerateWebAuthnCredentialsSchema>" - }, - { - "$ref": "#/definitions/Partial<CreateWebAuthnCredentialSchema>" + "GenerateWebAuthnCredentialsSchema": { + "type": "object", + "properties": { + "password": { + "type": "string" } + }, + "additionalProperties": false, + "required": [ + "password" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -28054,10 +28753,13 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebAuthnTotpSchema": { + "CreateWebAuthnCredentialSchema": { "type": "object", "properties": { - "code": { + "credential": { + "type": "string" + }, + "name": { "type": "string" }, "ticket": { @@ -28066,7 +28768,8 @@ }, "additionalProperties": false, "required": [ - "code", + "credential", + "name", "ticket" ], "definitions": { @@ -28648,20 +29351,14 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "WebhookCreateSchema": { - "type": "object", - "properties": { - "name": { - "maxLength": 80, - "type": "string" + "WebAuthnPostSchema": { + "anyOf": [ + { + "$ref": "#/definitions/Partial<GenerateWebAuthnCredentialsSchema>" }, - "avatar": { - "type": "string" + { + "$ref": "#/definitions/Partial<CreateWebAuthnCredentialSchema>" } - }, - "additionalProperties": false, - "required": [ - "name" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -29242,20 +29939,20 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "WidgetModifySchema": { + "WebAuthnTotpSchema": { "type": "object", "properties": { - "enabled": { - "type": "boolean" + "code": { + "type": "string" }, - "channel_id": { + "ticket": { "type": "string" } }, "additionalProperties": false, "required": [ - "channel_id", - "enabled" + "code", + "ticket" ], "definitions": { "ChannelPermissionOverwriteType": { @@ -29836,125 +30533,615 @@ }, "$schema": "http://json-schema.org/draft-07/schema#" }, - "MessageEditSchema": { + "WebhookCreateSchema": { "type": "object", "properties": { - "file": { + "name": { + "maxLength": 80, + "type": "string" + }, + "avatar": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "name" + ], + "definitions": { + "ChannelPermissionOverwriteType": { + "enum": [ + 0, + 1, + 2 + ], + "type": "number" + }, + "ChannelModifySchema": { "type": "object", "properties": { - "filename": { + "name": { + "maxLength": 100, + "type": "string" + }, + "type": { + "enum": [ + 0, + 1, + 10, + 11, + 12, + 13, + 14, + 15, + 2, + 255, + 3, + 33, + 34, + 35, + 4, + 5, + 6, + 64, + 7, + 8, + 9 + ], + "type": "number" + }, + "topic": { "type": "string" + }, + "icon": { + "type": [ + "null", + "string" + ] + }, + "bitrate": { + "type": "integer" + }, + "user_limit": { + "type": "integer" + }, + "rate_limit_per_user": { + "type": "integer" + }, + "position": { + "type": "integer" + }, + "permission_overwrites": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ChannelPermissionOverwriteType" + }, + "allow": { + "type": "string" + }, + "deny": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "allow", + "deny", + "id", + "type" + ] + } + }, + "parent_id": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nsfw": { + "type": "boolean" + }, + "rtc_region": { + "type": "string" + }, + "default_auto_archive_duration": { + "type": "integer" + }, + "default_reaction_emoji": { + "type": [ + "null", + "string" + ] + }, + "flags": { + "type": "integer" + }, + "default_thread_rate_limit_per_user": { + "type": "integer" + }, + "video_quality_mode": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "ActivitySchema": { + "type": "object", + "properties": { + "afk": { + "type": "boolean" + }, + "status": { + "$ref": "#/definitions/Status" + }, + "activities": { + "type": "array", + "items": { + "$ref": "#/definitions/Activity" + } + }, + "since": { + "type": "integer" } }, "additionalProperties": false, "required": [ - "filename" + "status" ] }, - "embed": { - "$ref": "#/definitions/Embed" - }, - "flags": { - "type": "string" - }, - "content": { - "type": "string" - }, - "nonce": { + "Status": { + "enum": [ + "dnd", + "idle", + "invisible", + "offline", + "online" + ], "type": "string" }, - "channel_id": { - "type": "string" + "Activity": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/ActivityType" + }, + "url": { + "type": "string" + }, + "created_at": { + "type": "integer" + }, + "timestamps": { + "type": "object", + "properties": { + "start": { + "type": "integer" + }, + "end": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "end", + "start" + ] + }, + "application_id": { + "type": "string" + }, + "details": { + "type": "string" + }, + "state": { + "type": "string" + }, + "emoji": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "id": { + "type": "string" + }, + "animated": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "animated", + "name" + ] + }, + "party": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "size": { + "type": "array", + "items": [ + { + "type": "integer" + } + ], + "minItems": 1, + "maxItems": 1 + } + }, + "additionalProperties": false + }, + "assets": { + "type": "object", + "properties": { + "large_image": { + "type": "string" + }, + "large_text": { + "type": "string" + }, + "small_image": { + "type": "string" + }, + "small_text": { + "type": "string" + } + }, + "additionalProperties": false + }, + "secrets": { + "type": "object", + "properties": { + "join": { + "type": "string" + }, + "spectate": { + "type": "string" + }, + "match": { + "type": "string" + } + }, + "additionalProperties": false + }, + "instance": { + "type": "boolean" + }, + "flags": { + "type": "string" + }, + "id": { + "type": "string" + }, + "sync_id": { + "type": "string" + }, + "metadata": { + "type": "object", + "properties": { + "context_uri": { + "type": "string" + }, + "album_id": { + "type": "string" + }, + "artist_ids": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "album_id", + "artist_ids" + ] + }, + "session_id": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "flags", + "name", + "session_id", + "type" + ] }, - "tts": { - "type": "boolean" + "ActivityType": { + "enum": [ + 0, + 1, + 2, + 4, + 5 + ], + "type": "number" }, - "embeds": { - "type": "array", - "items": { - "$ref": "#/definitions/Embed" - } + "Record<string,[number,number][]>": { + "type": "object", + "additionalProperties": false }, - "allowed_mentions": { + "Embed": { "type": "object", "properties": { - "parse": { - "type": "array", - "items": { - "type": "string" - } + "title": { + "type": "string" }, - "roles": { - "type": "array", - "items": { - "type": "string" - } + "type": { + "enum": [ + "article", + "gifv", + "image", + "link", + "rich", + "video" + ], + "type": "string" }, - "users": { + "description": { + "type": "string" + }, + "url": { + "type": "string" + }, + "timestamp": { + "type": "string", + "format": "date-time" + }, + "color": { + "type": "integer" + }, + "footer": { + "type": "object", + "properties": { + "text": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "text" + ] + }, + "image": { + "$ref": "#/definitions/EmbedImage" + }, + "thumbnail": { + "$ref": "#/definitions/EmbedImage" + }, + "video": { + "$ref": "#/definitions/EmbedImage" + }, + "provider": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "author": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "url": { + "type": "string" + }, + "icon_url": { + "type": "string" + }, + "proxy_icon_url": { + "type": "string" + } + }, + "additionalProperties": false + }, + "fields": { "type": "array", "items": { - "type": "string" + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "value": { + "type": "string" + }, + "inline": { + "type": "boolean" + } + }, + "additionalProperties": false, + "required": [ + "name", + "value" + ] } - }, - "replied_user": { - "type": "boolean" } }, "additionalProperties": false }, - "message_reference": { + "EmbedImage": { "type": "object", "properties": { - "message_id": { + "url": { "type": "string" }, + "proxy_url": { + "type": "string" + }, + "height": { + "type": "integer" + }, + "width": { + "type": "integer" + } + }, + "additionalProperties": false + }, + "Partial<ChannelOverride>": { + "type": "object", + "properties": { + "message_notifications": { + "type": "integer" + }, + "mute_config": { + "$ref": "#/definitions/MuteConfig" + }, + "muted": { + "type": "boolean" + }, "channel_id": { + "type": [ + "null", + "string" + ] + } + }, + "additionalProperties": false + }, + "MuteConfig": { + "type": "object", + "properties": { + "end_time": { + "type": "integer" + }, + "selected_time_window": { + "type": "integer" + } + }, + "additionalProperties": false, + "required": [ + "end_time", + "selected_time_window" + ] + }, + "CustomStatus": { + "type": "object", + "properties": { + "emoji_id": { "type": "string" }, - "guild_id": { + "emoji_name": { "type": "string" }, - "fail_if_not_exists": { + "expires_at": { + "type": "integer" + }, + "text": { + "type": "string" + } + }, + "additionalProperties": false + }, + "FriendSourceFlags": { + "type": "object", + "properties": { + "all": { "type": "boolean" } }, "additionalProperties": false, "required": [ - "channel_id", - "message_id" + "all" ] }, - "payload_json": { - "type": "string" - }, - "attachments": { - "description": "TODO: we should create an interface for attachments\nTODO: OpenWAAO<-->attachment-style metadata conversion", - "type": "array", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string" - }, - "filename": { + "GuildFolder": { + "type": "object", + "properties": { + "color": { + "type": "integer" + }, + "guild_ids": { + "type": "array", + "items": { "type": "string" } }, - "additionalProperties": false, - "required": [ - "filename", - "id" - ] - } + "id": { + "type": "integer" + }, + "name": { + "type": "string" + } + }, + "additionalProperties": false, + "required": [ + "color", + "guild_ids", + "id", + "name" + ] }, - "sticker_ids": { - "type": "array", - "items": { - "type": "string" - } + "Partial<GenerateWebAuthnCredentialsSchema>": { + "type": "object", + "properties": { + "password": { + "type": "string" + } + }, + "additionalProperties": false + }, + "Partial<CreateWebAuthnCredentialSchema>": { + "type": "object", + "properties": { + "credential": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ticket": { + "type": "string" + } + }, + "additionalProperties": false + } + }, + "$schema": "http://json-schema.org/draft-07/schema#" + }, + "WidgetModifySchema": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "channel_id": { + "type": "string" } }, "additionalProperties": false, + "required": [ + "channel_id", + "enabled" + ], "definitions": { "ChannelPermissionOverwriteType": { "enum": [ diff --git a/src/api/middlewares/Authentication.ts b/src/api/middlewares/Authentication.ts index f4c33963..771f0de8 100644 --- a/src/api/middlewares/Authentication.ts +++ b/src/api/middlewares/Authentication.ts @@ -29,6 +29,8 @@ export const NO_AUTHORIZATION_ROUTES = [ "/auth/mfa/totp", "/auth/mfa/webauthn", "/auth/verify", + "/auth/forgot", + "/auth/reset", // Routes with a seperate auth system "/webhooks/", // Public information endpoints diff --git a/src/api/routes/auth/forgot.ts b/src/api/routes/auth/forgot.ts new file mode 100644 index 00000000..faa43dbb --- /dev/null +++ b/src/api/routes/auth/forgot.ts @@ -0,0 +1,92 @@ +import { getIpAdress, route, verifyCaptcha } from "@fosscord/api"; +import { + Config, + Email, + FieldErrors, + ForgotPasswordSchema, + User, +} from "@fosscord/util"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; +const router = Router(); + +router.post( + "/", + route({ body: "ForgotPasswordSchema" }), + async (req: Request, res: Response) => { + const { login, captcha_key } = req.body as ForgotPasswordSchema; + + const config = Config.get(); + + if ( + config.password_reset.requireCaptcha && + config.security.captcha.enabled + ) { + const { sitekey, service } = config.security.captcha; + if (!captcha_key) { + return res.status(400).json({ + captcha_key: ["captcha-required"], + captcha_sitekey: sitekey, + captcha_service: service, + }); + } + + const ip = getIpAdress(req); + const verify = await verifyCaptcha(captcha_key, ip); + if (!verify.success) { + return res.status(400).json({ + captcha_key: verify["error-codes"], + captcha_sitekey: sitekey, + captcha_service: service, + }); + } + } + + const user = await User.findOneOrFail({ + where: [{ phone: login }, { email: login }], + select: ["username", "id", "disabled", "deleted", "email"], + relations: ["security_keys"], + }).catch(() => { + throw FieldErrors({ + login: { + message: req.t("auth:password_reset.EMAIL_DOES_NOT_EXIST"), + code: "EMAIL_DOES_NOT_EXIST", + }, + }); + }); + + if (!user.email) + throw FieldErrors({ + login: { + message: + "This account does not have an email address associated with it.", + code: "NO_EMAIL", + }, + }); + + if (user.deleted) + return res.status(400).json({ + message: "This account is scheduled for deletion.", + code: 20011, + }); + + if (user.disabled) + return res.status(400).json({ + message: req.t("auth:login.ACCOUNT_DISABLED"), + code: 20013, + }); + + return await Email.sendResetPassword(user, user.email) + .then(() => { + return res.sendStatus(204); + }) + .catch((e) => { + console.error( + `Failed to send password reset email to ${user.username}#${user.discriminator}: ${e}`, + ); + throw new HTTPError("Failed to send password reset email", 500); + }); + }, +); + +export default router; diff --git a/src/api/routes/auth/reset.ts b/src/api/routes/auth/reset.ts new file mode 100644 index 00000000..94053e1a --- /dev/null +++ b/src/api/routes/auth/reset.ts @@ -0,0 +1,57 @@ +import { route } from "@fosscord/api"; +import { + checkToken, + Config, + Email, + FieldErrors, + generateToken, + PasswordResetSchema, + User, +} from "@fosscord/util"; +import bcrypt from "bcrypt"; +import { Request, Response, Router } from "express"; +import { HTTPError } from "lambert-server"; + +const router = Router(); + +router.post( + "/", + route({ body: "PasswordResetSchema" }), + async (req: Request, res: Response) => { + const { password, token } = req.body as PasswordResetSchema; + + try { + const { jwtSecret } = Config.get().security; + const { user } = await checkToken(token, jwtSecret, true); + + // the salt is saved in the password refer to bcrypt docs + const hash = await bcrypt.hash(password, 12); + + const data = { + data: { + hash, + valid_tokens_since: new Date(), + }, + }; + await User.update({ id: user.id }, data); + + // come on, the user has to have an email to reset their password in the first place + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + await Email.sendPasswordChanged(user, user.email!); + + res.json({ token: await generateToken(user.id) }); + } catch (e) { + if ((e as Error).toString() === "Invalid Token") + throw FieldErrors({ + password: { + message: req.t("auth:password_reset.INVALID_TOKEN"), + code: "INVALID_TOKEN", + }, + }); + + throw new HTTPError((e as Error).toString(), 400); + } + }, +); + +export default router; diff --git a/src/api/routes/auth/verify/resend.ts b/src/api/routes/auth/verify/resend.ts index 1cd14f23..918af9a1 100644 --- a/src/api/routes/auth/verify/resend.ts +++ b/src/api/routes/auth/verify/resend.ts @@ -36,7 +36,7 @@ router.post( throw new HTTPError("User does not have an email address", 400); } - await Email.sendVerificationEmail(user, user.email) + await Email.sendVerifyEmail(user, user.email) .then(() => { return res.sendStatus(204); }) diff --git a/src/util/config/Config.ts b/src/util/config/Config.ts index d6f804bf..c056d454 100644 --- a/src/util/config/Config.ts +++ b/src/util/config/Config.ts @@ -31,6 +31,7 @@ import { LimitsConfiguration, LoginConfiguration, MetricsConfiguration, + PasswordResetConfiguration, RabbitMQConfiguration, RegionConfiguration, RegisterConfiguration, @@ -60,4 +61,6 @@ export class ConfigValue { defaults: DefaultsConfiguration = new DefaultsConfiguration(); external: ExternalTokensConfiguration = new ExternalTokensConfiguration(); email: EmailConfiguration = new EmailConfiguration(); + password_reset: PasswordResetConfiguration = + new PasswordResetConfiguration(); } diff --git a/src/util/config/types/PasswordResetConfiguration.ts b/src/util/config/types/PasswordResetConfiguration.ts new file mode 100644 index 00000000..806d77be --- /dev/null +++ b/src/util/config/types/PasswordResetConfiguration.ts @@ -0,0 +1,21 @@ +/* + Fosscord: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Fosscord and Fosscord Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +export class PasswordResetConfiguration { + requireCaptcha: boolean = false; +} diff --git a/src/util/config/types/index.ts b/src/util/config/types/index.ts index 1431c128..510e19f8 100644 --- a/src/util/config/types/index.ts +++ b/src/util/config/types/index.ts @@ -30,6 +30,7 @@ export * from "./KafkaConfiguration"; export * from "./LimitConfigurations"; export * from "./LoginConfiguration"; export * from "./MetricsConfiguration"; +export * from "./PasswordResetConfiguration"; export * from "./RabbitMQConfiguration"; export * from "./RegionConfiguration"; export * from "./RegisterConfiguration"; diff --git a/src/util/entities/User.ts b/src/util/entities/User.ts index 2947b205..f99a85e7 100644 --- a/src/util/entities/User.ts +++ b/src/util/entities/User.ts @@ -393,7 +393,7 @@ export class User extends BaseClass { // send verification email if users aren't verified by default and we have an email if (!Config.get().defaults.user.verified && email) { - await Email.sendVerificationEmail(user, email).catch((e) => { + await Email.sendVerifyEmail(user, email).catch((e) => { console.error( `Failed to send verification email to ${user.username}#${user.discriminator}: ${e}`, ); diff --git a/src/util/schemas/ForgotPasswordSchema.ts b/src/util/schemas/ForgotPasswordSchema.ts new file mode 100644 index 00000000..9a28bd18 --- /dev/null +++ b/src/util/schemas/ForgotPasswordSchema.ts @@ -0,0 +1,22 @@ +/* + Fosscord: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Fosscord and Fosscord Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +export interface ForgotPasswordSchema { + login: string; + captcha_key?: string; +} diff --git a/src/util/schemas/PasswordResetSchema.ts b/src/util/schemas/PasswordResetSchema.ts new file mode 100644 index 00000000..9cc74940 --- /dev/null +++ b/src/util/schemas/PasswordResetSchema.ts @@ -0,0 +1,22 @@ +/* + Fosscord: A FOSS re-implementation and extension of the Discord.com backend. + Copyright (C) 2023 Fosscord and Fosscord Contributors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <https://www.gnu.org/licenses/>. +*/ + +export interface PasswordResetSchema { + password: string; + token: string; +} diff --git a/src/util/schemas/index.ts b/src/util/schemas/index.ts index 194d8571..44909a3a 100644 --- a/src/util/schemas/index.ts +++ b/src/util/schemas/index.ts @@ -16,6 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>. */ +export * from "./AckBulkSchema"; export * from "./ActivitySchema"; export * from "./ApplicationAuthorizeSchema"; export * from "./ApplicationCreateSchema"; @@ -32,6 +33,7 @@ export * from "./CodesVerificationSchema"; export * from "./DmChannelCreateSchema"; export * from "./EmojiCreateSchema"; export * from "./EmojiModifySchema"; +export * from "./ForgotPasswordSchema"; export * from "./GatewayPayloadSchema"; export * from "./GuildCreateSchema"; export * from "./GuildTemplateCreateSchema"; @@ -45,8 +47,10 @@ export * from "./MemberChangeProfileSchema"; export * from "./MemberChangeSchema"; export * from "./MessageAcknowledgeSchema"; export * from "./MessageCreateSchema"; +export * from "./MessageEditSchema"; export * from "./MfaCodesSchema"; export * from "./ModifyGuildStickerSchema"; +export * from "./PasswordResetSchema"; export * from "./PurgeSchema"; export * from "./RegisterSchema"; export * from "./RelationshipPostSchema"; @@ -69,22 +73,6 @@ export * from "./VanityUrlSchema"; export * from "./VoiceIdentifySchema"; export * from "./VoiceStateUpdateSchema"; export * from "./VoiceVideoSchema"; -export * from "./IdentifySchema"; -export * from "./ActivitySchema"; -export * from "./LazyRequestSchema"; -export * from "./GuildUpdateSchema"; -export * from "./ChannelPermissionOverwriteSchema"; -export * from "./UserGuildSettingsSchema"; -export * from "./GatewayPayloadSchema"; -export * from "./RolePositionUpdateSchema"; -export * from "./ChannelReorderSchema"; -export * from "./UserSettingsSchema"; -export * from "./BotModifySchema"; -export * from "./ApplicationModifySchema"; -export * from "./ApplicationCreateSchema"; -export * from "./ApplicationAuthorizeSchema"; -export * from "./AckBulkSchema"; export * from "./WebAuthnSchema"; export * from "./WebhookCreateSchema"; export * from "./WidgetModifySchema"; -export * from "./MessageEditSchema"; diff --git a/src/util/util/Email.ts b/src/util/util/Email.ts index 3028b063..fa72d9c0 100644 --- a/src/util/util/Email.ts +++ b/src/util/util/Email.ts @@ -194,8 +194,14 @@ const transporters = { export const Email: { transporter: Transporter | null; init: () => Promise<void>; - generateVerificationLink: (id: string, email: string) => Promise<string>; - sendVerificationEmail: ( + generateLink: ( + type: "verify" | "reset", + id: string, + email: string, + ) => Promise<string>; + sendVerifyEmail: (user: User, email: string) => Promise<SentMessageInfo>; + sendResetPassword: (user: User, email: string) => Promise<SentMessageInfo>; + sendPasswordChanged: ( user: User, email: string, ) => Promise<SentMessageInfo>; @@ -231,10 +237,10 @@ export const Email: { * Replaces all placeholders in an email template with the correct values */ doReplacements: function ( - template: string, - user: User, - emailVerificationUrl?: string, - passwordResetUrl?: string, + template, + user, + emailVerificationUrl?, + passwordResetUrl?, ipInfo?: { ip: string; city: string; @@ -285,23 +291,22 @@ export const Email: { * * @param id user id * @param email user email - * @returns a verification link for the user */ - generateVerificationLink: async function (id: string, email: string) { + generateLink: async function (type, id, email) { const token = (await generateToken(id, email)) as string; const instanceUrl = Config.get().general.frontPage || "http://localhost:3001"; - const link = `${instanceUrl}/verify#token=${token}`; + const link = `${instanceUrl}/${type}#token=${token}`; return link; }, - sendVerificationEmail: async function (user: User, email: string) { + /** + * Sends an email to the user with a link to verify their email address + */ + sendVerifyEmail: async function (user, email) { if (!this.transporter) return; // generate a verification link for the user - const verificationLink = await this.generateVerificationLink( - user.id, - email, - ); + const link = await this.generateLink("verify", user.id, email); // load the email template const rawTemplate = fs.readFileSync( @@ -314,7 +319,78 @@ export const Email: { ); // replace email template placeholders - const html = this.doReplacements(rawTemplate, user, verificationLink); + const html = this.doReplacements(rawTemplate, user, link); + + // extract the title from the email template to use as the email subject + const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; + + // construct the email + const message = { + from: + Config.get().general.correspondenceEmail || "noreply@localhost", + to: email, + subject, + html, + }; + + // send the email + return this.transporter.sendMail(message); + }, + /** + * Sends an email to the user with a link to reset their password + */ + sendResetPassword: async function (user, email) { + if (!this.transporter) return; + + // generate a password reset link for the user + const link = await this.generateLink("reset", user.id, email); + + // load the email template + const rawTemplate = fs.readFileSync( + path.join( + ASSET_FOLDER_PATH, + "email_templates", + "password_reset_request.html", + ), + { encoding: "utf-8" }, + ); + + // replace email template placeholders + const html = this.doReplacements(rawTemplate, user, undefined, link); + + // extract the title from the email template to use as the email subject + const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; + + // construct the email + const message = { + from: + Config.get().general.correspondenceEmail || "noreply@localhost", + to: email, + subject, + html, + }; + + // send the email + return this.transporter.sendMail(message); + }, + /** + * Sends an email to the user notifying them that their password has been changed + */ + sendPasswordChanged: async function (user, email) { + if (!this.transporter) return; + + // load the email template + const rawTemplate = fs.readFileSync( + path.join( + ASSET_FOLDER_PATH, + "email_templates", + "password_changed.html", + ), + { encoding: "utf-8" }, + ); + + // replace email template placeholders + const html = this.doReplacements(rawTemplate, user); // extract the title from the email template to use as the email subject const subject = html.match(/<title>(.*)<\/title>/)?.[1] || ""; diff --git a/src/util/util/Token.ts b/src/util/util/Token.ts index e7b2006d..ffc442aa 100644 --- a/src/util/util/Token.ts +++ b/src/util/util/Token.ts @@ -38,6 +38,15 @@ async function checkEmailToken( where: { email: decoded.email, }, + select: [ + "email", + "id", + "verified", + "deleted", + "disabled", + "username", + "data", + ], }); if (!user) return rej("Invalid Token"); |