Skip to main content

pdk_jwt_lib/provider/
token_provider.rs

1// Copyright (c) 2026, Salesforce, Inc.,
2// All rights reserved.
3// For full license text, see the LICENSE.txt file
4
5//! JWT token extraction from HTTP requests
6//!
7//! This module provides strategies to extract JWT tokens from incoming HTTP requests.
8//! It supports extracting tokens from Authorization headers using the Bearer scheme.
9//!
10//! ## Primary types
11//!
12//! - [`TokenProvider`]: provider for extracting JWT tokens from HTTP headers
13//!
14//! ## Supported extraction methods
15//!
16//! - **Bearer**: extracts JWT tokens from `Authorization: Bearer <token>` headers
17//!
18
19use crate::error::jwt_error::JWTError;
20use pdk_core::classy::hl::HeadersHandler;
21
22const AUTHORIZATION_HEADER: &str = "Authorization";
23const BEARER_START: &str = "Bearer ";
24const BEARER_START_LENGTH: usize = BEARER_START.len();
25
26/// Strategies to retrieve the JWT token from the incoming HTTP requests context.
27pub struct TokenProvider {}
28
29impl TokenProvider {
30    pub fn bearer(handler: &dyn HeadersHandler) -> Result<String, JWTError> {
31        handler
32            .header(AUTHORIZATION_HEADER)
33            .ok_or(JWTError::AuthorizationHeaderNotPresent)
34            .and_then(|auth_header| {
35                if auth_header.starts_with(BEARER_START) {
36                    let token = &auth_header[BEARER_START_LENGTH..];
37
38                    if token.starts_with(' ') {
39                        Err(JWTError::BearerMalformed)
40                    } else {
41                        Ok(String::from(token))
42                    }
43                } else {
44                    Err(JWTError::BearerNotPresent)
45                }
46            })
47    }
48}
49
50#[cfg(test)]
51mod tests {
52    mod bearer_provider_test_cases {
53        use super::super::TokenProvider;
54        use crate::error::jwt_error::JWTError;
55        use mockall::mock;
56        use test_case::test_case;
57
58        mock! {
59            pub HeadersHandler {}
60            impl pdk_core::classy::hl::HeadersHandler for HeadersHandler{
61                    fn headers(&self) -> Vec<(String, String)>;
62                    fn header(&self, name: &str) -> Option<String>;
63                    fn add_header(&self, name: &str, value: &str);
64                    fn set_header(&self, name: &str, value: &str);
65                    fn set_headers<'a>(&self, headers: Vec<(&'a str, &'a str)>);
66                    fn remove_header(&self, name: &str);
67            }
68        }
69
70        fn bearer_provision_test(
71            handler_response: Option<String>,
72            expectation: Result<String, JWTError>,
73        ) {
74            let mut handler = MockHeadersHandler::new();
75
76            handler
77                .expect_header()
78                .times(1)
79                .return_const(handler_response);
80
81            assert_eq!(TokenProvider::bearer(&handler), expectation);
82        }
83
84        #[test]
85        fn when_authorization_header_present_should_return_that_value() {
86            bearer_provision_test(
87                Some(String::from(
88                    "Bearer I am a value for the authorization bearer header",
89                )),
90                Ok(String::from(
91                    "I am a value for the authorization bearer header",
92                )),
93            );
94        }
95
96        #[test]
97        fn when_authorization_header_not_present_should_return_an_error() {
98            bearer_provision_test(None, Err(JWTError::AuthorizationHeaderNotPresent));
99        }
100
101        #[test_case("I am a value for the authorization header but not bearer"; "Bearer must be present")]
102        #[test_case(" Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer can not have spaces before")]
103        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 Bearer"; "Bearer can not be at the end")]
104        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer can not be in the middle")]
105        #[test_case("eyJ0eXAiOiJKV1QiLCBearereyJhbGciOiJIUzI1NiJ9"; "Bearer can not be inside of the token")]
106        #[test_case("BearereyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer must be separated from token")]
107        #[test_case("bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "B in Bearer must be uppercase")]
108        #[test_case("BEARER eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer must be capitalized")]
109        fn bearer_header_should_be_well_formed(header: &str) {
110            bearer_provision_test(Some(String::from(header)), Err(JWTError::BearerNotPresent));
111        }
112
113        #[test]
114        fn bearer_keyword_should_be_followed_by_just_one_space() {
115            bearer_provision_test(
116                Some(String::from("Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")),
117                Err(JWTError::BearerMalformed),
118            );
119        }
120    }
121}