1 /*
2 * RESTinio
3 */
4
5 /*!
6 * @file
7 * @brief Helpers for dealing with bearer authentification.
8 *
9 * @since v.0.6.7.1
10 */
11
12 #pragma once
13
14 #include <restinio/helpers/http_field_parsers/authorization.hpp>
15
16 #include <restinio/http_headers.hpp>
17 #include <restinio/request_handler.hpp>
18 #include <restinio/expected.hpp>
19
20 #include <iostream>
21
22 namespace restinio
23 {
24
25 namespace http_field_parsers
26 {
27
28 namespace bearer_auth
29 {
30
31 //
32 // params_t
33 //
34 /*!
35 * @brief Parameters for bearer authentification.
36 *
37 * @since v.0.6.7.1
38 */
39 struct params_t
40 {
41 //! Access Token.
42 /*!
43 * Can't be empty.
44 */
45 std::string token;
46 };
47
48 //
49 // extraction_error_t
50 //
51 /*!
52 * @brief Error codes for failures of extraction of bearer authentification
53 * parameters.
54 *
55 * @since v.0.6.7.1
56 */
57 enum class extraction_error_t
58 {
59 //! There is no HTTP field with authentification parameters.
60 no_auth_http_field,
61
62 //! The HTTP field with authentification parameters can't be parsed.
63 illegal_http_field_value,
64
65 //! Different authentification scheme found.
66 //! bearer authentification scheme is expected.
67 not_bearer_auth_scheme,
68
69 //! Invalid value of parameter for bearer authentification scheme.
70 //! The single parameter in the form of b64token is expected.
71 invalid_bearer_auth_param,
72 };
73
74 /*!
75 * @brief Helper function to get a string name of extraction_error enum.
76 *
77 * @since v.0.6.9
78 */
79 RESTINIO_NODISCARD
80 inline string_view_t
to_string_view(extraction_error_t what)81 to_string_view( extraction_error_t what ) noexcept
82 {
83 string_view_t result{ "<unknown>" };
84
85 switch( what )
86 {
87 case extraction_error_t::no_auth_http_field:
88 result = string_view_t{ "no_auth_http_field" };
89 break;
90
91 case extraction_error_t::illegal_http_field_value:
92 result = string_view_t{ "illegal_http_field_value" };
93 break;
94
95 case extraction_error_t::not_bearer_auth_scheme:
96 result = string_view_t{ "not_bearer_auth_scheme" };
97 break;
98
99 case extraction_error_t::invalid_bearer_auth_param:
100 result = string_view_t{ "invalid_bearer_auth_param" };
101 break;
102 }
103
104 return result;
105 }
106
107 //
108 // try_extract_params
109 //
110 /*!
111 * @brief Helper function for getting parameters of bearer authentification
112 * from an already parsed HTTP-field.
113 *
114 * @attention
115 * This function doesn't check the content of
116 * authorization_value_t::auth_scheme. It's expected that this field was
117 * checked earlier.
118 *
119 * Usage example:
120 * @code
121 * auto on_request(restinio::request_handle_t & req) {
122 * using namespace restinio::http_field_parsers;
123 * const auto opt_field = req.header().opt_value_of(
124 * restinio::http_field::authorization);
125 * if(opt_field) {
126 * const auto parsed_field = authorization_value_t::try_parse(*opt_field);
127 * if(parsed_field) {
128 * if("basic" == parsed_field->auth_scheme) {
129 * ... // Dealing with Basic authentification scheme.
130 * }
131 * else if("bearer" == parsed_field->auth_scheme) {
132 * using namespace restinio::http_field_parsers::bearer_auth;
133 * const auto bearer_params = try_extract_params(*parsed_field);
134 * if(bearer_params) {
135 * const std::string & token = auth_params->token;
136 * ... // Do something with token.
137 * }
138 * }
139 * else {
140 * ... // Other authentification schemes.
141 * }
142 * }
143 * }
144 * ...
145 * }
146 * @endcode
147 *
148 * @since v.0.6.8
149 */
150 RESTINIO_NODISCARD
151 inline expected_t< params_t, extraction_error_t >
try_extract_params(const authorization_value_t & http_field)152 try_extract_params(
153 const authorization_value_t & http_field )
154 {
155 const auto * b64token = get_if<authorization_value_t::token68_t>(
156 &http_field.auth_param );
157 if( !b64token )
158 return make_unexpected( extraction_error_t::invalid_bearer_auth_param );
159
160 return params_t{ b64token->value };
161 }
162
163 /*!
164 * @brief Helper function for getting parameters of bearer authentification
165 * from an already parsed HTTP-field.
166 *
167 * @attention
168 * This function doesn't check the content of
169 * authorization_value_t::auth_scheme. It's expected that this field was
170 * checked earlier.
171 *
172 * @note
173 * This function can be used if one wants to avoid memory allocation
174 * and can reuse value of auth_params.
175 *
176 * Usage example (please note that `const` is not used in code when
177 * authorization HTTP-field is parsed):
178 * @code
179 * auto on_request(restinio::request_handle_t & req) {
180 * using namespace restinio::http_field_parsers;
181 * const auto opt_field = req.header().opt_value_of(
182 * restinio::http_field::authorization);
183 * if(opt_field) {
184 * // parsed_field is a mutable object.
185 * // The content of parsed_field->auth_param can be moved out.
186 * auto parsed_field = authorization_value_t::try_parse(*opt_field);
187 * if(parsed_field) {
188 * if("basic" == parsed_field->auth_scheme) {
189 * ... // Dealing with Basic authentification scheme.
190 * }
191 * else if("bearer" == parsed_field->auth_scheme) {
192 * using namespace restinio::http_field_parsers::bearer_auth;
193 * const auto bearer_params =
194 * // Please note the usage of std::move here.
195 * try_extract_params(std::move(*parsed_field));
196 * if(bearer_params) {
197 * const std::string & token = auth_params->token;
198 * ... // Do something with token.
199 * }
200 * }
201 * else {
202 * ... // Other authentification schemes.
203 * }
204 * }
205 * }
206 * ...
207 * }
208 * @endcode
209 *
210 * @since v.0.6.8
211 */
212 RESTINIO_NODISCARD
213 inline expected_t< params_t, extraction_error_t >
try_extract_params(authorization_value_t && http_field)214 try_extract_params(
215 authorization_value_t && http_field )
216 {
217 auto * b64token = get_if<authorization_value_t::token68_t>(
218 &http_field.auth_param );
219 if( !b64token )
220 return make_unexpected( extraction_error_t::invalid_bearer_auth_param );
221
222 return params_t{ std::move(b64token->value) };
223 }
224
225 namespace impl
226 {
227
228 RESTINIO_NODISCARD
229 inline expected_t< params_t, extraction_error_t >
perform_extraction_attempt(const optional_t<string_view_t> opt_field_value)230 perform_extraction_attempt(
231 const optional_t< string_view_t > opt_field_value )
232 {
233 if( !opt_field_value )
234 return make_unexpected( extraction_error_t::no_auth_http_field );
235
236 auto field_value_parse_result = authorization_value_t::try_parse(
237 *opt_field_value );
238 if( !field_value_parse_result )
239 return make_unexpected( extraction_error_t::illegal_http_field_value );
240
241 auto & parsed_value = *field_value_parse_result;
242 if( "bearer" != parsed_value.auth_scheme )
243 return make_unexpected( extraction_error_t::not_bearer_auth_scheme );
244
245 return try_extract_params( std::move(parsed_value) );
246 }
247
248 } /* namespace impl */
249
250 //
251 // try_extract_params
252 //
253 /*!
254 * @brief Helper function for getting parameters of bearer authentification
255 * from a set of HTTP-fields.
256 *
257 * This helper function is intended to be used for cases when authentification
258 * parameters are stored inside a HTTP-field with a custom name. For example:
259 * @code
260 * auto check_authorization(const restinio::http_header_fields_t & fields) {
261 * using namespace restinio::http_field_parsers::bearer_auth;
262 * const auto auth_params = try_extract_params(fields, "X-My-Authorization");
263 * if(auth_params) {
264 * const std::string & token = auth_params->token;
265 * ... // Do something with token.
266 * }
267 * ...
268 * }
269 * @endcode
270 *
271 * @since v.0.6.9
272 */
273 RESTINIO_NODISCARD
274 inline expected_t< params_t, extraction_error_t >
try_extract_params(const http_header_fields_t & fields,string_view_t auth_field_name)275 try_extract_params(
276 //! A set of HTTP-fields.
277 const http_header_fields_t & fields,
278 //! The name of a HTTP-field with authentification parameters.
279 string_view_t auth_field_name )
280 {
281 return impl::perform_extraction_attempt(
282 fields.opt_value_of( auth_field_name ) );
283 }
284
285 /*!
286 * @brief Helper function for getting parameters of bearer authentification
287 * from a request.
288 *
289 * This helper function is intended to be used for cases when authentification
290 * parameters are stored inside a HTTP-field with a custom name. For example:
291 * @code
292 * auto on_request(restinio::request_handle_t & req) {
293 * using namespace restinio::http_field_parsers::bearer_auth;
294 * const auto auth_params = try_extract_params(*req, "X-My-Authorization");
295 * if(auth_params) {
296 * const std::string & token = auth_params->token;
297 * ... // Do something with token.
298 * }
299 * ...
300 * }
301 * @endcode
302 *
303 * @since v.0.6.7.1
304 */
305 template< typename Extra_Data >
306 RESTINIO_NODISCARD
307 inline expected_t< params_t, extraction_error_t >
try_extract_params(const generic_request_t<Extra_Data> & req,string_view_t auth_field_name)308 try_extract_params(
309 //! A request that should hold a HTTP-field with authentification
310 //! parameters.
311 const generic_request_t< Extra_Data > & req,
312 //! The name of a HTTP-field with authentification parameters.
313 string_view_t auth_field_name )
314 {
315 return try_extract_params( req.header(), auth_field_name );
316 }
317
318 /*!
319 * @brief Helper function for getting parameters of bearer authentification
320 * from a set of HTTP-fields.
321 *
322 * Usage example:
323 * @code
324 * auto check_authorization(const restinio::http_header_fields_t & fields) {
325 * using namespace restinio::http_field_parsers::bearer_auth;
326 * const auto auth_params = try_extract_params(
327 * fields, restinio::http_field::authorization);
328 * if(auth_params) {
329 * const std::string & token = auth_params->token;
330 * ... // Do something with token.
331 * }
332 * ...
333 * }
334 * @endcode
335 *
336 * @since v.0.6.9
337 */
338 RESTINIO_NODISCARD
339 inline expected_t< params_t, extraction_error_t >
try_extract_params(const http_header_fields_t & fields,http_field_t auth_field_id)340 try_extract_params(
341 //! A set of HTTP-fields.
342 const http_header_fields_t & fields,
343 //! The ID of a HTTP-field with authentification parameters.
344 http_field_t auth_field_id )
345 {
346 return impl::perform_extraction_attempt(
347 fields.opt_value_of( auth_field_id ) );
348 }
349
350 /*!
351 * @brief Helper function for getting parameters of bearer authentification
352 * from a request.
353 *
354 * Usage example:
355 * @code
356 * auto on_request(restinio::request_handle_t & req) {
357 * using namespace restinio::http_field_parsers::bearer_auth;
358 * const auto auth_params = try_extract_params(
359 * *req, restinio::http_field::authorization);
360 * if(auth_params) {
361 * const std::string & token = auth_params->token;
362 * ... // Do something with token.
363 * }
364 * ...
365 * }
366 * @endcode
367 *
368 * @since v.0.6.7.1
369 */
370 template< typename Extra_Data >
371 RESTINIO_NODISCARD
372 inline expected_t< params_t, extraction_error_t >
try_extract_params(const generic_request_t<Extra_Data> & req,http_field_t auth_field_id)373 try_extract_params(
374 //! A request that should hold a HTTP-field with authentification
375 //! parameters.
376 const generic_request_t< Extra_Data > & req,
377 //! The ID of a HTTP-field with authentification parameters.
378 http_field_t auth_field_id )
379 {
380 return try_extract_params( req.header(), auth_field_id );
381 }
382
383 } /* namespace bearer_auth */
384
385 } /* namespace http_field_parsers */
386
387 } /* namespace restinio */
388
389