CCF
Loading...
Searching...
No Matches
http_jwt.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
5#include "ccf/crypto/base64.h"
7#include "ccf/http_consts.h"
8#include "http_parser.h"
9
10#define FMT_HEADER_ONLY
11#include <fmt/format.h>
12#include <optional>
13#include <string>
14
15namespace http
16{
18 {
19 RS256,
20 ES256,
21 };
24 {{JwtCryptoAlgorithm::RS256, "RS256"},
25 {JwtCryptoAlgorithm::ES256, "ES256"}});
26
27 struct JwtHeader
28 {
30 std::string kid;
31 };
34
36 {
37 size_t exp;
38 std::string iss;
39 std::optional<size_t> nbf;
40 std::optional<std::string> tid;
41 };
45
47 {
48 public:
49 struct Token
50 {
51 nlohmann::json header;
53 nlohmann::json payload;
55 std::vector<uint8_t> signature;
56 std::string_view signed_content;
57 };
58
59 static bool parse_auth_scheme(
60 std::string_view& auth_header_value, std::string& error_reason)
61 {
62 auto next_space = auth_header_value.find(" ");
63 if (next_space == std::string::npos)
64 {
65 error_reason = "Authorization header only contains one field";
66 return false;
67 }
68 auto auth_scheme = auth_header_value.substr(0, next_space);
69 if (auth_scheme != ccf::http::auth::BEARER_AUTH_SCHEME)
70 {
71 error_reason = fmt::format(
72 "Authorization header does not have {} scheme",
73 ccf::http::auth::BEARER_AUTH_SCHEME);
74 return false;
75 }
76 auth_header_value = auth_header_value.substr(next_space + 1);
77 return true;
78 }
79
80 static std::optional<Token> parse_token(
81 std::string_view& token, std::string& error_reason)
82 {
83 constexpr char separator = '.';
84 size_t first_dot = token.find(separator);
85 size_t second_dot = std::string::npos;
86 if (first_dot != std::string::npos)
87 {
88 second_dot = token.find(separator, first_dot + 1);
89 }
90 size_t extra_dot = std::string::npos;
91 if (second_dot != std::string::npos)
92 {
93 extra_dot = token.find(separator, second_dot + 1);
94 }
95 if (
96 first_dot == std::string::npos || second_dot == std::string::npos ||
97 extra_dot != std::string::npos)
98 {
99 error_reason = "Malformed JWT: must contain exactly 3 parts";
100 return std::nullopt;
101 }
102 size_t header_size = first_dot;
103 size_t payload_size = second_dot - first_dot - 1;
104 std::string_view header_b64url = token.substr(0, header_size);
105 std::string_view payload_b64url =
106 token.substr(first_dot + 1, payload_size);
107 std::string_view signature_b64url = token.substr(second_dot + 1);
108 auto header_raw = ccf::crypto::raw_from_b64url(header_b64url);
109 auto payload_raw = ccf::crypto::raw_from_b64url(payload_b64url);
110 auto signature_raw = ccf::crypto::raw_from_b64url(signature_b64url);
111 auto signed_content = token.substr(0, second_dot);
112 nlohmann::json header;
113 nlohmann::json payload;
114 try
115 {
116 header = nlohmann::json::parse(header_raw);
117 payload = nlohmann::json::parse(payload_raw);
118 }
119 catch (const nlohmann::json::parse_error& e)
120 {
121 error_reason =
122 fmt::format("JWT header or payload is not valid JSON: {}", e.what());
123 return std::nullopt;
124 }
125 if (!header.is_object() || !payload.is_object())
126 {
127 error_reason = "JWT header or payload is not an object";
128 return std::nullopt;
129 }
130 JwtHeader header_typed;
131 try
132 {
133 header_typed = header.get<JwtHeader>();
134 }
135 catch (const ccf::JsonParseError& e)
136 {
137 error_reason =
138 fmt::format("JWT header does not follow schema: {}", e.describe());
139 return std::nullopt;
140 }
141 JwtPayload payload_typed;
142 try
143 {
144 payload_typed = payload.get<JwtPayload>();
145 }
146 catch (const ccf::JsonParseError& e)
147 {
148 error_reason = fmt::format(
149 "JWT payload is missing required field: {}", e.describe());
150 return std::nullopt;
151 }
152 Token parsed = {
153 header,
154 header_typed,
155 payload,
156 payload_typed,
157 signature_raw,
158 signed_content};
159 return parsed;
160 }
161
162 static std::optional<Token> extract_token(
163 const ccf::http::HeaderMap& headers, std::string& error_reason)
164 {
165 const auto auth_it = headers.find(ccf::http::headers::AUTHORIZATION);
166 if (auth_it == headers.end())
167 {
168 error_reason =
169 fmt::format("Missing {} header", ccf::http::headers::AUTHORIZATION);
170 return std::nullopt;
171 }
172 std::string_view token = auth_it->second;
173 if (!parse_auth_scheme(token, error_reason))
174 {
175 return std::nullopt;
176 }
177 auto parsed = parse_token(token, error_reason);
178 return parsed;
179 }
180
182 const Token& token, const ccf::crypto::VerifierPtr& verifier)
183 {
184 return verifier->verify(
185 (uint8_t*)token.signed_content.data(),
186 token.signed_content.size(),
187 token.signature.data(),
188 token.signature.size(),
190 }
191 };
192}
Definition json.h:26
Definition http_jwt.h:47
static std::optional< Token > parse_token(std::string_view &token, std::string &error_reason)
Definition http_jwt.h:80
static bool validate_token_signature(const Token &token, const ccf::crypto::VerifierPtr &verifier)
Definition http_jwt.h:181
static std::optional< Token > extract_token(const ccf::http::HeaderMap &headers, std::string &error_reason)
Definition http_jwt.h:162
static bool parse_auth_scheme(std::string_view &auth_header_value, std::string &error_reason)
Definition http_jwt.h:59
#define DECLARE_JSON_REQUIRED_FIELDS(TYPE,...)
Definition json.h:714
#define DECLARE_JSON_TYPE(TYPE)
Definition json.h:663
#define DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(TYPE)
Definition json.h:690
#define DECLARE_JSON_OPTIONAL_FIELDS(TYPE,...)
Definition json.h:786
#define DECLARE_JSON_ENUM(TYPE,...)
Definition json.h:837
std::vector< uint8_t > raw_from_b64url(const std::string_view &b64url_string)
Definition base64.cpp:17
std::shared_ptr< Verifier > VerifierPtr
Definition verifier.h:228
std::map< std::string, std::string, std::less<> > HeaderMap
Definition http_header_map.h:10
Definition error_reporter.h:6
JwtCryptoAlgorithm
Definition http_jwt.h:18
Definition http_jwt.h:28
JwtCryptoAlgorithm alg
Definition http_jwt.h:29
std::string kid
Definition http_jwt.h:30
Definition http_jwt.h:36
std::optional< std::string > tid
Definition http_jwt.h:40
size_t exp
Definition http_jwt.h:37
std::string iss
Definition http_jwt.h:38
std::optional< size_t > nbf
Definition http_jwt.h:39
Definition http_jwt.h:50
JwtPayload payload_typed
Definition http_jwt.h:54
nlohmann::json header
Definition http_jwt.h:51
std::vector< uint8_t > signature
Definition http_jwt.h:55
JwtHeader header_typed
Definition http_jwt.h:52
nlohmann::json payload
Definition http_jwt.h:53
std::string_view signed_content
Definition http_jwt.h:56