jsonwebtoken_google/
lib.rs1extern crate core;
2
3use jsonwebtoken::{Algorithm, Validation};
4use serde::de::DeserializeOwned;
5use thiserror::Error;
6
7use crate::keys::{GoogleKeyProviderError, GooglePublicKeyProvider};
8
9mod keys;
10
11#[cfg(any(test, feature = "test-helper"))]
12pub mod test_helper;
13
14
15#[derive(Error, Debug)]
19pub enum ParserError {
20 #[error("Wrong header.")]
21 WrongHeader,
22 #[error("Unknown kid.")]
23 UnknownKid,
24 #[error("Download public key error - {0}.")]
25 KeyProvider(GoogleKeyProviderError),
26 #[error("Wrong token format - {0}.")]
27 WrongToken(jsonwebtoken::errors::Error),
28}
29
30pub struct Parser {
35 client_id: String,
36 key_provider: tokio::sync::Mutex<GooglePublicKeyProvider>,
37}
38
39impl Parser {
40 pub const GOOGLE_CERT_URL: &'static str = "https://www.googleapis.com/oauth2/v3/certs";
41
42 pub fn new(client_id: &str) -> Self {
43 Parser::new_with_custom_cert_url(client_id, Parser::GOOGLE_CERT_URL)
44 }
45
46 pub fn new_with_custom_cert_url(client_id: &str, public_key_url: &str) -> Self {
47 Self {
48 client_id: client_id.to_owned(),
49 key_provider: tokio::sync::Mutex::new(GooglePublicKeyProvider::new(public_key_url)),
50 }
51 }
52
53 pub async fn parse<T: DeserializeOwned>(&self, token: &str) -> Result<T, ParserError> {
59 let mut provider = self.key_provider.lock().await;
60 match jsonwebtoken::decode_header(token) {
61 Ok(header) => match header.kid {
62 None => Result::Err(ParserError::UnknownKid),
63 Some(kid) => match provider.get_key(kid.as_str()).await {
64 Ok(key) => {
65 let aud = vec![self.client_id.to_owned()];
66 let mut validation = Validation::new(Algorithm::RS256);
67 validation.set_audience(&aud);
68 validation.set_issuer(&["https://accounts.google.com".to_string(), "accounts.google.com".to_string()]);
69 validation.validate_exp = true;
70 validation.validate_nbf = false;
71 let result = jsonwebtoken::decode::<T>(token, &key, &validation);
72 match result {
73 Result::Ok(token_data) => Result::Ok(token_data.claims),
74 Result::Err(error) => Result::Err(ParserError::WrongToken(error)),
75 }
76 }
77 Err(e) => {
78 let error = ParserError::KeyProvider(e);
79 Result::Err(error)
80 }
81 },
82 },
83 Err(_) => Result::Err(ParserError::WrongHeader),
84 }
85 }
86}
87
88#[cfg(test)]
89mod tests {
90 use jsonwebtoken::errors::ErrorKind;
91
92 use crate::ParserError;
93 use crate::test_helper::{setup, TokenClaims};
94
95 #[tokio::test]
96 async fn should_correct() {
97 let claims = TokenClaims::new();
98 let (token, parser, _server) = setup(&claims);
99 let result = parser.parse::<TokenClaims>(token.as_str()).await;
100 let result = result.unwrap();
101 assert_eq!(result.email, claims.email);
102 }
103
104 #[tokio::test]
105 async fn should_validate_exp() {
106 let claims = TokenClaims::new_expired();
107 let (token, validator, _server) = setup(&claims);
108 let result = validator.parse::<TokenClaims>(token.as_str()).await;
109
110 assert!(
111 if let ParserError::WrongToken(error) = result.err().unwrap() {
112 if let ErrorKind::ExpiredSignature = error.into_kind() {
113 true
114 } else {
115 false
116 }
117 } else {
118 false
119 }
120 );
121 }
122
123 #[tokio::test]
124 async fn should_validate_iss() {
125 let mut claims = TokenClaims::new();
126 claims.iss = "https://some.com".to_owned();
127 let (token, validator, _server) = setup(&claims);
128 let result = validator.parse::<TokenClaims>(token.as_str()).await;
129 assert!(
130 if let ParserError::WrongToken(error) = result.err().unwrap() {
131 if let ErrorKind::InvalidIssuer = error.into_kind() {
132 true
133 } else {
134 false
135 }
136 } else {
137 false
138 }
139 );
140 }
141
142 #[tokio::test]
143 async fn should_validate_aud() {
144 let mut claims = TokenClaims::new();
145 claims.aud = "other-id".to_owned();
146 let (token, validator, _server) = setup(&claims);
147 let result = validator.parse::<TokenClaims>(token.as_str()).await;
148 assert!(
149 if let ParserError::WrongToken(error) = result.err().unwrap() {
150 if let ErrorKind::InvalidAudience = error.into_kind() {
151 true
152 } else {
153 false
154 }
155 } else {
156 false
157 }
158 );
159 }
160}