ts_token/
token_verifier.rs1use core::time::Duration;
4
5use base64ct::{Base64UrlUnpadded, Encoding};
6use ts_crypto::{
7 EdwardsVerifyingKey, EllipticVerifyingKey, RsaVerifyingKey, Sha256, Sha384, Sha512,
8 VerifyingKey,
9 rsa::{Pkcs1v15, Pss},
10};
11
12use crate::{
13 JsonWebKey, JsonWebToken,
14 jwt::{Claims, Header},
15};
16
17pub struct TokenVerifier {
19 pub jwk: JsonWebKey,
21 pub key: VerifyingKey,
23}
24
25impl TokenVerifier {
26 pub fn new(jwk: JsonWebKey) -> Option<Self> {
28 let key = match &jwk.kty {
29 crate::KeyType::Ec { x, y } => {
30 let x = Base64UrlUnpadded::decode_vec(x).ok()?;
31 let y = Base64UrlUnpadded::decode_vec(y).ok()?;
32 let key = EllipticVerifyingKey::from_coordinates(&x, &y)?;
33 VerifyingKey::Elliptic(key)
34 }
35 crate::KeyType::Rsa { n, e } => {
36 let n = Base64UrlUnpadded::decode_vec(n).ok()?;
37 let e = Base64UrlUnpadded::decode_vec(e).ok()?;
38 let key = RsaVerifyingKey::from_parameters(&n, &e)?;
39 VerifyingKey::Rsa(key)
40 }
41 crate::KeyType::Okp { x } => {
42 let raw_key = Base64UrlUnpadded::decode_vec(x).ok()?;
43 let key = EdwardsVerifyingKey::from_raw_key(&raw_key)?;
44 VerifyingKey::Edwards(key)
45 }
46 };
47 Some(Self { jwk, key })
50 }
51
52 pub fn verify(&self, jws: &str) -> Option<JsonWebToken> {
54 let mut parts = jws.split('.');
55 let header_str = parts.next()?;
56 let claims_str = parts.next()?;
57 let signature = parts.next()?;
58
59 let header = Base64UrlUnpadded::decode_vec(header_str).ok()?;
60 let header: Header = serde_json::from_slice(&header).ok()?;
61
62 let claims = Base64UrlUnpadded::decode_vec(claims_str).ok()?;
63 let claims: Claims = serde_json::from_slice(&claims).ok()?;
64
65 let signature = Base64UrlUnpadded::decode_vec(signature).ok()?;
66
67 if header.kid != self.jwk.kid {
68 return None;
69 }
70
71 let message = [header_str, claims_str].join(".");
72
73 match self.jwk.alg.as_str() {
75 "RS256" => {
76 let VerifyingKey::Rsa(key) = &self.key else {
77 return None;
78 };
79 if !key.verifies::<Pkcs1v15, Sha256>(&signature, message.as_bytes()) {
80 return None;
81 }
82 }
83 "RS384" => {
84 let VerifyingKey::Rsa(key) = &self.key else {
85 return None;
86 };
87 if !key.verifies::<Pkcs1v15, Sha384>(&signature, message.as_bytes()) {
88 return None;
89 }
90 }
91 "RS512" => {
92 let VerifyingKey::Rsa(key) = &self.key else {
93 return None;
94 };
95 if !key.verifies::<Pkcs1v15, Sha512>(&signature, message.as_bytes()) {
96 return None;
97 }
98 }
99
100 "PS256" => {
101 let VerifyingKey::Rsa(key) = &self.key else {
102 return None;
103 };
104 if !key.verifies::<Pss, Sha256>(&signature, message.as_bytes()) {
105 return None;
106 }
107 }
108 "PS384" => {
109 let VerifyingKey::Rsa(key) = &self.key else {
110 return None;
111 };
112 if !key.verifies::<Pss, Sha384>(&signature, message.as_bytes()) {
113 return None;
114 }
115 }
116 "PS512" => {
117 let VerifyingKey::Rsa(key) = &self.key else {
118 return None;
119 };
120 if !key.verifies::<Pss, Sha512>(&signature, message.as_bytes()) {
121 return None;
122 }
123 }
124
125 "ES256" => {
126 let VerifyingKey::Elliptic(key) = &self.key else {
127 return None;
128 };
129 if !key.verifies::<Sha256>(&signature, message.as_bytes()) {
130 return None;
131 }
132 }
133 "ES384" => {
134 let VerifyingKey::Elliptic(key) = &self.key else {
135 return None;
136 };
137 if !key.verifies::<Sha384>(&signature, message.as_bytes()) {
138 return None;
139 }
140 }
141 "ES512" => {
142 let VerifyingKey::Elliptic(key) = &self.key else {
143 return None;
144 };
145 if !key.verifies::<Sha512>(&signature, message.as_bytes()) {
146 return None;
147 }
148 }
149
150 "Ed25519" | "Ed448" | "EdDSA" => {
151 let VerifyingKey::Edwards(key) = &self.key else {
152 return None;
153 };
154 if !key.verifies(&signature, message.as_bytes()) {
155 return None;
156 }
157 }
158
159 _ => return None,
160 }
161
162 let exp = Duration::from_secs(claims.exp);
164 let iat = Duration::from_secs(claims.iat);
165 let Ok(now) = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH) else {
166 return None;
167 };
168 if exp < now || iat > now {
169 return None;
170 }
171
172 Some(JsonWebToken { header, claims })
173 }
174}