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}