use axum::http::HeaderMap;
use tonic::metadata::MetadataMap;
use crate::{CallerIdentity, auth::jwks::JwksCache};
#[derive(Clone, Debug)]
pub struct BearerToken {
token: String,
}
impl BearerToken {
#[must_use]
pub fn as_str(&self) -> &str {
&self.token
}
}
#[derive(Clone, Copy, Debug, thiserror::Error)]
pub enum AuthError {
#[error("missing bearer token")]
MissingBearer,
#[error("invalid bearer token")]
InvalidToken,
}
pub fn extract_http_bearer(headers: &HeaderMap) -> Result<BearerToken, AuthError> {
headers
.get(axum::http::header::AUTHORIZATION)
.and_then(|value| value.to_str().ok())
.and_then(parse_bearer)
.ok_or(AuthError::MissingBearer)
}
pub fn extract_grpc_bearer(metadata: &MetadataMap) -> Result<BearerToken, AuthError> {
metadata
.get("authorization")
.and_then(|value| value.to_str().ok())
.and_then(parse_bearer)
.ok_or(AuthError::MissingBearer)
}
pub async fn authorize_bearer_token(
cache: &JwksCache,
bearer: &BearerToken,
) -> Result<CallerIdentity, AuthError> {
cache
.validate(bearer.as_str())
.await
.map(|claims| claims.caller_identity())
.map_err(|_error| AuthError::InvalidToken)
}
fn parse_bearer(value: &str) -> Option<BearerToken> {
let token = value.strip_prefix("Bearer ")?.trim();
if token.is_empty() {
return None;
}
Some(BearerToken {
token: token.to_owned(),
})
}