pdk-jwt-lib 1.10.0

PDK JWT Library
Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! JWT token extraction from HTTP requests
//!
//! This module provides strategies to extract JWT tokens from incoming HTTP requests.
//! It supports extracting tokens from Authorization headers using the Bearer scheme.
//!
//! ## Primary types
//!
//! - [`TokenProvider`]: provider for extracting JWT tokens from HTTP headers
//!
//! ## Supported extraction methods
//!
//! - **Bearer**: extracts JWT tokens from `Authorization: Bearer <token>` headers
//!

use crate::error::jwt_error::JWTError;
use pdk_core::classy::hl::HeadersHandler;

const AUTHORIZATION_HEADER: &str = "Authorization";
const BEARER_START: &str = "Bearer ";
const BEARER_START_LENGTH: usize = BEARER_START.len();

/// Strategies to retrieve the JWT token from the incoming HTTP requests context.
pub struct TokenProvider {}

impl TokenProvider {
    pub fn bearer(handler: &dyn HeadersHandler) -> Result<String, JWTError> {
        handler
            .header(AUTHORIZATION_HEADER)
            .ok_or(JWTError::AuthorizationHeaderNotPresent)
            .and_then(|auth_header| {
                if auth_header.starts_with(BEARER_START) {
                    let token = &auth_header[BEARER_START_LENGTH..];

                    if token.starts_with(' ') {
                        Err(JWTError::BearerMalformed)
                    } else {
                        Ok(String::from(token))
                    }
                } else {
                    Err(JWTError::BearerNotPresent)
                }
            })
    }
}

#[cfg(test)]
mod tests {
    mod bearer_provider_test_cases {
        use super::super::TokenProvider;
        use crate::error::jwt_error::JWTError;
        use mockall::mock;
        use test_case::test_case;

        mock! {
            pub HeadersHandler {}
            impl pdk_core::classy::hl::HeadersHandler for HeadersHandler{
                    fn headers(&self) -> Vec<(String, String)>;
                    fn header(&self, name: &str) -> Option<String>;
                    fn add_header(&self, name: &str, value: &str);
                    fn set_header(&self, name: &str, value: &str);
                    fn set_headers<'a>(&self, headers: Vec<(&'a str, &'a str)>);
                    fn remove_header(&self, name: &str);
            }
        }

        fn bearer_provision_test(
            handler_response: Option<String>,
            expectation: Result<String, JWTError>,
        ) {
            let mut handler = MockHeadersHandler::new();

            handler
                .expect_header()
                .times(1)
                .return_const(handler_response);

            assert_eq!(TokenProvider::bearer(&handler), expectation);
        }

        #[test]
        fn when_authorization_header_present_should_return_that_value() {
            bearer_provision_test(
                Some(String::from(
                    "Bearer I am a value for the authorization bearer header",
                )),
                Ok(String::from(
                    "I am a value for the authorization bearer header",
                )),
            );
        }

        #[test]
        fn when_authorization_header_not_present_should_return_an_error() {
            bearer_provision_test(None, Err(JWTError::AuthorizationHeaderNotPresent));
        }

        #[test_case("I am a value for the authorization header but not bearer"; "Bearer must be present")]
        #[test_case(" Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer can not have spaces before")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 Bearer"; "Bearer can not be at the end")]
        #[test_case("eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9 Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer can not be in the middle")]
        #[test_case("eyJ0eXAiOiJKV1QiLCBearereyJhbGciOiJIUzI1NiJ9"; "Bearer can not be inside of the token")]
        #[test_case("BearereyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer must be separated from token")]
        #[test_case("bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "B in Bearer must be uppercase")]
        #[test_case("BEARER eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9"; "Bearer must be capitalized")]
        fn bearer_header_should_be_well_formed(header: &str) {
            bearer_provision_test(Some(String::from(header)), Err(JWTError::BearerNotPresent));
        }

        #[test]
        fn bearer_keyword_should_be_followed_by_just_one_space() {
            bearer_provision_test(
                Some(String::from("Bearer  eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9")),
                Err(JWTError::BearerMalformed),
            );
        }
    }
}