1
1
//! Authentication middleware for the API.
2
2
//!
3
- //! It uses a "token" GET param to authenticate the user. URLs must be of the
4
- //! form:
3
+ //! It uses a "token" to authenticate the user. The token must be one of the
4
+ //! `access_tokens` in the tracker [HTTP API configuration](torrust_tracker_configuration::HttpApi).
5
+ //!
6
+ //! There are two ways to provide the token:
7
+ //!
8
+ //! 1. As a `Bearer` token in the `Authorization` header.
9
+ //! 2. As a `token` GET param in the URL.
10
+ //!
11
+ //! Using the `Authorization` header:
12
+ //!
13
+ //! ```console
14
+ //! curl -H "Authorization: Bearer MyAccessToken" http://<host>:<port>/api/v1/<context>
15
+ //! ```
16
+ //!
17
+ //! Using the `token` GET param:
5
18
//!
6
19
//! `http://<host>:<port>/api/v1/<context>?token=<token>`.
7
20
//!
21
34
//! All the tokes have the same permissions, so it is not possible to have
22
35
//! different permissions for different tokens. The label is only used to
23
36
//! identify the token.
37
+ //!
38
+ //! NOTICE: The token is not encrypted, so it is recommended to use HTTPS to
39
+ //! protect the token from being intercepted.
40
+ //!
41
+ //! NOTICE: If both the `Authorization` header and the `token` GET param are
42
+ //! provided, the `Authorization` header will be used.
24
43
use std:: sync:: Arc ;
25
44
26
45
use axum:: extract:: { self } ;
@@ -32,6 +51,8 @@ use torrust_tracker_configuration::AccessTokens;
32
51
33
52
use crate :: v1:: responses:: unhandled_rejection_response;
34
53
54
+ pub const AUTH_BEARER_TOKEN_HEADER_PREFIX : & str = "Bearer" ;
55
+
35
56
/// Container for the `token` extracted from the query params.
36
57
#[ derive( Deserialize , Debug ) ]
37
58
pub struct QueryParams {
@@ -43,16 +64,28 @@ pub struct State {
43
64
pub access_tokens : Arc < AccessTokens > ,
44
65
}
45
66
46
- /// Middleware for authentication using a "token" GET param.
67
+ /// Middleware for authentication.
68
+ ///
47
69
/// The token must be one of the tokens in the tracker [HTTP API configuration](torrust_tracker_configuration::HttpApi).
48
70
pub async fn auth (
49
71
extract:: State ( state) : extract:: State < State > ,
50
72
extract:: Query ( params) : extract:: Query < QueryParams > ,
51
73
request : Request < axum:: body:: Body > ,
52
74
next : Next ,
53
75
) -> Response {
54
- let Some ( token) = params. token else {
55
- return AuthError :: Unauthorized . into_response ( ) ;
76
+ let token_from_header = extract_bearer_token_from_header ( & request) ;
77
+
78
+ let token_from_get_param = params. token . clone ( ) ;
79
+
80
+ let provided_tokens = ( token_from_header, token_from_get_param) ;
81
+
82
+ println ! ( "Provided tokens: {provided_tokens:?}" ) ;
83
+
84
+ let token = match provided_tokens {
85
+ ( Some ( token_from_header) , Some ( _token_from_get_param) ) => token_from_header,
86
+ ( Some ( token_from_header) , None ) => token_from_header,
87
+ ( None , Some ( token_from_get_param) ) => token_from_get_param,
88
+ ( None , None ) => return AuthError :: Unauthorized . into_response ( ) ,
56
89
} ;
57
90
58
91
if !authenticate ( & token, & state. access_tokens ) {
@@ -62,9 +95,33 @@ pub async fn auth(
62
95
next. run ( request) . await
63
96
}
64
97
98
+ fn extract_bearer_token_from_header ( request : & Request < axum:: body:: Body > ) -> Option < String > {
99
+ let headers = request. headers ( ) ;
100
+ headers
101
+ . get ( axum:: http:: header:: AUTHORIZATION )
102
+ . and_then ( |header_value| header_value. to_str ( ) . ok ( ) )
103
+ . and_then ( |header_str| {
104
+ if ( header_str) == AUTH_BEARER_TOKEN_HEADER_PREFIX {
105
+ // Empty token
106
+ return Some ( String :: new ( ) ) ;
107
+ }
108
+
109
+ if !header_str. starts_with ( & format ! ( "{AUTH_BEARER_TOKEN_HEADER_PREFIX} " ) . to_string ( ) ) {
110
+ // Invalid token type. Missing "Bearer" prefix.
111
+ // We return the header as is, so the caller can decide what to do.
112
+ return Some ( header_str. to_owned ( ) ) ;
113
+ }
114
+
115
+ header_str
116
+ . strip_prefix ( & format ! ( "{AUTH_BEARER_TOKEN_HEADER_PREFIX} " ) . to_string ( ) )
117
+ . map ( std:: string:: ToString :: to_string)
118
+ } )
119
+ }
120
+
65
121
enum AuthError {
66
122
/// Missing token for authentication.
67
123
Unauthorized ,
124
+
68
125
/// Token was provided but it is not valid.
69
126
TokenNotValid ,
70
127
}
0 commit comments