verify.ts 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { Request as ExpressRequest, Response as ExpressResponse } from "express";
  2. import { Option, tryDo } from "@shared/common/async_utils";
  3. import { rpcClient } from "src/utils/rpc";
  4. import got from "got";
  5. import { logger } from "src/utils/logging";
  6. import { ENV } from "src/utils/environment";
  7. export interface VerifyInfo {
  8. captchaSitekey: string;
  9. userVerified: boolean;
  10. needsCaptcha: boolean;
  11. }
  12. export interface VerifyRequest {
  13. captchaResponse: string;
  14. }
  15. interface HCaptchaResponse {
  16. success: boolean;
  17. "error-codes"?: string[];
  18. }
  19. const VERIFY_URL = "https://hcaptcha.com/siteverify";
  20. type GetResult = Promise<ExpressResponse<VerifyInfo>>;
  21. export const get = async (req: ExpressRequest, res: ExpressResponse): GetResult => {
  22. if (!req.session?.userId) {
  23. return res.json({
  24. captchaSitekey: ENV.HCAPTCHA_SITEKEY,
  25. userVerified: true,
  26. });
  27. }
  28. const verifiedResult = await tryDo(rpcClient.userVerified({ userId: req.session.userId }));
  29. let verified = true;
  30. if (!verifiedResult.ok) {
  31. logger.error("Failed to check verified state: %s", verifiedResult.error);
  32. } else {
  33. verified = verifiedResult.result.verified;
  34. logger.verbose("User %s (%s) verified: %s", req.session.username, req.session.userId, verified);
  35. }
  36. return res.json({
  37. captchaSitekey: ENV.HCAPTCHA_SITEKEY,
  38. userVerified: verified,
  39. needsCaptcha: ENV.HCAPTCHA_ENABLED === "TRUE",
  40. });
  41. };
  42. type PostResult = Promise<ExpressResponse<Option<unknown, { error: string }>>>;
  43. export const post = async (req: ExpressRequest, res: ExpressResponse): PostResult => {
  44. const needVerify = ENV.HCAPTCHA_ENABLED === "TRUE";
  45. if (!req.session?.userId) {
  46. return res.json({
  47. ok: false,
  48. error: "Not logged in, please log in",
  49. });
  50. }
  51. if (needVerify) {
  52. const hasToken = (body: unknown):
  53. body is VerifyRequest => body instanceof Object
  54. && (body as VerifyRequest).captchaResponse !== undefined;
  55. if (needVerify && !hasToken(req.body)) {
  56. return res.json({
  57. ok: false,
  58. error: "No user token provided, please try again",
  59. });
  60. }
  61. const response = await tryDo(got<HCaptchaResponse>(VERIFY_URL, {
  62. method: "post",
  63. responseType: "json",
  64. form: {
  65. secret: ENV.HCAPTCHA_SECRET,
  66. response: req.body.captchaResponse,
  67. },
  68. }));
  69. if (!response.ok) {
  70. logger.error("Failed to hCaptcha user %s: %s", req.session.userId, response.error);
  71. return res.json({
  72. ok: false,
  73. error: "Failed to verify hCaptcha response. Please try again.",
  74. });
  75. }
  76. const captchaResponse = response.result.body;
  77. if (!captchaResponse.success) {
  78. const errors = captchaResponse["error-codes"] ?? [];
  79. logger.error("Failed hCaptcha verify on user %s. Got errors: %s", req.session.userId, errors.join(";"));
  80. return res.json({
  81. ok: false,
  82. error: "Failed to verify hCaptcha response. Please try again.",
  83. });
  84. }
  85. }
  86. const verifyResponse = await rpcClient.verifyUser({ userId: req.session.userId });
  87. if (!verifyResponse.ok) {
  88. logger.error("Failed to verify user %s (%s)", req.session.userId, req.session.username);
  89. return res.json({
  90. ok: false,
  91. error: "Failed to verify your account. Please try again or contact server owners.",
  92. });
  93. }
  94. return res.json({
  95. ok: true,
  96. });
  97. };