verify_user_jwt.cpp 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. // SPDX-FileCopyrightText: Copyright 2018 Citra Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #if defined(__GNUC__) || defined(__clang__)
  4. #pragma GCC diagnostic push
  5. #pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
  6. #endif
  7. #include <jwt/jwt.hpp>
  8. #if defined(__GNUC__) || defined(__clang__)
  9. #pragma GCC diagnostic pop
  10. #endif
  11. #include <system_error>
  12. #include "common/logging/log.h"
  13. #include "web_service/verify_user_jwt.h"
  14. #include "web_service/web_backend.h"
  15. #include "web_service/web_result.h"
  16. namespace WebService {
  17. static std::string public_key;
  18. std::string GetPublicKey(const std::string& host) {
  19. if (public_key.empty()) {
  20. Client client(host, "", ""); // no need for credentials here
  21. public_key = client.GetPlain("/jwt/external/key.pem", true).returned_data;
  22. if (public_key.empty()) {
  23. LOG_ERROR(WebService, "Could not fetch external JWT public key, verification may fail");
  24. } else {
  25. LOG_INFO(WebService, "Fetched external JWT public key (size={})", public_key.size());
  26. }
  27. }
  28. return public_key;
  29. }
  30. VerifyUserJWT::VerifyUserJWT(const std::string& host) : pub_key(GetPublicKey(host)) {}
  31. Network::VerifyUser::UserData VerifyUserJWT::LoadUserData(const std::string& verify_uid,
  32. const std::string& token) {
  33. const std::string audience = fmt::format("external-{}", verify_uid);
  34. using namespace jwt::params;
  35. std::error_code error;
  36. // We use the Citra backend so the issuer is citra-core
  37. auto decoded =
  38. jwt::decode(token, algorithms({"rs256"}), error, secret(pub_key), issuer("citra-core"),
  39. aud(audience), validate_iat(true), validate_jti(true));
  40. if (error) {
  41. LOG_INFO(WebService, "Verification failed: category={}, code={}, message={}",
  42. error.category().name(), error.value(), error.message());
  43. return {};
  44. }
  45. Network::VerifyUser::UserData user_data{};
  46. if (decoded.payload().has_claim("username")) {
  47. user_data.username = decoded.payload().get_claim_value<std::string>("username");
  48. }
  49. if (decoded.payload().has_claim("displayName")) {
  50. user_data.display_name = decoded.payload().get_claim_value<std::string>("displayName");
  51. }
  52. if (decoded.payload().has_claim("avatarUrl")) {
  53. user_data.avatar_url = decoded.payload().get_claim_value<std::string>("avatarUrl");
  54. }
  55. if (decoded.payload().has_claim("roles")) {
  56. auto roles = decoded.payload().get_claim_value<std::vector<std::string>>("roles");
  57. user_data.moderator = std::find(roles.begin(), roles.end(), "moderator") != roles.end();
  58. }
  59. return user_data;
  60. }
  61. } // namespace WebService