@@ -52,6 +52,16 @@ pub enum HttpServerAuthConfig {
5252 password : SensitiveString ,
5353 } ,
5454
55+ /// Bearer authentication.
56+ ///
57+ /// The token is matched against the `Authorization` header using the `Bearer` scheme.
58+ Bearer {
59+ /// The bearer token to match against incoming requests.
60+ #[ configurable( metadata( docs:: examples = "${TOKEN}" ) ) ]
61+ #[ configurable( metadata( docs:: examples = "my-secret-token" ) ) ]
62+ token : SensitiveString ,
63+ } ,
64+
5565 /// Custom authentication using VRL code.
5666 ///
5767 /// Takes in request and validates it using VRL code.
@@ -69,13 +79,13 @@ impl<'de> Deserialize<'de> for HttpServerAuthConfig {
6979 {
7080 struct HttpServerAuthConfigVisitor ;
7181
72- const FIELD_KEYS : [ & str ; 4 ] = [ "strategy" , "username" , "password" , "source" ] ;
82+ const FIELD_KEYS : [ & str ; 5 ] = [ "strategy" , "username" , "password" , "token ", "source" ] ;
7383
7484 impl < ' de > Visitor < ' de > for HttpServerAuthConfigVisitor {
7585 type Value = HttpServerAuthConfig ;
7686
7787 fn expecting ( & self , formatter : & mut fmt:: Formatter ) -> fmt:: Result {
78- formatter. write_str ( "a valid authentication strategy (basic or custom)" )
88+ formatter. write_str ( "a valid authentication strategy (basic, bearer, or custom)" )
7989 }
8090
8191 fn visit_map < A > ( self , mut map : A ) -> Result < HttpServerAuthConfig , A :: Error >
@@ -114,13 +124,24 @@ impl<'de> Deserialize<'de> for HttpServerAuthConfig {
114124 password : SensitiveString :: from ( password) ,
115125 } )
116126 }
127+ "bearer" => {
128+ let token = fields
129+ . remove ( "token" )
130+ . ok_or_else ( || Error :: missing_field ( "token" ) ) ?;
131+ Ok ( HttpServerAuthConfig :: Bearer {
132+ token : SensitiveString :: from ( token) ,
133+ } )
134+ }
117135 "custom" => {
118136 let source = fields
119137 . remove ( "source" )
120138 . ok_or_else ( || Error :: missing_field ( "source" ) ) ?;
121139 Ok ( HttpServerAuthConfig :: Custom { source } )
122140 }
123- _ => Err ( Error :: unknown_variant ( strategy, & [ "basic" , "custom" ] ) ) ,
141+ _ => Err ( Error :: unknown_variant (
142+ strategy,
143+ & [ "basic" , "bearer" , "custom" ] ,
144+ ) ) ,
124145 }
125146 }
126147 }
@@ -145,6 +166,15 @@ impl HttpServerAuthConfig {
145166 "Invalid username/password" ,
146167 ) )
147168 }
169+ HttpServerAuthConfig :: Bearer { token } => {
170+ let auth = Authorization :: bearer ( token. inner ( ) ) . map_err ( |e| {
171+ format ! ( "Invalid bearer token: {e}" )
172+ } ) ?;
173+ Ok ( HttpServerAuthMatcher :: AuthHeader (
174+ auth. 0 . encode ( ) ,
175+ "Invalid token" ,
176+ ) )
177+ }
148178 HttpServerAuthConfig :: Custom { source } => {
149179 let state = TypeState :: default ( ) ;
150180
@@ -402,6 +432,112 @@ mod tests {
402432 ) ;
403433 }
404434
435+ #[ test]
436+ fn config_should_support_bearer_strategy ( ) {
437+ let config: HttpServerAuthConfig = serde_yaml:: from_str ( indoc ! { r#"
438+ strategy: bearer
439+ token: my-secret-token
440+ "#
441+ } )
442+ . unwrap ( ) ;
443+
444+ if let HttpServerAuthConfig :: Bearer { token } = config {
445+ assert_eq ! ( token. inner( ) , "my-secret-token" ) ;
446+ } else {
447+ panic ! ( "Expected HttpServerAuthConfig::Bearer" ) ;
448+ }
449+ }
450+
451+ #[ test]
452+ fn build_bearer_auth_should_always_work ( ) {
453+ let bearer_auth = HttpServerAuthConfig :: Bearer {
454+ token : random_string ( 16 ) . into ( ) ,
455+ } ;
456+
457+ let matcher = bearer_auth. build ( & Default :: default ( ) , & Default :: default ( ) ) ;
458+
459+ assert ! ( matcher. is_ok( ) ) ;
460+ assert ! ( matches!(
461+ matcher. unwrap( ) ,
462+ HttpServerAuthMatcher :: AuthHeader { .. }
463+ ) ) ;
464+ }
465+
466+ #[ test]
467+ fn build_bearer_auth_should_use_token_related_message ( ) {
468+ let bearer_auth = HttpServerAuthConfig :: Bearer {
469+ token : random_string ( 16 ) . into ( ) ,
470+ } ;
471+
472+ let ( _, error_message) = bearer_auth
473+ . build ( & Default :: default ( ) , & Default :: default ( ) )
474+ . unwrap ( )
475+ . auth_header ( ) ;
476+ assert_eq ! ( "Invalid token" , error_message) ;
477+ }
478+
479+ #[ test]
480+ fn bearer_auth_matcher_should_return_401_when_missing_auth_header ( ) {
481+ let bearer_auth = HttpServerAuthConfig :: Bearer {
482+ token : "my-token" . into ( ) ,
483+ } ;
484+
485+ let matcher = bearer_auth
486+ . build ( & Default :: default ( ) , & Default :: default ( ) )
487+ . unwrap ( ) ;
488+
489+ let ( _guard, addr) = next_addr ( ) ;
490+ let result = matcher. handle_auth ( Some ( & addr) , & HeaderMap :: new ( ) , "/" ) ;
491+
492+ assert ! ( result. is_err( ) ) ;
493+ let error = result. unwrap_err ( ) ;
494+ assert_eq ! ( 401 , error. code( ) ) ;
495+ assert_eq ! ( "No authorization header" , error. message( ) ) ;
496+ }
497+
498+ #[ test]
499+ fn bearer_auth_matcher_should_return_401_with_wrong_token ( ) {
500+ let bearer_auth = HttpServerAuthConfig :: Bearer {
501+ token : "my-token" . into ( ) ,
502+ } ;
503+
504+ let matcher = bearer_auth
505+ . build ( & Default :: default ( ) , & Default :: default ( ) )
506+ . unwrap ( ) ;
507+
508+ let mut headers = HeaderMap :: new ( ) ;
509+ headers. insert ( AUTHORIZATION , HeaderValue :: from_static ( "Bearer wrong-token" ) ) ;
510+ let ( _guard, addr) = next_addr ( ) ;
511+ let result = matcher. handle_auth ( Some ( & addr) , & headers, "/" ) ;
512+
513+ assert ! ( result. is_err( ) ) ;
514+ let error = result. unwrap_err ( ) ;
515+ assert_eq ! ( 401 , error. code( ) ) ;
516+ assert_eq ! ( "Invalid token" , error. message( ) ) ;
517+ }
518+
519+ #[ test]
520+ fn bearer_auth_matcher_should_return_ok_for_correct_token ( ) {
521+ let token = "my-secret-token" ;
522+ let bearer_auth = HttpServerAuthConfig :: Bearer {
523+ token : token. into ( ) ,
524+ } ;
525+
526+ let matcher = bearer_auth
527+ . build ( & Default :: default ( ) , & Default :: default ( ) )
528+ . unwrap ( ) ;
529+
530+ let mut headers = HeaderMap :: new ( ) ;
531+ headers. insert (
532+ AUTHORIZATION ,
533+ Authorization :: bearer ( token) . unwrap ( ) . 0 . encode ( ) ,
534+ ) ;
535+ let ( _guard, addr) = next_addr ( ) ;
536+ let result = matcher. handle_auth ( Some ( & addr) , & headers, "/" ) ;
537+
538+ assert ! ( result. is_ok( ) ) ;
539+ }
540+
405541 #[ test]
406542 fn build_custom_should_fail_on_invalid_source ( ) {
407543 let custom_auth = HttpServerAuthConfig :: Custom {
0 commit comments