firebase_token/
verifier.rs

1use std::{collections::HashMap, str::FromStr};
2
3use jsonwebtoken::{
4    Algorithm, DecodingKey, Validation, decode, decode_header, errors::Error as JwtError,
5};
6use serde::de::DeserializeOwned;
7use thiserror::Error;
8
9use crate::jwk::JwkKey;
10
11const ISSUER_URL: &str = "https://securetoken.google.com/";
12
13#[derive(Error, Debug)]
14pub enum VerificationError {
15    #[error("InvalidSignature `{0}`")]
16    InvalidSignature(JwtError),
17    #[error("InvalidToken `{0}`")]
18    InvalidToken(JwtError),
19    #[error("NoKid")]
20    NoKid,
21    #[error("InvalidKid")]
22    InvalidKid(String),
23    #[error("InvalidDecodingKey `{0}`")]
24    InvalidDecodingKey(JwtError),
25    #[error("UnknownKeyAlgorithm `{0}`")]
26    UnknownKeyAlgorithm(String),
27}
28
29pub(crate) struct JwkVerifier {
30    keys: HashMap<String, JwkKey>,
31    config: JwkConfig,
32}
33
34impl JwkVerifier {
35    pub(crate) fn new(project_id: &str, keys: Vec<JwkKey>) -> JwkVerifier {
36        JwkVerifier {
37            keys: keys_to_map(keys),
38            config: JwkConfig::new(project_id),
39        }
40    }
41
42    pub(crate) fn set_keys(&mut self, keys: Vec<JwkKey>) {
43        self.keys = keys_to_map(keys);
44    }
45
46    pub(crate) fn verify<T: DeserializeOwned>(&self, token: &str) -> Result<T, VerificationError> {
47        let header = decode_header(token).map_err(VerificationError::InvalidSignature)?;
48
49        let kid = header.kid.ok_or(VerificationError::NoKid)?;
50
51        let public_key = self
52            .keys
53            .get(&kid)
54            .ok_or(VerificationError::InvalidKid(kid))?;
55
56        let decoding_key = DecodingKey::from_rsa_components(&public_key.n, &public_key.e)
57            .map_err(VerificationError::InvalidDecodingKey)?;
58
59        let algorithm = Algorithm::from_str(&public_key.alg)
60            .map_err(|_| VerificationError::UnknownKeyAlgorithm(public_key.alg.clone()))?;
61
62        let mut validation = Validation::new(algorithm);
63        validation.set_audience(&[&self.config.audience]);
64        validation.set_issuer(&[&self.config.issuer]);
65
66        decode(token, &decoding_key, &validation)
67            .map(|v| v.claims)
68            .map_err(VerificationError::InvalidToken)
69    }
70}
71
72struct JwkConfig {
73    audience: String,
74    issuer: String,
75}
76
77impl JwkConfig {
78    fn new(project_id: &str) -> Self {
79        let issuer = format!("{ISSUER_URL}{project_id}");
80
81        Self {
82            audience: project_id.to_owned(),
83            issuer,
84        }
85    }
86}
87
88fn keys_to_map(keys: Vec<JwkKey>) -> HashMap<String, JwkKey> {
89    let mut keys_as_map = HashMap::new();
90
91    for key in keys {
92        keys_as_map.insert(key.kid.clone(), key);
93    }
94
95    keys_as_map
96}