pubhubs/api/mod.rs
1//! Types describing the pubhubs API
2//!
3//! The different endpoints offered by the PubHubs servers and hubs are described by types
4//! implementing the [`EndpointDetails`] trait.
5//!
6//! # Overview for web clients
7//!
8//! For reference, the flows described below to enter pubhubs, to enter a hub and to obtain a pubhubs card
9//! are also implemented by the `pubhubs enter` CLI tool see [`crate::cli`], which
10//! is usually invoked via `cargo run enter`.
11//!
12//! ## Entering pubhubs
13//!
14//! 1. Everything starts with the global client, knowing only the url of pubhubs central (PHC),
15//! obtaining general information about the rest of the pubhubs environment
16//! from the [`phc::user::WelcomeEP`] endpoint of PHC, including, for example, the url of
17//! the authentication server and transcryptor.
18//!
19//! 2. Next the user authenticates towards the
20//! authentication server in order to obtain **[`Attr`]ibutes**.
21//!
22//! - The global client first gets the available authentication methods via [`auths::WelcomeEP`], and
23//! - then obtains the attributes using the [`auths::AuthStartEP`]
24//! and [`auths::AuthCompleteEP`] endpoints.
25//!
26//! 3. Using those [`Attr`]ibutes the global client can 'enter' PubHubs via the
27//! [`phc::user::EnterEP`], registering a new account if needed.
28//! or logging into an existing account. The result of entering PubHubs is not
29//! a session cookie, but an **[`phc::user::AuthToken`]** that must be passed along
30//! in the `Authorization` header of most subsequent requests from the global client to PHC.
31//!
32//! 4. After having entered pubhubs, the global client retrieves details on the [`phc::user::UserState`] via
33//! the [`phc::user::StateEP`] endpoint (authenticating using the previously obtained auth token).
34//! The user state includes (among other details), a list of stored **user objects**,
35//! [`phc::user::UserState::stored_objects`], which can be retrieved using
36//! [`phc::user::GetObjectEP`] (and stored using [`phc::user::OverwriteObjectEP`] and
37//! [`phc::user::NewObjectEP`]).
38//!
39//! 5. The contents of user objects should be encrypted by the global client. Indeed, there's
40//! no need for PHC to be able to read the contents of these user objects.
41//! But where can the global client store the 'user object key' used to encrypt
42//! these objects? Not in plaintext at PHC! Instead, the authentication server provides
43//! **attribute keys** via [`auths::AttrKeysEP`] with which the global client can encrypt its
44//! 'user object key' before storing it at PHC in a designated user object. Other user objects
45//! can then be encrypted using this user object key.
46//!
47//! ## Entering hubs
48//!
49//! Entering a hub is slightly more involved, as pubhubs central is not allowed to know the hub and
50//! the transcryptor is not allowed to know the user. These privacy guarantees can be achieved by
51//! clever use of elliptic curves as described in [the whitepaper](https://academic.oup.com/logcom/article/34/7/1377/7319213)
52//! The procedure below uses the same
53//! ideas but has been modified to prevent already generated pseudonyms from being linked
54//! when a cryptographically relevant quantum computer materializes.
55//! For the cryptographic details, see [`sso`].
56//!
57//! 1. The global client obtains a sealed **[`sso::PolymorphicPseudonymPackage`] (PPP)**
58//! from PHC via [`phc::user::PppEP`] and a **hub nonce** and **hub state**
59//! pair from the hub via [`hub::EnterStartEP`].
60//!
61//! 2. The global client sends the PPP, hub nonce and hub id to the transcryptor's
62//! [`tr::EhppEP`], which yields a sealed **[`sso::EncryptedHubPseudonymPackage`] (EHPP)**.
63//!
64//! 3. This EHPP is forwarded back to PHC, to the [`phc::user::HhppEP`], yielding a signed
65//! **[`sso::HashedHubPseudonymPackage`] (HHPP)**.
66//!
67//! 4. This HHPP is sent back by the global client to the hub's [`hub::EnterCompleteEP`],
68//! together with the **hub state**, which results in a Synapse access token to the hub.
69//!
70//! ## PubHubs card
71//!
72//! Pubhubs also issues yivi 'PubHubs cards'. To obtain one for the user:
73//!
74//! 1. First enter pubhubs, and get a signed [`phc::user::CardPseudPackage`] from PHC via
75//! [`phc::user::CardPseudEP`].
76//!
77//! 2. Then pass this to [`auths::CardEP`] to get:
78//!
79//! - An [`auths::CardResp::Success::attr`] [`Attr`]ibute that can be added to the user's
80//! account via the [`phc::user::EnterEP`] endpoint. Note that instead of passing an
81//! identifying attribute, one may also authenticate by putting auth token in the
82//! `Authorization` header.
83//!
84//! - An [`auths::CardResp::Success::issuance_request`] that can be used to start an issuance
85//! session with the authentication server's yivi server to issue the card to the user. It's
86//! important that this is done _after_ adding the card to user's yivi app lest the user
87//! might end up with a card that does not work.
88//!
89//! In the flow above, the user may have to scan two Yivi QR codes: one to disclosure attributes
90//! to enter pubhubs, and then another QR code to receive the PubHubs card. The disclosure and
91//! issuance Yivi sessions can be combined into one 'chained session' as follows.
92//!
93//! 1. When entering PubHubs, set [`auths::AuthStartReq::yivi_chained_session`] to `true`.
94//!
95//! This causes the yivi server to not immediately return the disclosure result to the browser,
96//! but first to the `auths::YIVI_NEXT_SESSION_PATH` endpoint of this authentication server.
97//!
98//! 2. When the authentication server receives this disclosure, it will keep the yivi server
99//! waiting, and will make the disclosure available via [`auths::YiviWaitForResultEP`].
100//! (The disclosure can unfortunately not be obtained in the regular way from the yivi server
101//! until the authentication server releases the yivi server.)
102//!
103//! 3. Using this disclosure, one can obtain [`Attr`]ibutes, enter PubHubs, obtain a PubHubs card,
104//! and add this card to the user's account, exactly as before.
105//!
106//! 4. But now instead of passing it directly to the yivi server (which involves scanning a second
107//! QR code), [`auths::CardResp::Success::issuance_request`] can be passed via the
108//! [`auths::YiviReleaseNextSessionEP`] endpoint of the authentication server to the waiting yivi
109//! server, which will cause the current disclosure session to be followed-up by the pubhubs
110//! card issuance session - without the user having to scan a second QR code.
111//!
112//! # Errors
113//!
114//! A request to a pubhubs endpoint may fail in several ways.
115//!
116//! 1. Errors that are par for the course are generally encoded in
117//! [`EndpointDetails::ResponseType`] types themselves. For example,
118//! [`phc::user::EnterResp::AccountDoesNotExist`] is returned when a user tries to log into an
119//! account that does not exist. It should always be clear to the caller how to act on these errors.
120//!
121//! 2. Other errors, such as unexpected errors, or errors caused by the caller breaking protocol
122//! in some avoidable manner are generally returned via the [`ErrorCode`]
123//! in the `Result<EndpointDetails::ResponseType, ErrorCode>`.
124//!
125//! - **[`ErrorCode::InternalError`]**: something unexpected went wrong internally.
126//! Consult the logs of the server for more details. Retrying the request is not
127//! recommended.
128//!
129//! - **[`ErrorCode::PleaseRetry`]**
130//! just wait a moment, and retry the same request.
131//!
132//! - **[`ErrorCode::BadRequest`]**: there's something unexpected is wrong with the request - do not
133//! retransmit the same request.
134//!
135//! 3. It may, however, happen that a request is rejected before it reaches our code, for example,
136//! by the HTTP framework [`actix_web`] or by the reverse proxy. One may in that case encounter a
137//! HTTP status code (or even a TCP/TLS disconnect). Notable HTTP status codes are:
138//!
139//! - **400 - Bad Request** Occurs, for example, when the json in the request body cannot be deserialized
140//! to the [`EndpointDetails::RequestType`].
141//!
142//! - **502 - Bad Gateway** Occurs, for example, when one of the servers is (temporarily)
143//! down. The client should try the same request again.
144//!
145//! 4. A response may also be rejected when it arrives at a browser, for example, due to improperly set
146//! [Cross-Origin Resource Sharing] headers.
147//!
148//! [Cross-Origin Resource Sharing]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CORS
149//! [`Attr`]: crate::attr::Attr
150//!
151//! # Changelog of breaking changes
152//!
153//! ## 2025-06-25
154//! - Removed `Expired` and `InvalidSignature` error codes.
155//! - Added [`phc::user::EnterResp::RetryWithNewIdentifyingAttr`] and
156//! [`phc::user::EnterResp::RetryWithNewAddAttr`] variants.
157//!
158//! ## 2025-07-14
159//! - Renamed `phc::user::EnterResp::Entered::auth_token` to
160//! [`phc::user::EnterResp::Entered::auth_token_package`], and changed its type,
161//! replacing [`phc::user::AuthToken`]
162//! with [`phc::user::AuthTokenPackage`] (which contains an expiry date). Instead of
163//! `{ "Ok": "AuThToken..." }` the `auth_token(_package)` field will now
164//! be of the form `{ "Ok": { "auth_token": "AuThToken", "expires": 123456789 } }`.
165mod common;
166pub use common::*;
167mod signed;
168pub use signed::*;
169mod sealed;
170pub use sealed::*;
171
172mod discovery;
173pub use discovery::*;
174
175#[cfg(test)]
176mod wire_compat;
177
178pub mod admin;
179
180pub mod auths;
181pub mod hub;
182pub mod phc;
183pub mod server;
184pub mod tr;
185
186pub mod sso;
187
188pub use crate::misc::jwt::NumericDate;