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

#include <oauth2_middleware.h>

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

Public Member Functions

 oauth2_middleware (const oauth2_config &config)
 Construct middleware with OAuth 2.0 configuration.
 
void set_jwks_provider (std::shared_ptr< jwks_provider > provider)
 Set the JWKS provider for signature verification.
 
void set_access_control_manager (std::shared_ptr< security::access_control_manager > manager)
 Set the access control manager for RBAC integration.
 
std::optional< auth_resultauthenticate (const crow::request &req, crow::response &res) const
 Authenticate a request using OAuth 2.0 Bearer token.
 
bool require_scope (const jwt_claims &claims, crow::response &res, std::string_view required_scope) const
 Check if the authenticated request has a required scope.
 
bool require_any_scope (const jwt_claims &claims, crow::response &res, const std::vector< std::string > &required_scopes) const
 Check if the request has any of the required scopes.
 
bool enabled () const noexcept
 Check if OAuth 2.0 is enabled.
 
const jwt_validatorvalidator () const noexcept
 Get the underlying JWT validator.
 

Private Member Functions

std::optional< std::string_view > extract_bearer_token (const crow::request &req) const
 Extract Bearer token from Authorization header.
 
bool verify_signature (const jwt_token &token) const
 Verify token signature using JWKS keys.
 

Static Private Member Functions

static void set_unauthorized (crow::response &res, std::string_view message)
 Set 401 Unauthorized response.
 
static void set_forbidden (crow::response &res, std::string_view message)
 Set 403 Forbidden response.
 

Private Attributes

oauth2_config config_
 
jwt_validator validator_
 
std::shared_ptr< jwks_providerjwks_provider_
 
std::shared_ptr< security::access_control_managersecurity_manager_
 

Detailed Description

Definition at line 111 of file oauth2_middleware.h.

Constructor & Destructor Documentation

◆ oauth2_middleware()

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

Construct middleware with OAuth 2.0 configuration.

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

Definition at line 32 of file oauth2_middleware.cpp.

Member Function Documentation

◆ authenticate()

std::optional< auth_result > kcenon::pacs::web::auth::oauth2_middleware::authenticate ( const crow::request & req,
crow::response & res ) const
nodiscard

Authenticate a request using OAuth 2.0 Bearer token.

Extracts the Bearer token, validates JWT claims, verifies the signature, and creates an auth_result containing both the user_context and validated claims. On failure, sets an appropriate error response (401 or 403).

Parameters
reqThe HTTP request
resThe HTTP response (set on error)
Returns
auth_result on success, std::nullopt on failure
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/oauth2_middleware.h.

Definition at line 45 of file oauth2_middleware.cpp.

46 {
47
48 // Extract Bearer token from Authorization header
49 auto bearer = extract_bearer_token(req);
50 if (!bearer) {
51 set_unauthorized(res, "Missing or invalid Authorization header");
52 return std::nullopt;
53 }
54
55 // Decode JWT
56 auto [token, decode_err] = validator_.decode(*bearer);
57 if (decode_err != jwt_error::none) {
58 set_unauthorized(res, std::string(jwt_error_message(decode_err)));
59 return std::nullopt;
60 }
61
62 // Validate claims (issuer, audience, expiration)
63 auto claims_err = validator_.validate_claims(token.claims);
64 if (claims_err != jwt_error::none) {
65 set_unauthorized(res, std::string(jwt_error_message(claims_err)));
66 return std::nullopt;
67 }
68
69 // Verify signature using JWKS
70 if (!verify_signature(token)) {
71 set_unauthorized(res, "Token signature verification failed");
72 return std::nullopt;
73 }
74
75 // Create user_context from JWT subject
76 // Try to find existing user in RBAC system, or create an OAuth-based context
77 security::User user;
78 user.id = token.claims.sub;
79 user.username = token.claims.sub;
80 user.active = true;
81
82 // If we have an access control manager, try to look up the user
84 auto user_result = security_manager_->get_user(token.claims.sub);
85 if (user_result.is_ok()) {
86 user = user_result.unwrap();
87 } else if (config_.allow_unknown_users) {
88 // Backward compatibility: grant Viewer role to unknown OAuth users
89 user.roles = {security::Role::Viewer};
90 } else {
91 set_unauthorized(res, "Unknown user: not registered in access control system");
92 return std::nullopt;
93 }
94 } else if (config_.allow_unknown_users) {
95 user.roles = {security::Role::Viewer};
96 } else {
97 set_unauthorized(res, "Unknown user: no access control manager configured");
98 return std::nullopt;
99 }
100
101 auto ctx = security::user_context(std::move(user), token.claims.jti);
102 ctx.set_source_ip(std::string(req.remote_ip_address));
103 ctx.touch();
104
105 return auth_result{std::move(ctx), std::move(token.claims)};
106}
jwt_error validate_claims(const jwt_claims &claims) const
Validate JWT claims against configuration.
std::pair< jwt_token, jwt_error > decode(std::string_view token_string) const
Decode a JWT token string into its components.
static void set_unauthorized(crow::response &res, std::string_view message)
Set 401 Unauthorized response.
std::optional< std::string_view > extract_bearer_token(const crow::request &req) const
Extract Bearer token from Authorization header.
bool verify_signature(const jwt_token &token) const
Verify token signature using JWKS keys.
std::shared_ptr< security::access_control_manager > security_manager_
@ Viewer
Read-only access to studies.
std::string_view jwt_error_message(jwt_error error) noexcept
Get human-readable description for a JWT error.
bool allow_unknown_users
Allow unknown OAuth users not found in RBAC to access as Viewer When false (default): unknown users r...

References kcenon::pacs::web::auth::oauth2_config::allow_unknown_users, config_, kcenon::pacs::web::auth::jwt_validator::decode(), extract_bearer_token(), kcenon::pacs::security::User::id, kcenon::pacs::web::auth::jwt_error_message(), kcenon::pacs::web::auth::none, security_manager_, set_unauthorized(), kcenon::pacs::web::auth::jwt_validator::validate_claims(), validator_, verify_signature(), and kcenon::pacs::security::Viewer.

Here is the call graph for this function:

◆ enabled()

bool kcenon::pacs::web::auth::oauth2_middleware::enabled ( ) const
nodiscardnoexcept

Check if OAuth 2.0 is enabled.

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

Definition at line 138 of file oauth2_middleware.cpp.

138 {
139 return config_.enabled;
140}
bool enabled
Enable OAuth 2.0 authorization (disabled by default for backward compat)

References config_, and kcenon::pacs::web::auth::oauth2_config::enabled.

◆ extract_bearer_token()

std::optional< std::string_view > kcenon::pacs::web::auth::oauth2_middleware::extract_bearer_token ( const crow::request & req) const
nodiscardprivate

Extract Bearer token from Authorization header.

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

Definition at line 150 of file oauth2_middleware.cpp.

151 {
152
153 auto auth_header = req.get_header_value("Authorization");
154 if (auth_header.empty()) return std::nullopt;
155
156 std::string_view header_view(auth_header);
157
158 // Check for "Bearer " prefix (case-insensitive for "Bearer")
159 constexpr std::string_view bearer_prefix = "Bearer ";
160 if (header_view.size() <= bearer_prefix.size()) return std::nullopt;
161
162 // Case-sensitive check per RFC 6750
163 if (header_view.substr(0, bearer_prefix.size()) != bearer_prefix) {
164 return std::nullopt;
165 }
166
167 auto token = header_view.substr(bearer_prefix.size());
168 if (token.empty()) return std::nullopt;
169
170 return token;
171}

Referenced by authenticate().

Here is the caller graph for this function:

◆ require_any_scope()

bool kcenon::pacs::web::auth::oauth2_middleware::require_any_scope ( const jwt_claims & claims,
crow::response & res,
const std::vector< std::string > & required_scopes ) const
nodiscard

Check if the request has any of the required scopes.

Parameters
claimsJWT claims from a previously validated token
resThe HTTP response (set on failure)
required_scopesList of acceptable scopes
Returns
true if at least one scope is present
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/oauth2_middleware.h.

Definition at line 121 of file oauth2_middleware.cpp.

124 {
125
126 if (!jwt_validator::has_any_scope(claims, required_scopes)) {
127 std::string scope_list;
128 for (size_t i = 0; i < required_scopes.size(); ++i) {
129 if (i > 0) scope_list += " or ";
130 scope_list += required_scopes[i];
131 }
132 set_forbidden(res, "Insufficient scope: requires " + scope_list);
133 return false;
134 }
135 return true;
136}
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.
static void set_forbidden(crow::response &res, std::string_view message)
Set 403 Forbidden response.

References kcenon::pacs::web::auth::jwt_validator::has_any_scope(), and set_forbidden().

Here is the call graph for this function:

◆ require_scope()

bool kcenon::pacs::web::auth::oauth2_middleware::require_scope ( const jwt_claims & claims,
crow::response & res,
std::string_view required_scope ) const
nodiscard

Check if the authenticated request has a required scope.

Must be called after authenticate(). Returns false and sets 403 response if the required scope is missing.

Parameters
claimsJWT claims from a previously validated token
resThe HTTP response (set on failure)
required_scopeThe required OAuth scope
Returns
true if the scope is present
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/oauth2_middleware.h.

Definition at line 108 of file oauth2_middleware.cpp.

111 {
112
113 if (!jwt_validator::has_scope(claims, required_scope)) {
114 set_forbidden(res,
115 "Insufficient scope: requires " + std::string(required_scope));
116 return false;
117 }
118 return true;
119}
static bool has_scope(const jwt_claims &claims, std::string_view scope) noexcept
Check if token has a specific scope.

References kcenon::pacs::web::auth::jwt_validator::has_scope(), and set_forbidden().

Here is the call graph for this function:

◆ set_access_control_manager()

void kcenon::pacs::web::auth::oauth2_middleware::set_access_control_manager ( std::shared_ptr< security::access_control_manager > manager)

Set the access control manager for RBAC integration.

Parameters
managerShared pointer to access control manager
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/oauth2_middleware.h.

Definition at line 40 of file oauth2_middleware.cpp.

41 {
42 security_manager_ = std::move(manager);
43}

References security_manager_.

◆ set_forbidden()

void kcenon::pacs::web::auth::oauth2_middleware::set_forbidden ( crow::response & res,
std::string_view message )
staticprivate

Set 403 Forbidden response.

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

Definition at line 207 of file oauth2_middleware.cpp.

208 {
209 res.code = 403;
210 res.add_header("Content-Type", "application/json");
211 res.body = make_error_json("FORBIDDEN", message);
212}
std::string make_error_json(std::string_view code, std::string_view message)
Create JSON error response body with details.
Definition rest_types.h:79

References kcenon::pacs::web::make_error_json().

Referenced by require_any_scope(), and require_scope().

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

◆ set_jwks_provider()

void kcenon::pacs::web::auth::oauth2_middleware::set_jwks_provider ( std::shared_ptr< jwks_provider > provider)

Set the JWKS provider for signature verification.

Parameters
providerShared pointer to JWKS provider
Examples
/home/runner/work/pacs_system/pacs_system/include/kcenon/pacs/web/auth/oauth2_middleware.h.

Definition at line 35 of file oauth2_middleware.cpp.

36 {
37 jwks_provider_ = std::move(provider);
38}
std::shared_ptr< jwks_provider > jwks_provider_

References jwks_provider_.

◆ set_unauthorized()

void kcenon::pacs::web::auth::oauth2_middleware::set_unauthorized ( crow::response & res,
std::string_view message )
staticprivate

Set 401 Unauthorized response.

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

Definition at line 199 of file oauth2_middleware.cpp.

200 {
201 res.code = 401;
202 res.add_header("Content-Type", "application/json");
203 res.add_header("WWW-Authenticate", "Bearer");
204 res.body = make_error_json("UNAUTHORIZED", message);
205}

References kcenon::pacs::web::make_error_json().

Referenced by authenticate().

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

◆ validator()

const jwt_validator & kcenon::pacs::web::auth::oauth2_middleware::validator ( ) const
nodiscardnoexcept

Get the underlying JWT validator.

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

Definition at line 142 of file oauth2_middleware.cpp.

142 {
143 return validator_;
144}

References validator_.

◆ verify_signature()

bool kcenon::pacs::web::auth::oauth2_middleware::verify_signature ( const jwt_token & token) const
nodiscardprivate

Verify token signature using JWKS keys.

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

Definition at line 173 of file oauth2_middleware.cpp.

173 {
174 if (!jwks_provider_) return false;
175
176 // Try to get key by kid from the token header
177 std::optional<jwk_key> key;
178 if (!token.header.kid.empty()) {
179 key = jwks_provider_->get_key(token.header.kid);
180 }
181
182 // Fall back to algorithm-based lookup
183 if (!key) {
184 key = jwks_provider_->get_key_by_alg(token.header.alg);
185 }
186
187 if (!key) return false;
188
189 // Verify based on algorithm
190 if (token.header.alg == "RS256") {
191 return validator_.verify_rs256(token, key->pem);
192 } else if (token.header.alg == "ES256") {
193 return validator_.verify_es256(token, key->pem);
194 }
195
196 return false;
197}
bool verify_es256(const jwt_token &token, std::string_view public_key_pem) const
Verify ES256 (ECDSA-SHA256) signature.
bool verify_rs256(const jwt_token &token, std::string_view public_key_pem) const
Verify RS256 (RSA-SHA256) signature.

References kcenon::pacs::web::auth::jwt_header::alg, kcenon::pacs::web::auth::jwt_token::header, jwks_provider_, kcenon::pacs::web::auth::jwt_header::kid, validator_, kcenon::pacs::web::auth::jwt_validator::verify_es256(), and kcenon::pacs::web::auth::jwt_validator::verify_rs256().

Referenced by authenticate().

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

Member Data Documentation

◆ config_

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

◆ jwks_provider_

std::shared_ptr<jwks_provider> kcenon::pacs::web::auth::oauth2_middleware::jwks_provider_
private

◆ security_manager_

std::shared_ptr<security::access_control_manager> kcenon::pacs::web::auth::oauth2_middleware::security_manager_
private

◆ validator_

jwt_validator kcenon::pacs::web::auth::oauth2_middleware::validator_
private

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