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