|
15 | 15 |
|
16 | 16 | #include <chrono> |
17 | 17 | #include <cstdint> |
18 | | -#include <nlohmann/json.hpp> |
19 | | -#include <optional> |
| 18 | +#include <exception> |
| 19 | +#include <livekit_uniffi.hpp> |
20 | 20 | #include <string> |
21 | | -#include <vector> |
22 | 21 |
|
23 | 22 | #include "token_source_internal.h" |
24 | 23 |
|
25 | 24 | namespace livekit { |
26 | | -namespace { |
27 | | - |
28 | | -std::optional<std::vector<std::uint8_t>> base64UrlDecode(const std::string& input) { |
29 | | - std::string normalized; |
30 | | - normalized.reserve(input.size()); |
31 | | - for (const char ch : input) { |
32 | | - if (ch == '-') { |
33 | | - normalized += '+'; |
34 | | - } else if (ch == '_') { |
35 | | - normalized += '/'; |
36 | | - } else { |
37 | | - normalized += ch; |
38 | | - } |
39 | | - } |
40 | | - |
41 | | - while (normalized.size() % 4 != 0) { |
42 | | - normalized += '='; |
43 | | - } |
44 | | - |
45 | | - static const int kDecodeTable[256] = { |
46 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
47 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, |
48 | | - 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, |
49 | | - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, |
50 | | - 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
51 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
52 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
53 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, |
54 | | - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; |
55 | | - |
56 | | - std::vector<std::uint8_t> output; |
57 | | - output.reserve(normalized.size() * 3 / 4); |
58 | | - |
59 | | - std::uint32_t buffer = 0; |
60 | | - int bits = 0; |
61 | | - for (const unsigned char ch : normalized) { |
62 | | - if (ch == '=') { |
63 | | - break; |
64 | | - } |
65 | | - const int value = kDecodeTable[ch]; |
66 | | - if (value < 0) { |
67 | | - return std::nullopt; |
68 | | - } |
69 | | - buffer = (buffer << 6) | static_cast<std::uint32_t>(value); |
70 | | - bits += 6; |
71 | | - if (bits >= 8) { |
72 | | - bits -= 8; |
73 | | - output.push_back(static_cast<std::uint8_t>((buffer >> bits) & 0xFF)); |
74 | | - } |
75 | | - } |
76 | | - |
77 | | - return output; |
78 | | -} |
79 | | - |
80 | | -std::optional<std::string> extractJwtPayloadJson(const std::string& token) { |
81 | | - const std::size_t first_dot = token.find('.'); |
82 | | - if (first_dot == std::string::npos) { |
83 | | - return std::nullopt; |
84 | | - } |
85 | | - const std::size_t second_dot = token.find('.', first_dot + 1); |
86 | | - if (second_dot == std::string::npos) { |
87 | | - return std::nullopt; |
88 | | - } |
89 | | - |
90 | | - const std::string payload_segment = token.substr(first_dot + 1, second_dot - first_dot - 1); |
91 | | - const auto decoded = base64UrlDecode(payload_segment); |
92 | | - if (!decoded.has_value() || decoded->empty()) { |
93 | | - return std::nullopt; |
94 | | - } |
95 | | - |
96 | | - return std::string(decoded->begin(), decoded->end()); |
97 | | -} |
98 | | - |
99 | | -// Read an integer-valued JWT claim (e.g. "nbf"/"exp"). JWT numeric date claims |
100 | | -// are seconds since the epoch; non-integer or absent claims return nullopt. |
101 | | -std::optional<std::int64_t> readNumericClaim(const nlohmann::json& payload, const char* key) { |
102 | | - const auto it = payload.find(key); |
103 | | - if (it == payload.end() || !it->is_number()) { |
104 | | - return std::nullopt; |
105 | | - } |
106 | | - return it->get<std::int64_t>(); |
107 | | -} |
108 | | - |
109 | | -} // namespace |
110 | 25 |
|
111 | 26 | bool isParticipantTokenValid(const std::string& participant_token) { |
112 | | - const auto payload_json = extractJwtPayloadJson(participant_token); |
113 | | - if (!payload_json.has_value()) { |
114 | | - return false; |
115 | | - } |
116 | | - |
117 | | - const nlohmann::json payload = nlohmann::json::parse(*payload_json, nullptr, /*allow_exceptions=*/false); |
118 | | - if (!payload.is_object()) { |
119 | | - return false; |
120 | | - } |
121 | | - |
122 | | - const auto now_seconds = |
123 | | - std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); |
124 | | - |
125 | | - const auto nbf = readNumericClaim(payload, "nbf"); |
126 | | - if (nbf.has_value() && *nbf > now_seconds) { |
127 | | - return false; |
128 | | - } |
129 | | - |
130 | | - const auto exp = readNumericClaim(payload, "exp"); |
131 | | - if (exp.has_value()) { |
132 | | - constexpr std::int64_t kExpiryBufferSeconds = 60; |
133 | | - if (*exp <= now_seconds + kExpiryBufferSeconds) { |
| 27 | + try { |
| 28 | + const auto claims = livekit_uniffi::token_claims_from_unverified(participant_token); |
| 29 | + constexpr std::uint64_t kExpiryBufferSeconds = 60; |
| 30 | + const auto now_seconds = |
| 31 | + std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now().time_since_epoch()).count(); |
| 32 | + if (now_seconds < 0) { |
134 | 33 | return false; |
135 | 34 | } |
| 35 | + return claims.exp > static_cast<std::uint64_t>(now_seconds) + kExpiryBufferSeconds; |
| 36 | + } catch (const std::exception&) { |
| 37 | + return false; |
136 | 38 | } |
137 | | - |
138 | | - return true; |
139 | 39 | } |
140 | 40 |
|
141 | 41 | } // namespace livekit |
0 commit comments