verify.ts 3.3 KB

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