tiny_oidc_rp/
id_token.rs

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