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 };
22
23 struct JwtHeader
24 {
26 std::string kid;
27 };
30
32 {
33 size_t nbf;
34 size_t exp;
35 std::string iss;
36 std::optional<std::string> tid;
37 };
41
43 {
44 public:
45 struct Token
46 {
47 nlohmann::json header;
49 nlohmann::json payload;
51 std::vector<uint8_t> signature;
52 std::string_view signed_content;
53 };
54
55 static bool parse_auth_scheme(
56 std::string_view& auth_header_value, std::string& error_reason)
57 {
58 auto next_space = auth_header_value.find(" ");
59 if (next_space == std::string::npos)
60 {
61 error_reason = "Authorization header only contains one field";
62 return false;
63 }
64 auto auth_scheme = auth_header_value.substr(0, next_space);
65 if (auth_scheme != ccf::http::auth::BEARER_AUTH_SCHEME)
66 {
67 error_reason = fmt::format(
68 "Authorization header does not have {} scheme",
69 ccf::http::auth::BEARER_AUTH_SCHEME);
70 return false;
71 }
72 auth_header_value = auth_header_value.substr(next_space + 1);
73 return true;
74 }
75
76 static std::optional<Token> parse_token(
77 std::string_view& token, std::string& error_reason)
78 {
79 constexpr char separator = '.';
80 size_t first_dot = token.find(separator);
81 size_t second_dot = std::string::npos;
82 if (first_dot != std::string::npos)
83 {
84 second_dot = token.find(separator, first_dot + 1);
85 }
86 size_t extra_dot = std::string::npos;
87 if (second_dot != std::string::npos)
88 {
89 extra_dot = token.find(separator, second_dot + 1);
90 }
91 if (
92 first_dot == std::string::npos || second_dot == std::string::npos ||
93 extra_dot != std::string::npos)
94 {
95 error_reason = "Malformed JWT: must contain exactly 3 parts";
96 return std::nullopt;
97 }
98 size_t header_size = first_dot;
99 size_t payload_size = second_dot - first_dot - 1;
100 std::string_view header_b64url = token.substr(0, header_size);
101 std::string_view payload_b64url =
102 token.substr(first_dot + 1, payload_size);
103 std::string_view signature_b64url = token.substr(second_dot + 1);
104 auto header_raw = ccf::crypto::raw_from_b64url(header_b64url);
105 auto payload_raw = ccf::crypto::raw_from_b64url(payload_b64url);
106 auto signature_raw = ccf::crypto::raw_from_b64url(signature_b64url);
107 auto signed_content = token.substr(0, second_dot);
108 nlohmann::json header;
109 nlohmann::json payload;
110 try
111 {
112 header = nlohmann::json::parse(header_raw);
113 payload = nlohmann::json::parse(payload_raw);
114 }
115 catch (const nlohmann::json::parse_error& e)
116 {
117 error_reason =
118 fmt::format("JWT header or payload is not valid JSON: {}", e.what());
119 return std::nullopt;
120 }
121 if (!header.is_object() || !payload.is_object())
122 {
123 error_reason = "JWT header or payload is not an object";
124 return std::nullopt;
125 }
126 JwtHeader header_typed;
127 try
128 {
129 header_typed = header.get<JwtHeader>();
130 }
131 catch (const ccf::JsonParseError& e)
132 {
133 error_reason =
134 fmt::format("JWT header does not follow schema: {}", e.describe());
135 return std::nullopt;
136 }
137 JwtPayload payload_typed;
138 try
139 {
140 payload_typed = payload.get<JwtPayload>();
141 }
142 catch (const ccf::JsonParseError& e)
143 {
144 error_reason = fmt::format(
145 "JWT payload is missing required field: {}", e.describe());
146 return std::nullopt;
147 }
148 Token parsed = {
149 header,
150 header_typed,
151 payload,
152 payload_typed,
153 signature_raw,
154 signed_content};
155 return parsed;
156 }
157
158 static std::optional<Token> extract_token(
159 const ccf::http::HeaderMap& headers, std::string& error_reason)
160 {
161 const auto auth_it = headers.find(ccf::http::headers::AUTHORIZATION);
162 if (auth_it == headers.end())
163 {
164 error_reason =
165 fmt::format("Missing {} header", ccf::http::headers::AUTHORIZATION);
166 return std::nullopt;
167 }
168 std::string_view token = auth_it->second;
169 if (!parse_auth_scheme(token, error_reason))
170 {
171 return std::nullopt;
172 }
173 auto parsed = parse_token(token, error_reason);
174 return parsed;
175 }
176
178 const Token& token, const ccf::crypto::VerifierPtr& verifier)
179 {
180 return verifier->verify(
181 (uint8_t*)token.signed_content.data(),
182 token.signed_content.size(),
183 token.signature.data(),
184 token.signature.size(),
186 }
187 };
188}
Definition json.h:24
Definition http_jwt.h:43
static std::optional< Token > parse_token(std::string_view &token, std::string &error_reason)
Definition http_jwt.h:76
static bool validate_token_signature(const Token &token, const ccf::crypto::VerifierPtr &verifier)
Definition http_jwt.h:177
static std::optional< Token > extract_token(const ccf::http::HeaderMap &headers, std::string &error_reason)
Definition http_jwt.h:158
static bool parse_auth_scheme(std::string_view &auth_header_value, std::string &error_reason)
Definition http_jwt.h:55
#define DECLARE_JSON_REQUIRED_FIELDS(TYPE,...)
Definition json.h:712
#define DECLARE_JSON_TYPE(TYPE)
Definition json.h:661
#define DECLARE_JSON_TYPE_WITH_OPTIONAL_FIELDS(TYPE)
Definition json.h:688
#define DECLARE_JSON_OPTIONAL_FIELDS(TYPE,...)
Definition json.h:784
#define DECLARE_JSON_ENUM(TYPE,...)
Definition json.h:835
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:24
JwtCryptoAlgorithm alg
Definition http_jwt.h:25
std::string kid
Definition http_jwt.h:26
Definition http_jwt.h:32
std::optional< std::string > tid
Definition http_jwt.h:36
size_t nbf
Definition http_jwt.h:33
size_t exp
Definition http_jwt.h:34
std::string iss
Definition http_jwt.h:35
Definition http_jwt.h:46
JwtPayload payload_typed
Definition http_jwt.h:50
nlohmann::json header
Definition http_jwt.h:47
std::vector< uint8_t > signature
Definition http_jwt.h:51
JwtHeader header_typed
Definition http_jwt.h:48
nlohmann::json payload
Definition http_jwt.h:49
std::string_view signed_content
Definition http_jwt.h:52