firebase_auth_sdk/api/
token.rs1use 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 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 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 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 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 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 if decoded.exp <= timestamp {
70 return Err(Error::Token("Token is expired!".into()));
71 }
72
73 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#[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}