pub(crate) fn frontend_api_host(publishable_key: &str) -> Option<String> {
let encoded = publishable_key
.strip_prefix("pk_test_")
.or_else(|| publishable_key.strip_prefix("pk_live_"))?;
let decoded = base64_decode(encoded)?;
let text = String::from_utf8(decoded).ok()?;
let host = text.strip_suffix('$').unwrap_or(&text).trim();
(!host.is_empty()).then(|| host.to_owned())
}
fn base64_decode(input: &str) -> Option<Vec<u8>> {
fn sextet(byte: u8) -> Option<u8> {
match byte {
b'A'..=b'Z' => Some(byte - b'A'),
b'a'..=b'z' => Some(byte - b'a' + 26),
b'0'..=b'9' => Some(byte - b'0' + 52),
b'+' => Some(62),
b'/' => Some(63),
_ => None,
}
}
let input = input.trim_end_matches('=');
let mut out = Vec::with_capacity(input.len() / 4 * 3 + 3);
let mut accumulator: u32 = 0;
let mut bits: u32 = 0;
for &byte in input.as_bytes() {
accumulator = (accumulator << 6) | u32::from(sextet(byte)?);
bits += 6;
if bits >= 8 {
bits -= 8;
out.push((accumulator >> bits) as u8);
}
}
Some(out)
}
#[cfg(test)]
mod tests {
use super::*;
fn publishable_key(prefix: &str, host_marker: &str) -> String {
format!("{prefix}{}", base64_encode(host_marker.as_bytes()))
}
fn base64_encode(input: &[u8]) -> String {
const TABLE: &[u8; 64] =
b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
let mut out = String::with_capacity(input.len().div_ceil(3) * 4);
for chunk in input.chunks(3) {
let b0 = chunk[0];
out.push(TABLE[(b0 >> 2) as usize] as char);
match chunk {
[_, b1, b2] => {
out.push(TABLE[(((b0 & 0b11) << 4) | (b1 >> 4)) as usize] as char);
out.push(TABLE[(((b1 & 0b1111) << 2) | (b2 >> 6)) as usize] as char);
out.push(TABLE[(b2 & 0b11_1111) as usize] as char);
}
[_, b1] => {
out.push(TABLE[(((b0 & 0b11) << 4) | (b1 >> 4)) as usize] as char);
out.push(TABLE[((b1 & 0b1111) << 2) as usize] as char);
out.push('=');
}
_ => {
out.push(TABLE[((b0 & 0b11) << 4) as usize] as char);
out.push_str("==");
}
}
}
out
}
#[test]
fn decodes_frontend_api_host_from_test_key() {
let key = publishable_key("pk_test_", "foo-bar-13.clerk.accounts.dev$");
assert_eq!(
frontend_api_host(&key).as_deref(),
Some("foo-bar-13.clerk.accounts.dev")
);
}
#[test]
fn decodes_frontend_api_host_from_live_key() {
let key = publishable_key("pk_live_", "clerk.example.com$");
assert_eq!(
frontend_api_host(&key).as_deref(),
Some("clerk.example.com")
);
}
#[test]
fn tolerates_a_host_without_the_trailing_marker() {
let key = publishable_key("pk_test_", "clerk.example.com");
assert_eq!(
frontend_api_host(&key).as_deref(),
Some("clerk.example.com")
);
}
#[test]
fn rejects_a_key_without_a_publishable_prefix() {
assert!(frontend_api_host("sk_test_secret").is_none());
assert!(frontend_api_host("not-a-key").is_none());
}
#[test]
fn rejects_a_key_whose_body_is_not_valid_base64() {
assert!(frontend_api_host("pk_test_not*base64").is_none());
}
#[test]
fn rejects_a_key_that_decodes_to_an_empty_host() {
let key = publishable_key("pk_test_", "$");
assert!(frontend_api_host(&key).is_none());
}
}