firebase_token/
verifier.rs1use 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}