wire_e2e_identity/acquisition/
oidc_challenge.rs

1use rusty_jwt_tools::{jwk_thumbprint::JwkThumbprint, prelude::Pem};
2
3use super::{Result, X509CredentialAcquisition, states};
4use crate::{
5    acme::{RustyAcme, RustyAcmeError},
6    pki_env_hooks::{HttpHeader, HttpMethod},
7};
8
9impl X509CredentialAcquisition<states::DpopChallengeCompleted> {
10    /// Complete the OIDC challenge and get the certificate chain.
11    ///
12    /// Returns (signing keypair in PEM format, certificate chain).
13    /// The first certificate in the chain is the end-entity certificate,
14    /// i.e. the one certifying the public portion of the signing keypair.
15    pub async fn complete_oidc_challenge(self) -> Result<(Pem, Vec<Vec<u8>>)> {
16        let hooks = self.pki_env.hooks();
17
18        // Complete the OIDC challenge.
19        let oidc_challenge_token = &self.data.oidc_challenge.token;
20        let thumbprint = JwkThumbprint::generate(&self.acme_jwk, self.config.hash_alg)?.kid;
21        let key_auth = format!("{oidc_challenge_token}.{thumbprint}");
22
23        let url = &self.data.oidc_challenge.url;
24        let id_token = hooks
25            .authenticate(self.config.idp_url.clone(), key_auth, url.to_string())
26            .await?;
27
28        let oidc_challenge_request = RustyAcme::oidc_chall_request(
29            id_token,
30            &self.data.oidc_challenge,
31            &self.data.acme_account,
32            self.config.sign_alg,
33            &self.acme_kp,
34            self.data.nonce.clone(),
35        )?;
36        let (nonce, response) = self.acme_request(url, &oidc_challenge_request).await?;
37        let _ = RustyAcme::new_chall_response(response)?;
38
39        // Finalize the order. This generates a CSR (Certificate Signing Request) and
40        // sends it to the ACME server.
41        let finalize_request = RustyAcme::finalize_req(
42            &self.data.order,
43            &self.data.acme_account,
44            self.config.sign_alg,
45            &self.acme_kp,
46            &self.sign_kp,
47            nonce,
48        )?;
49        let (nonce, response) = self.acme_request(&self.data.order.finalize, &finalize_request).await?;
50        let finalize = RustyAcme::finalize_response(response)?;
51
52        // Get the certificate chain.
53        //
54        // See [RFC 8555 Section 7.4.2](https://www.rfc-editor.org/rfc/rfc8555.html#section-7.4.2).
55        let certificate_request = RustyAcme::certificate_req(
56            &finalize,
57            &self.data.acme_account,
58            self.config.sign_alg,
59            &self.acme_kp,
60            nonce,
61        )?;
62        let headers = vec![HttpHeader {
63            name: "content-type".into(),
64            value: "application/jose+json".into(),
65        }];
66        let body = serde_json::to_string(&certificate_request)?.into();
67        let response = hooks
68            .http_request(HttpMethod::Post, finalize.certificate.to_string(), headers, body)
69            .await?;
70        let response = String::from_utf8(response.body).map_err(|e| RustyAcmeError::from(e.utf8_error()))?;
71        let certificates = RustyAcme::certificate_response(response, self.data.order, self.config.hash_alg, None)?;
72        Ok((self.sign_kp, certificates))
73    }
74}