95 using AccessTokenHashFunction =
96 std::function<core::Result<std::string>(std::string_view access_token)>;
99 AccessTokenHashFunction access_token_hash,
102 : jwt_verifier_(jwt_verifier),
103 dpop_verifier_(dpop_verifier),
104 access_token_hash_(std::move(access_token_hash)),
105 replay_cache_(replay_cache),
106 options_(std::move(options)) {}
110 const auto authorization =
111 server_auth_detail::header_value(request.
headers,
"Authorization");
112 if (!authorization.has_value()) {
113 return mcp::core::unexpected(
114 server::make_auth_error(
"missing authorization header"));
118 server_auth_detail::authorization_scheme_and_token(*authorization);
119 if (!parsed.has_value()) {
120 return mcp::core::unexpected(
121 server::make_auth_error(
"invalid authorization header"));
124 const auto scheme = parsed->first;
125 const auto access_token = parsed->second;
126 const bool dpop_scheme = server_auth_detail::ascii_iequals(scheme,
"DPoP");
127 if (!dpop_scheme && !server_auth_detail::ascii_iequals(scheme,
"Bearer")) {
128 return mcp::core::unexpected(
129 server::make_auth_error(
"unsupported authorization scheme"));
132 auto jwt = verify_access_token(access_token);
133 if (!jwt.has_value()) {
134 return mcp::core::unexpected(server::make_auth_error(
135 "access token verification failed", jwt.error().message));
138 const bool has_dpop_header =
139 server_auth_detail::header_value(request.
headers,
"DPoP").has_value();
140 if (options_.require_dpop || dpop_scheme || has_dpop_header) {
141 auto proof = verify_dpop_proof(request, access_token);
142 if (!proof.has_value()) {
143 return mcp::core::unexpected(proof.error());
148 identity.
subject = jwt->subject;
149 identity.
claims = jwt_claims_to_identity(*jwt);
155 std::string_view access_token) {
157 jwt_request.jwt = std::string(access_token);
158 jwt_request.purpose = JwtVerificationPurpose::kAccessToken;
159 jwt_request.issuer = options_.issuer;
160 jwt_request.audience = options_.audience;
161 jwt_request.required_algorithm = options_.required_algorithm;
162 jwt_request.required_claims = options_.required_claims;
163 jwt_request.now = SystemClock::now();
164 return jwt_verifier_.verify(jwt_request);
168 std::string_view access_token) {
171 return mcp::core::unexpected(server::make_auth_error(
172 "DPoP authentication requires HTTP method and URL"));
175 const auto proof_header =
176 server_auth_detail::header_value(request.
headers,
"DPoP");
177 if (!proof_header.has_value() || proof_header->empty()) {
178 return mcp::core::unexpected(
179 server::make_auth_error(
"missing DPoP proof header"));
181 if (!access_token_hash_) {
183 "DPoP authentication requires an access-token hash function"));
187 auto claims = dpop_verifier_.verify(
188 std::string(*proof_header), target,
189 std::optional<std::string>{std::string(access_token)});
190 if (!claims.has_value()) {
192 "DPoP proof verification failed", claims.error().message));
195 auto expected_hash = access_token_hash_(access_token);
196 if (!expected_hash.has_value()) {
198 "DPoP access-token hash failed", expected_hash.error().message));
201 auto claim_options = options_.dpop_claims;
202 claim_options.now = SystemClock::now();
203 claim_options.expected_access_token_hash = *expected_hash;
205 *claims, target, std::optional<std::string>{std::string(access_token)},
206 claim_options, replay_cache_);
207 if (!validated.has_value()) {
209 "DPoP proof claims are invalid", validated.error().message));
214 static std::unordered_map<std::string, std::string> jwt_claims_to_identity(
215 const VerifiedJwtClaims& claims) {
216 std::unordered_map<std::string, std::string> identity_claims;
217 if (!claims.issuer.empty()) {
218 identity_claims.emplace(
"iss", claims.issuer);
220 if (!claims.audience.empty()) {
221 identity_claims.emplace(
"aud", claims.audience);
223 for (
const auto& claim : claims.claims) {
224 identity_claims.emplace(claim.first, claim.second);
226 return identity_claims;
229 JwtVerifier& jwt_verifier_;
230 DpopVerifier& dpop_verifier_;
231 AccessTokenHashFunction access_token_hash_;
232 DpopReplayCache* replay_cache_ =
nullptr;
233 DpopAuthProviderOptions options_;
core::Result< core::Unit > validate_dpop_proof_claims(const DpopProofClaims &claims, const HttpRequestTarget &target, const std::optional< std::string > &access_token, const DpopClaimValidationOptions &options={}, DpopReplayCache *replay_cache=nullptr)
Validate DPoP claims after JWT signature verification.
Definition dpop.hpp:196