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}