firebase_auth_sdk/api/
token.rs

1use super::FailResponse;
2use crate::error::Error;
3use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Validation};
4use serde::{Deserialize, Serialize};
5
6impl crate::FireAuth {
7    pub async fn refresh_id_token(&self, refresh_token: &str) -> Result<RefreshIdToken, Error> {
8        let url = format!(
9            "https://securetoken.googleapis.com/v1/token?key={}",
10            self.api_key,
11        );
12
13        let client = reqwest::Client::new();
14        let resp = client
15            .post(url)
16            .header("Content-Type", "application/json")
17            .json(&RefreshIdTokenPayload {
18                grant_type: "refresh_token",
19                refresh_token,
20            })
21            .send()
22            .await?;
23
24        if resp.status() != 200 {
25            let error = resp.json::<FailResponse>().await?.error;
26            return Err(Error::Token(error.message));
27        }
28
29        let body = resp.json::<RefreshIdToken>().await?;
30        Ok(body)
31    }
32
33    pub async fn verify_id_token(&self, id_token: &str) -> Result<IdTokenClaims, Error> {
34        // Gets the kid property of the token header
35        let kid = decode_header(id_token)
36            .map_err(|_| Error::Token("Malformed token header!".into()))?
37            .kid
38            .ok_or(Error::Token("Missing kid in token header!".into()))?;
39
40        // Fetches the possible decoding keys
41        let url = String::from ( "https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com" );
42        let client = reqwest::Client::new();
43        let resp = client.get(url).send().await?;
44
45        if resp.status() != 200 {
46            // Cannot guarantee an error message from the response
47            return Err(Error::API("Failed to fetch keys!".into()));
48        }
49
50        let body: std::collections::HashMap<String, String> =
51            serde_json::from_str(&resp.text().await?)
52                .map_err(|_| Error::API("Failed to parse keys!".into()))?;
53
54        // Gets the key that will verify the ID token
55        let decoding_key = body
56            .get(&kid)
57            .ok_or(Error::Token("No match decoding key!".into()))?;
58        let decoding_key = &DecodingKey::from_rsa_pem(decoding_key.as_bytes())
59            .map_err(|_| Error::Token("Failed to parse decoding key!".into()))?;
60
61        // Decodes the ID token
62        let decoded =
63            decode::<IdTokenClaims>(id_token, decoding_key, &Validation::new(Algorithm::RS256))
64                .map_err(|_| Error::Token("Invalid ID token!".into()))?
65                .claims;
66        let timestamp = jsonwebtoken::get_current_timestamp();
67
68        // Checks if the token is expired
69        if decoded.exp <= timestamp {
70            return Err(Error::Token("Token is expired!".into()));
71        }
72
73        // Checks if the token is valid yet
74        if decoded.iat >= timestamp {
75            return Err(Error::Token("Token isn't valid yet!".into()));
76        }
77
78        Ok(decoded)
79    }
80}
81
82#[derive(Debug, Serialize)]
83struct RefreshIdTokenPayload<'a> {
84    grant_type: &'a str,
85    refresh_token: &'a str,
86}
87
88#[derive(Debug, Serialize, Deserialize)]
89pub struct RefreshIdToken {
90    pub access_token: String,
91    pub expires_in: String,
92    pub token_type: String,
93    pub refresh_token: String,
94    pub id_token: String,
95    pub user_id: String,
96    pub project_id: String,
97}
98
99// The firebase ID token claims
100#[derive(Clone, Debug, Deserialize, Serialize)]
101pub struct IdTokenClaims {
102    pub exp: u64,
103    pub iat: u64,
104    pub iss: String,
105    pub sub: String,
106    pub auth_time: u64,
107}