1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
use std::collections::HashSet;

use jsonwebtoken::Algorithm;

/// Defines the jwt validation parameters (with defaults simplifying configuration).
pub struct Validation {
    /// Add some leeway (in seconds) to the `exp` and `nbf` validation to
    /// account for clock skew.
    ///
    /// Defaults to `60`.
    pub leeway: u64,
    /// Whether to validate the `exp` field.
    ///
    /// Defaults to `true`.
    pub validate_exp: bool,
    /// Whether to validate the `nbf` field.
    ///
    /// Defaults to `false`.
    pub validate_nbf: bool,
    /// If it contains a value, the validation will check that the `aud` claim value is in the values provided.
    ///
    /// Defaults to `None`.
    pub aud: Option<Vec<String>>,
    /// If it contains a value, the validation will check that the `iss` claim value is in the values provided.
    ///
    /// Defaults to `None`.
    pub iss: Option<Vec<String>>,

    /// Whether to validate the JWT signature. Very insecure to turn that off!
    ///
    /// Defaults to true.
    pub validate_signature: bool,

    /// Accepted algorithms
    ///
    /// If empty anly the algorithms matching key will be authorized
    pub algs: Vec<Algorithm>,
}

impl Validation {
    /// new Validation with default values
    pub fn new() -> Self {
        Default::default()
    }

    /// check that the `iss` claim is a member of the values provided
    pub fn iss<T: ToString>(mut self, items: &[T]) -> Self {
        self.iss = Some(items.iter().map(|x| x.to_string()).collect());

        self
    }

    /// check that the `aud` claim is a member of the items provided
    pub fn aud<T: ToString>(mut self, items: &[T]) -> Self {
        self.aud = Some(items.iter().map(|x| x.to_string()).collect());

        self
    }

    /// enables or disables exp validation
    pub fn exp(mut self, val: bool) -> Self {
        self.validate_exp = val;

        self
    }

    /// enables or disables nbf validation
    pub fn nbf(mut self, val: bool) -> Self {
        self.validate_nbf = val;

        self
    }

    /// Add some leeway (in seconds) to the `exp` and `nbf` validation to
    /// account for clock skew.
    pub fn leeway(mut self, value: u64) -> Self {
        self.leeway = value;

        self
    }

    /// Whether to validate the JWT cryptographic signature
    /// Very insecure to turn that off, only do it if you know what you're doing.
    pub fn disable_validation(mut self) -> Self {
        self.validate_signature = false;

        self
    }

    /// Authorized algorithms.
    ///
    /// If no algs are supplied default algs for the key will be used
    /// (example for a EC key, algs = [ES256, ES384]).
    pub fn algs(mut self, algs: Vec<Algorithm>) -> Self {
        self.algs = algs;

        self
    }

    pub(crate) fn to_jwt_validation(&self, default_algs: &[Algorithm]) -> jsonwebtoken::Validation {
        let required_claims = if self.validate_exp {
            let mut claims = HashSet::with_capacity(1);
            claims.insert("exp".to_owned());
            claims
        } else {
            HashSet::with_capacity(0)
        };

        let aud = self.aud.clone().map(HashSet::from_iter);
        let iss = self.iss.clone().map(HashSet::from_iter);

        let mut jwt_validation = jsonwebtoken::Validation::default();

        jwt_validation.required_spec_claims = required_claims;
        jwt_validation.leeway = self.leeway;
        jwt_validation.validate_exp = self.validate_exp;
        jwt_validation.validate_nbf = self.validate_nbf;
        jwt_validation.iss = iss;
        jwt_validation.aud = aud;
        jwt_validation.sub = None;
        jwt_validation.algorithms = if self.algs.is_empty() {
            default_algs.to_owned()
        } else {
            self.algs.clone()
        };
        if !self.validate_signature {
            jwt_validation.insecure_disable_signature_validation();
        }

        jwt_validation
    }
}

impl Default for Validation {
    fn default() -> Self {
        Validation {
            leeway: 60,

            validate_exp: true,
            validate_nbf: false,

            iss: None,
            aud: None,

            validate_signature: true,
            algs: vec![],
        }
    }
}