PACS System 0.1.0
PACS DICOM system library
Loading...
Searching...
No Matches
kcenon::pacs::web::auth::jwt_validator Class Reference

#include <jwt_validator.h>

Collaboration diagram for kcenon::pacs::web::auth::jwt_validator:
Collaboration graph

Public Member Functions

 jwt_validator (const oauth2_config &config)
 Construct validator with OAuth 2.0 configuration.
 
std::pair< jwt_token, jwt_errordecode (std::string_view token_string) const
 Decode a JWT token string into its components.
 
jwt_error validate_claims (const jwt_claims &claims) const
 Validate JWT claims against configuration.
 
bool verify_rs256 (const jwt_token &token, std::string_view public_key_pem) const
 Verify RS256 (RSA-SHA256) signature.
 
bool verify_es256 (const jwt_token &token, std::string_view public_key_pem) const
 Verify ES256 (ECDSA-SHA256) signature.
 
const oauth2_configconfig () const noexcept
 Get the OAuth 2.0 configuration.
 

Static Public Member Functions

static bool has_scope (const jwt_claims &claims, std::string_view scope) noexcept
 Check if token has a specific scope.
 
static bool has_any_scope (const jwt_claims &claims, const std::vector< std::string > &scopes) noexcept
 Check if token has any of the specified scopes.
 

Private Attributes

oauth2_config config_
 

Detailed Description

Definition at line 124 of file jwt_validator.h.

Constructor & Destructor Documentation

◆ jwt_validator()

kcenon::pacs::web::auth::jwt_validator::jwt_validator ( const oauth2_config & config)
explicit

Construct validator with OAuth 2.0 configuration.

Parameters
configThe OAuth 2.0 configuration
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 178 of file jwt_validator.cpp.

179 : config_(config) {}
const oauth2_config & config() const noexcept
Get the OAuth 2.0 configuration.

Member Function Documentation

◆ config()

const oauth2_config & kcenon::pacs::web::auth::jwt_validator::config ( ) const
nodiscardnoexcept

Get the OAuth 2.0 configuration.

Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 481 of file jwt_validator.cpp.

481 {
482 return config_;
483}

References config_.

◆ decode()

std::pair< jwt_token, jwt_error > kcenon::pacs::web::auth::jwt_validator::decode ( std::string_view token_string) const
nodiscard

Decode a JWT token string into its components.

Splits the token, decodes Base64url segments, and parses JSON. Does NOT verify the signature.

Parameters
token_stringThe raw JWT string (header.payload.signature)
Returns
Pair of decoded token and error code
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 181 of file jwt_validator.cpp.

182 {
183
184 jwt_token token;
185
186 // Split into 3 parts: header.payload.signature
187 auto dot1 = token_string.find('.');
188 if (dot1 == std::string_view::npos) {
189 return {token, jwt_error::malformed_token};
190 }
191
192 auto dot2 = token_string.find('.', dot1 + 1);
193 if (dot2 == std::string_view::npos) {
194 return {token, jwt_error::malformed_token};
195 }
196
197 // Check no additional dots
198 if (token_string.find('.', dot2 + 1) != std::string_view::npos) {
199 return {token, jwt_error::malformed_token};
200 }
201
202 auto header_b64 = token_string.substr(0, dot1);
203 auto payload_b64 = token_string.substr(dot1 + 1, dot2 - dot1 - 1);
204 auto signature_b64 = token_string.substr(dot2 + 1);
205
206 // Store header.payload for signature verification
207 token.header_payload = std::string(token_string.substr(0, dot2));
208
209 // Decode header
210 auto header_json = base64url_decode(header_b64);
211 if (!header_json) {
212 return {token, jwt_error::invalid_base64};
213 }
214
215 // Decode payload
216 auto payload_json = base64url_decode(payload_b64);
217 if (!payload_json) {
218 return {token, jwt_error::invalid_base64};
219 }
220
221 // Decode signature
222 auto sig_bytes = base64url_decode(signature_b64);
223 if (!sig_bytes) {
224 return {token, jwt_error::invalid_base64};
225 }
226 token.signature_bytes = std::move(*sig_bytes);
227
228 // Parse header JSON
229 auto header_rv = crow::json::load(*header_json);
230 if (!header_rv) {
231 return {token, jwt_error::invalid_json};
232 }
233
234 token.header.alg = json_string(header_rv, "alg");
235 token.header.typ = json_string(header_rv, "typ");
236 token.header.kid = json_string(header_rv, "kid");
237
238 if (token.header.alg.empty()) {
239 return {token, jwt_error::missing_required_claim};
240 }
241
242 // Block dangerous algorithms unconditionally (CVE: JWT alg:none attack)
243 static const std::set<std::string> kDangerousAlgorithms = {
244 "none", "None", "NONE", "HS256", "HS384", "HS512"};
245 if (kDangerousAlgorithms.count(token.header.alg)) {
246 return {token, jwt_error::unsupported_algorithm};
247 }
248
249 // Check algorithm is allowed
250 auto& allowed = config_.allowed_algorithms;
251 if (!allowed.empty()) {
252 bool alg_allowed = std::any_of(
253 allowed.begin(), allowed.end(),
254 [&](const std::string& a) { return a == token.header.alg; });
255 if (!alg_allowed) {
256 return {token, jwt_error::unsupported_algorithm};
257 }
258 }
259
260 // Parse payload JSON
261 auto payload_rv = crow::json::load(*payload_json);
262 if (!payload_rv) {
263 return {token, jwt_error::invalid_json};
264 }
265
266 token.claims.iss = json_string(payload_rv, "iss");
267 token.claims.sub = json_string(payload_rv, "sub");
268 token.claims.aud = json_string(payload_rv, "aud");
269 token.claims.exp = json_int64(payload_rv, "exp");
270 token.claims.iat = json_int64(payload_rv, "iat");
271 token.claims.nbf = json_int64(payload_rv, "nbf");
272 token.claims.jti = json_string(payload_rv, "jti");
273
274 // Parse scopes from "scope" claim (space-delimited string per RFC 6749)
275 auto scope_str = json_string(payload_rv, "scope");
276 if (!scope_str.empty()) {
277 token.claims.scopes = parse_scopes(scope_str);
278 }
279
280 return {token, jwt_error::none};
281}
@ invalid_base64
Base64url decoding failed.
@ malformed_token
Token doesn't have 3 dot-separated parts.
@ unsupported_algorithm
Algorithm not in allowed list.
@ missing_required_claim
Required claim is missing.
@ invalid_json
JSON parsing failed.
std::optional< std::string > base64url_decode(std::string_view input)
Decode a Base64url-encoded string (RFC 4648 Section 5)
std::vector< std::string > allowed_algorithms
Allowed signing algorithms (default: RS256, ES256)

References kcenon::pacs::web::auth::jwt_header::alg, kcenon::pacs::web::auth::oauth2_config::allowed_algorithms, kcenon::pacs::web::auth::jwt_claims::aud, kcenon::pacs::web::auth::base64url_decode(), kcenon::pacs::web::auth::jwt_token::claims, config_, kcenon::pacs::web::auth::jwt_claims::exp, kcenon::pacs::web::auth::jwt_token::header, kcenon::pacs::web::auth::jwt_token::header_payload, kcenon::pacs::web::auth::jwt_claims::iat, kcenon::pacs::web::auth::invalid_base64, kcenon::pacs::web::auth::invalid_json, kcenon::pacs::web::auth::jwt_claims::iss, kcenon::pacs::web::auth::jwt_claims::jti, kcenon::pacs::web::auth::jwt_header::kid, kcenon::pacs::web::auth::malformed_token, kcenon::pacs::web::auth::missing_required_claim, kcenon::pacs::web::auth::jwt_claims::nbf, kcenon::pacs::web::auth::none, kcenon::pacs::web::auth::jwt_claims::scopes, kcenon::pacs::web::auth::jwt_token::signature_bytes, kcenon::pacs::web::auth::jwt_claims::sub, kcenon::pacs::web::auth::jwt_header::typ, and kcenon::pacs::web::auth::unsupported_algorithm.

Referenced by kcenon::pacs::web::auth::oauth2_middleware::authenticate().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ has_any_scope()

bool kcenon::pacs::web::auth::jwt_validator::has_any_scope ( const jwt_claims & claims,
const std::vector< std::string > & scopes )
staticnodiscardnoexcept

Check if token has any of the specified scopes.

Parameters
claimsThe token claims
scopesList of acceptable scopes
Returns
true if at least one scope matches
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 471 of file jwt_validator.cpp.

473 {
474
475 return std::any_of(scopes.begin(), scopes.end(),
476 [&claims](const std::string& scope) {
477 return has_scope(claims, scope);
478 });
479}

Referenced by kcenon::pacs::web::auth::oauth2_middleware::require_any_scope().

Here is the caller graph for this function:

◆ has_scope()

bool kcenon::pacs::web::auth::jwt_validator::has_scope ( const jwt_claims & claims,
std::string_view scope )
staticnodiscardnoexcept

Check if token has a specific scope.

Parameters
claimsThe token claims
scopeThe required scope
Returns
true if the scope is present
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 463 of file jwt_validator.cpp.

465 {
466
467 return std::any_of(claims.scopes.begin(), claims.scopes.end(),
468 [scope](const std::string& s) { return s == scope; });
469}

Referenced by kcenon::pacs::web::auth::oauth2_middleware::require_scope().

Here is the caller graph for this function:

◆ validate_claims()

jwt_error kcenon::pacs::web::auth::jwt_validator::validate_claims ( const jwt_claims & claims) const
nodiscard

Validate JWT claims against configuration.

Checks issuer, audience, expiration, and not-before claims.

Parameters
claimsThe decoded claims to validate
Returns
jwt_error::none if all claims are valid
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 283 of file jwt_validator.cpp.

283 {
284 auto now = current_timestamp();
285 auto skew = static_cast<std::int64_t>(config_.clock_skew_seconds);
286
287 // Check expiration
288 if (claims.exp > 0 && now > claims.exp + skew) {
290 }
291
292 // Check not-before
293 if (claims.nbf > 0 && now < claims.nbf - skew) {
295 }
296
297 // Check issuer
298 if (!config_.issuer.empty() && claims.iss != config_.issuer) {
300 }
301
302 // Check audience
303 if (!config_.audience.empty() && claims.aud != config_.audience) {
305 }
306
307 // Sub claim is typically required
308 if (claims.sub.empty()) {
310 }
311
312 return jwt_error::none;
313}
@ invalid_audience
Audience doesn't match expected value.
@ token_expired
Token has expired (exp claim)
@ invalid_issuer
Issuer doesn't match expected value.
@ token_not_yet_valid
Token not yet valid (nbf claim)
std::string audience
Expected audience (aud claim). Empty = skip audience validation.
std::uint32_t clock_skew_seconds
Allowed clock skew in seconds for exp/nbf validation.
std::string issuer
Expected token issuer (iss claim). Empty = skip issuer validation.

References kcenon::pacs::web::auth::jwt_claims::aud, kcenon::pacs::web::auth::oauth2_config::audience, kcenon::pacs::web::auth::oauth2_config::clock_skew_seconds, config_, kcenon::pacs::web::auth::jwt_claims::exp, kcenon::pacs::web::auth::invalid_audience, kcenon::pacs::web::auth::invalid_issuer, kcenon::pacs::web::auth::jwt_claims::iss, kcenon::pacs::web::auth::oauth2_config::issuer, kcenon::pacs::web::auth::missing_required_claim, kcenon::pacs::web::auth::jwt_claims::nbf, kcenon::pacs::web::auth::none, kcenon::pacs::web::auth::jwt_claims::sub, kcenon::pacs::web::auth::token_expired, and kcenon::pacs::web::auth::token_not_yet_valid.

Referenced by kcenon::pacs::web::auth::oauth2_middleware::authenticate().

Here is the caller graph for this function:

◆ verify_es256()

bool kcenon::pacs::web::auth::jwt_validator::verify_es256 ( const jwt_token & token,
std::string_view public_key_pem ) const
nodiscard

Verify ES256 (ECDSA-SHA256) signature.

Parameters
tokenThe decoded token with signature data
public_key_pemEC public key in PEM format
Returns
true if signature is valid
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 451 of file jwt_validator.cpp.

453 {
454 return false;
455}

Referenced by kcenon::pacs::web::auth::oauth2_middleware::verify_signature().

Here is the caller graph for this function:

◆ verify_rs256()

bool kcenon::pacs::web::auth::jwt_validator::verify_rs256 ( const jwt_token & token,
std::string_view public_key_pem ) const
nodiscard

Verify RS256 (RSA-SHA256) signature.

Parameters
tokenThe decoded token with signature data
public_key_pemRSA public key in PEM format
Returns
true if signature is valid
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/jwt_validator.h.

Definition at line 445 of file jwt_validator.cpp.

447 {
448 return false;
449}

Referenced by kcenon::pacs::web::auth::oauth2_middleware::verify_signature().

Here is the caller graph for this function:

Member Data Documentation

◆ config_

oauth2_config kcenon::pacs::web::auth::jwt_validator::config_
private

The documentation for this class was generated from the following files: