tiny_oidc_rp/id_token.rs
1// SPDX-License-Identifier: MIT
2use crate::error::AuthenticationFailedError;
3
4/// OpenID connect ID Token.
5/// More detail, see OpenID Connect Core specification
6/// [2. ID Token](https://openid.net/specs/openid-connect-core-1_0.html#IDToken) and
7/// [5.1 Standard Claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)
8#[derive(serde::Deserialize)]
9pub struct IdToken<T = ()> {
10 pub(crate) iss: String, // Issuer
11 pub(crate) sub: String, // Subject (unique identifier)
12 pub(crate) aud: String, // Audience (must be same as client_id)
13 pub(crate) exp: u64, // Unix time expires at
14 pub(crate) iat: u64, // Unix time issued at
15 pub(crate) nonce: String, // nonce (must be same as nonce of auth request)
16 pub(crate) email: Option<String>,
17 pub(crate) name: Option<String>,
18 /// Extra claims by crate user
19 #[serde(flatten)]
20 pub(crate) extra: T,
21}
22
23impl<T> IdToken<T>
24where
25 T: serde::de::DeserializeOwned,
26{
27 /// Decode IdToken from JWS string
28 /// Warning: This function does not validate JWS signature.
29 /// You can use this function for "code flow" only.
30 pub(crate) fn decode_without_jws_validation(
31 jws: &str,
32 ) -> Result<Self, AuthenticationFailedError> {
33 use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD};
34
35 // Decode JWT
36 let mut jws_elm = jws.split('.');
37 let _jws_header = jws_elm.next();
38 let jws_payload = jws_elm.next();
39 let _jws_sign = jws_elm.next();
40
41 if let Some(jws_payload) = jws_payload {
42 let json_str = URL_SAFE_NO_PAD.decode(jws_payload)?;
43 Ok(serde_json::from_slice(&json_str)?)
44 } else {
45 // Invalid JWS structure
46 Err(AuthenticationFailedError::JwsDecodeError)
47 }
48 }
49}
50
51// expose ID Token values
52impl<T> IdToken<T> {
53 /// ID Token issuer
54 pub fn issuer(&self) -> &str {
55 &self.iss
56 }
57
58 /// Subject Identifier.
59 /// A locally unique and never reassigned identifier within the Issuer for the End-User.
60 pub fn subject(&self) -> &str {
61 &self.sub
62 }
63
64 /// End-User's preferred e-mail address.
65 pub fn email(&self) -> Option<&str> {
66 self.email.as_deref()
67 }
68
69 /// End-User's full name in displayable form including all name parts,
70 /// possibly including titles and suffixes,
71 /// ordered according to the End-User's locale and preferences.
72 pub fn name(&self) -> Option<&str> {
73 self.name.as_deref()
74 }
75
76 /// Extra claims
77 pub fn extra(&self) -> &T {
78 &self.extra
79 }
80}