cxxmcp 1.1.6
C++ MCP SDK
Loading...
Searching...
No Matches
base64url.hpp
Go to the documentation of this file.
1// Copyright (c) 2025 [caomengxuan666]
2
3#pragma once
4
5#include <cstddef>
6#include <cstdint>
7#include <string>
8#include <string_view>
9#include <utility>
10#include <vector>
11
12#include "cxxmcp/auth/types.hpp"
14
17
18namespace mcp::auth::openssl {
19
20enum class JoseErrorCode {
21 kInvalidBase64Url = 2001,
22 kInvalidCompactJws = 2002,
23 kInvalidJoseHeader = 2003,
24 kInvalidJwk = 2004,
25 kUnsupportedJoseAlgorithm = 2005,
26 kSignatureVerificationFailed = 2006,
27 kJwtClaimValidationFailed = 2007,
28};
29
30inline core::Error make_jose_error(JoseErrorCode code, std::string message,
31 std::string detail = {}) {
32 return core::Error{static_cast<int>(code), std::move(message),
33 std::move(detail), std::string(AuthErrorCategory)};
34}
35
36inline std::string base64url_encode_bytes(const unsigned char* data,
37 std::size_t size) {
38 static constexpr char kAlphabet[] =
39 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
40 std::string output;
41 output.reserve(((size + 2) / 3) * 4);
42
43 for (std::size_t index = 0; index < size; index += 3) {
44 const auto remaining = size - index;
45 const auto b0 = data[index];
46 const auto b1 = remaining > 1 ? data[index + 1] : 0;
47 const auto b2 = remaining > 2 ? data[index + 2] : 0;
48
49 output.push_back(kAlphabet[(b0 >> 2) & 0x3F]);
50 output.push_back(kAlphabet[((b0 & 0x03) << 4) | ((b1 >> 4) & 0x0F)]);
51 if (remaining > 1) {
52 output.push_back(kAlphabet[((b1 & 0x0F) << 2) | ((b2 >> 6) & 0x03)]);
53 }
54 if (remaining > 2) {
55 output.push_back(kAlphabet[b2 & 0x3F]);
56 }
57 }
58 return output;
59}
60
61inline std::string base64url_encode(std::string_view data) {
62 return base64url_encode_bytes(
63 reinterpret_cast<const unsigned char*>(data.data()), data.size());
64}
65
66inline std::string base64url_encode(const std::vector<unsigned char>& data) {
67 return base64url_encode_bytes(data.data(), data.size());
68}
69
70namespace detail {
71
72inline int base64url_decode_value(char ch) {
73 if (ch >= 'A' && ch <= 'Z') {
74 return ch - 'A';
75 }
76 if (ch >= 'a' && ch <= 'z') {
77 return ch - 'a' + 26;
78 }
79 if (ch >= '0' && ch <= '9') {
80 return ch - '0' + 52;
81 }
82 if (ch == '-') {
83 return 62;
84 }
85 if (ch == '_') {
86 return 63;
87 }
88 return -1;
89}
90
91} // namespace detail
92
93inline core::Result<std::vector<unsigned char>> base64url_decode(
94 std::string_view input) {
95 if (input.find('=') != std::string_view::npos) {
96 return core::unexpected(
97 make_jose_error(JoseErrorCode::kInvalidBase64Url,
98 "base64url input must not contain padding"));
99 }
100 if (input.size() % 4 == 1) {
101 return core::unexpected(
102 make_jose_error(JoseErrorCode::kInvalidBase64Url,
103 "base64url input has invalid length"));
104 }
105
106 std::vector<unsigned char> output;
107 output.reserve((input.size() * 3) / 4);
108
109 std::uint32_t accumulator = 0;
110 int bits = 0;
111 for (const char ch : input) {
112 const int value = detail::base64url_decode_value(ch);
113 if (value < 0) {
114 return core::unexpected(make_jose_error(
115 JoseErrorCode::kInvalidBase64Url,
116 "base64url input contains an invalid character", std::string(1, ch)));
117 }
118
119 accumulator = (accumulator << 6) | static_cast<std::uint32_t>(value);
120 bits += 6;
121 if (bits >= 8) {
122 bits -= 8;
123 output.push_back(
124 static_cast<unsigned char>((accumulator >> bits) & 0xFF));
125 }
126 }
127
128 const std::uint32_t unused_mask =
129 bits == 0 ? 0U : ((1U << static_cast<unsigned int>(bits)) - 1U);
130 if ((accumulator & unused_mask) != 0U) {
131 return core::unexpected(
132 make_jose_error(JoseErrorCode::kInvalidBase64Url,
133 "base64url input contains non-zero trailing bits"));
134 }
135
136 return output;
137}
138
139inline core::Result<std::string> base64url_decode_to_string(
140 std::string_view input) {
141 auto decoded = base64url_decode(input);
142 if (!decoded.has_value()) {
143 return core::unexpected(decoded.error());
144 }
145 return std::string(decoded->begin(), decoded->end());
146}
147
148} // namespace mcp::auth::openssl
Shared lightweight value types for cxxmcp auth contracts.
Shared result and error primitives used by the public cxxmcp SDK.