jose/jws/
builder.rs

1use crate::{
2    format::Format,
3    header::{self, HeaderValue, JoseHeaderBuilder, JoseHeaderBuilderError},
4    jwa::JsonWebSigningAlgorithm,
5    JoseHeader, JsonWebSignature,
6};
7
8/// Builds a [`JsonWebSignature`] with custom header parameters.
9#[derive(Debug)]
10pub struct JsonWebSignatureBuilder<F: Format> {
11    header: Option<Result<F::JwsHeader, JoseHeaderBuilderError>>,
12}
13
14impl<F: Format> JsonWebSignatureBuilder<F> {
15    pub(super) fn new() -> Self {
16        Self { header: None }
17    }
18
19    /// Configures the custom header for this [`JsonWebSignature`].
20    ///
21    /// For [`Compact`](crate::format::Compact) and
22    /// [`JsonFlattened`](crate::format::JsonFlattened) format, this method
23    /// will set the single protected, and unprotected header if JSON flattened,
24    /// header.
25    ///
26    /// ## Support for empty protected headers
27    ///
28    /// The [JWS RFC] allows for the protected header to be empty, and instead
29    /// supply all necessary parameters in the unprotected header. By
30    /// default, the `jose` crate will overwrite the `alg` field (and
31    /// optionally `kid` field) in the protected header, with the signing
32    /// algorithm used in the signing operation.
33    /// To achieve that the `alg` field is set on the unprotected header, one
34    /// must set the `alg`
35    /// field to `HeaderValue::Protected(JsonWebSigningAlgorithm::None)`
36    /// manually.
37    ///
38    /// However, you must note, that this feature is not supported for the
39    /// [`Compact`](crate::format::Compact) format, becuase that format can only
40    /// have a protected header.
41    ///
42    /// ```
43    /// # use jose::{format::*, jws::*, header::HeaderValue, jwa::*};
44    /// # fn main() {
45    /// let jws = JsonWebSignature::<JsonFlattened, _>::builder()
46    ///     .header(|b| b.algorithm(HeaderValue::Unprotected(JsonWebSigningAlgorithm::None)))
47    ///     .build(())
48    ///     .unwrap();
49    /// # }
50    /// ```
51    ///
52    /// [JWS RFC]: <https://datatracker.ietf.org/doc/html/rfc7515>
53    pub fn header<
54        CB: FnOnce(JoseHeaderBuilder<F, header::Jws>) -> JoseHeaderBuilder<F, header::Jws>,
55    >(
56        mut self,
57        callback: CB,
58    ) -> Self {
59        let mut header = match self.header {
60            Some(Ok(hdr)) => Ok(hdr),
61
62            // when there was an error setting the previous header,
63            // do not overwrite the header value, because we want
64            // to keep the error and report it in the `build` method
65            Some(Err(_)) => return self,
66
67            // this `Err` value is just used as a placeholder to be replaced
68            None => Err(JoseHeaderBuilderError::MissingAlgorithm),
69        };
70
71        let builder = JoseHeader::<F, header::Jws>::builder()
72            .algorithm(HeaderValue::Protected(JsonWebSigningAlgorithm::None));
73        let builder = callback(builder);
74
75        F::finalize_jws_header_builder(&mut header, builder);
76        self.header = Some(header);
77
78        self
79    }
80
81    /// Finalizes this builder and returns the creates [`JsonWebSignature`].
82    ///
83    /// # Errors
84    ///
85    /// Fails if the supplied header parameters were invalid.
86    pub fn build<T>(self, payload: T) -> Result<JsonWebSignature<F, T>, JoseHeaderBuilderError> {
87        let header = match self.header {
88            Some(hdr) => hdr?,
89            None => {
90                let default_header = JoseHeader::<F, header::Jws>::builder()
91                    .algorithm(HeaderValue::Protected(JsonWebSigningAlgorithm::None));
92
93                let mut header = Err(JoseHeaderBuilderError::MissingAlgorithm);
94                F::finalize_jws_header_builder(&mut header, default_header);
95                header?
96            }
97        };
98
99        Ok(JsonWebSignature::new(header, payload))
100    }
101}