Skip to main content

wasm_oidc_plugin/
responses.rs

1// base64
2use {
3    base64::engine::general_purpose::URL_SAFE_NO_PAD as base64engine_urlsafe, base64::Engine as _,
4};
5
6// jwt_simple (upstream / superboring)
7use jwt_simple::{
8    claims::NoCustomClaims,
9    prelude::{RSAPublicKeyLike, VerificationOptions},
10    Error,
11};
12
13// log
14use log::{debug, info};
15
16// serde
17use serde::Deserialize;
18use url::Url;
19
20/// RSA modulus longer than 4096 bits is 512+ bytes in raw form.
21const RSA_MODULUS_BYTES_4096: usize = 512;
22
23/// [OpenID Connect Discovery Response](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig)
24#[derive(Deserialize, Debug)]
25pub struct OpenIdDiscoveryResponse {
26    /// The issuer of the OpenID Connect Provider
27    pub issuer: String,
28    /// The authorization endpoint to start the code flow
29    pub authorization_endpoint: Url,
30    /// The token endpoint to exchange the code for a token
31    pub token_endpoint: Url,
32    /// The URL to logout the user
33    pub end_session_endpoint: Option<Url>,
34    /// The jwks uri to load the jwks response from
35    pub jwks_uri: Url,
36}
37
38#[derive(Deserialize, Debug)]
39/// [JWKs response](https://tools.ietf.org/html/rfc7517)
40/// Contains a list of keys that are retrieved from the jwks uri
41pub struct JWKsResponse {
42    /// The keys of the jwks response, see `JWK`
43    pub keys: Vec<JsonWebKey>,
44}
45
46/// [JWK](https://tools.ietf.org/html/rfc7517)
47/// Define the structure of each key type that are retrieved from the jwks uri
48#[derive(Deserialize, Debug)]
49#[serde(tag = "alg")]
50pub enum JsonWebKey {
51    /// A RSA Key with RS256 algorithm
52    RS256 {
53        /// The key type like RSA
54        kty: String,
55        /// The Public Keys Component n, the modulus
56        n: String,
57        /// The Public Keys Component e, the exponent
58        e: String,
59    },
60    // Add more key types here
61}
62
63/// Enum that holds the public keys used to validate ID Tokens.
64/// Upstream jwt-simple for normal keys; fork for RSA moduli > 4096 bits.
65#[derive(Clone, Debug)]
66pub enum SigningKey {
67    RS256PublicKey(Box<jwt_simple::algorithms::RS256PublicKey>),
68    RS256PublicKeyLarge(Box<jwt_simple_legacy::algorithms::RS256PublicKey>),
69}
70
71impl SigningKey {
72    /// Returns `Ok(())` if the token verifies against this key.
73    pub fn verify_token(&self, token: &str, options: VerificationOptions) -> Result<(), Error> {
74        match self {
75            SigningKey::RS256PublicKey(key) => key
76                .verify_token::<NoCustomClaims>(token, Some(options))
77                .map(|_| ()),
78            SigningKey::RS256PublicKeyLarge(key) => {
79                use jwt_simple_legacy::prelude::RSAPublicKeyLike as _;
80                // Cannot use `.into()`: upstream and fork are different crates with the same type names.
81                key.verify_token::<jwt_simple_legacy::claims::NoCustomClaims>(
82                    token,
83                    Some(jwt_simple_legacy::prelude::VerificationOptions {
84                        allowed_issuers: options.allowed_issuers,
85                        allowed_audiences: options.allowed_audiences,
86                        ..Default::default()
87                    }),
88                )
89                .map(|_| ())
90            }
91        }
92    }
93}
94
95impl From<JsonWebKey> for SigningKey {
96    fn from(key: JsonWebKey) -> Self {
97        match key {
98            JsonWebKey::RS256 { kty, n, e, .. } => {
99                if kty != "RSA" {
100                    debug!("key is not of type RSA although alg is RS256");
101                }
102
103                let n_dec = base64engine_urlsafe.decode(n).unwrap();
104                let e_dec = base64engine_urlsafe.decode(e).unwrap();
105
106                if n_dec.len() > RSA_MODULUS_BYTES_4096 {
107                    info!("RSA modulus >4096 bits; using jwt-simple-fork");
108                    SigningKey::RS256PublicKeyLarge(Box::new(
109                        jwt_simple_legacy::algorithms::RS256PublicKey::from_components(
110                            &n_dec, &e_dec,
111                        )
112                        .expect("failed to parse large RS256 public key"),
113                    ))
114                } else {
115                    info!("loaded RS256 public key");
116                    SigningKey::RS256PublicKey(Box::new(
117                        jwt_simple::algorithms::RS256PublicKey::from_components(&n_dec, &e_dec)
118                            .expect("failed to parse RS256 public key"),
119                    ))
120                }
121            }
122        }
123    }
124}
125
126/// Struct that defines how the callback looks like to serialize it better with serde
127#[derive(Deserialize, Debug)]
128pub struct CodeCallback {
129    /// The code that is returned from the authorization endpoint
130    pub code: String,
131    /// The state that is returned from the authorization endpoint
132    pub state: String,
133}
134
135/// Struct that defines how the callback looks like to serialize it better with serde
136#[derive(Deserialize, Debug)]
137pub struct ProviderSelectionCallback {
138    /// The name of the provider that the user selected
139    pub authorize_with_provider: String,
140    /// The return_to path that the user should be redirected to after the provider selection
141    pub return_to: String,
142}