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