use axum::http::header::{HeaderMap, AUTHORIZATION};
use super::wire_format::WireFormat;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum BillingMode {
ApiKey,
Subscription,
Unknown,
}
impl BillingMode {
pub fn as_str(&self) -> &'static str {
match self {
BillingMode::ApiKey => "api_key",
BillingMode::Subscription => "subscription",
BillingMode::Unknown => "unknown",
}
}
}
pub fn detect_billing(wire: WireFormat, headers: &HeaderMap) -> BillingMode {
if wire == WireFormat::CursorConnectRpc {
return BillingMode::Subscription;
}
if headers.contains_key("x-api-key") {
return BillingMode::ApiKey;
}
match bearer_token(headers) {
Some(t) if t.starts_with("sk-ant-oat") => BillingMode::Subscription,
Some(t) if is_jwt(t) || t.starts_with("ya29.") => BillingMode::Subscription,
Some(t) if t.starts_with("sk-") => BillingMode::ApiKey,
_ => BillingMode::Unknown,
}
}
fn bearer_token(headers: &HeaderMap) -> Option<&str> {
let v = headers.get(AUTHORIZATION)?.to_str().ok()?.trim();
let (scheme, token) = v.split_once(' ')?;
scheme
.eq_ignore_ascii_case("bearer")
.then_some(token.trim_start())
}
fn is_jwt(token: &str) -> bool {
let b64url = |s: &str| {
!s.is_empty()
&& s.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
};
let mut parts = token.split('.');
matches!(
(parts.next(), parts.next(), parts.next(), parts.next()),
(Some(h), Some(p), Some(s), None)
if h.starts_with("eyJ") && b64url(h) && b64url(p) && b64url(s)
)
}
#[cfg(test)]
mod tests {
use super::*;
use axum::http::{HeaderName, HeaderValue};
const JWT: &str = "eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJ4In0.c2lnbmF0dXJl";
fn hdr(pairs: &[(&str, &str)]) -> HeaderMap {
let mut h = HeaderMap::new();
for (k, v) in pairs {
h.insert(
HeaderName::from_bytes(k.as_bytes()).unwrap(),
HeaderValue::from_str(v).unwrap(),
);
}
h
}
fn anthropic(pairs: &[(&str, &str)]) -> BillingMode {
detect_billing(WireFormat::AnthropicMessages, &hdr(pairs))
}
fn bearer(token: &str) -> BillingMode {
anthropic(&[("authorization", &format!("Bearer {token}"))])
}
#[test]
fn x_api_key_is_api_key() {
let mode = anthropic(&[("x-api-key", "sk-ant-xyz")]);
assert_eq!(mode, BillingMode::ApiKey);
assert_eq!(mode.as_str(), "api_key");
}
#[test]
fn claude_oauth_bearer_is_subscription() {
assert_eq!(bearer("sk-ant-oat01-abc"), BillingMode::Subscription);
assert_eq!(bearer("sk-ant-oat01-abc").as_str(), "subscription");
}
#[test]
fn jwt_bearer_is_subscription() {
assert_eq!(bearer(JWT), BillingMode::Subscription);
}
#[test]
fn google_oauth_bearer_is_subscription() {
assert_eq!(bearer("ya29.a0AfH6SMBabc"), BillingMode::Subscription);
}
#[test]
fn anthropic_api_key_bearer_is_api_key() {
assert_eq!(bearer("sk-ant-api03-abc"), BillingMode::ApiKey);
}
#[test]
fn openai_project_key_bearer_is_api_key() {
assert_eq!(bearer("sk-proj-abc"), BillingMode::ApiKey);
}
#[test]
fn generic_sk_bearer_is_api_key() {
assert_eq!(bearer("sk-abc123"), BillingMode::ApiKey);
}
#[test]
fn opaque_bearer_is_unknown() {
assert_eq!(bearer("oauth-token-value"), BillingMode::Unknown);
}
#[test]
fn malformed_jwt_is_unknown() {
assert_eq!(bearer("eyJabc"), BillingMode::Unknown);
assert_eq!(bearer("eyJabc.def"), BillingMode::Unknown);
assert_eq!(bearer("eyJabc..ghi"), BillingMode::Unknown);
assert_eq!(bearer("eyJabc.def.ghi.jkl"), BillingMode::Unknown);
assert_eq!(bearer("abc.def.ghi"), BillingMode::Unknown);
assert_eq!(bearer("eyJa+c.def.ghi"), BillingMode::Unknown);
}
#[test]
fn bearer_scheme_is_case_insensitive() {
let h = [("authorization", "bearer sk-ant-oat01-abc")];
assert_eq!(anthropic(&h), BillingMode::Subscription);
let h = [("authorization", "BEARER sk-proj-abc")];
assert_eq!(anthropic(&h), BillingMode::ApiKey);
}
#[test]
fn no_recognised_shape_is_unknown() {
assert_eq!(anthropic(&[]), BillingMode::Unknown);
assert_eq!(
anthropic(&[("authorization", "Bearer")]),
BillingMode::Unknown
);
assert_eq!(
anthropic(&[("authorization", "Bearer ")]),
BillingMode::Unknown
);
assert_eq!(
anthropic(&[("authorization", "Basic sk-abc")]),
BillingMode::Unknown
);
}
#[test]
fn x_api_key_wins_over_bearer() {
let h = [
("x-api-key", "sk-ant-xyz"),
("authorization", "Bearer sk-ant-oat01-t"),
];
assert_eq!(anthropic(&h), BillingMode::ApiKey);
}
#[test]
fn cursor_wire_format_is_subscription_whatever_the_token() {
for h in [
hdr(&[]),
hdr(&[("authorization", "Bearer opaque-cursor-token")]),
hdr(&[("authorization", "Bearer sk-proj-abc")]),
hdr(&[("x-api-key", "sk-ant-xyz")]),
] {
assert_eq!(
detect_billing(WireFormat::CursorConnectRpc, &h),
BillingMode::Subscription
);
}
}
#[test]
fn non_cursor_opaque_bearer_is_unknown() {
let h = hdr(&[("authorization", "Bearer opaque-cursor-token")]);
for wire in WireFormat::ALL {
if wire != WireFormat::CursorConnectRpc {
assert_eq!(detect_billing(wire, &h), BillingMode::Unknown, "{wire:?}");
}
}
}
}