Skip to main content

didbot_serve/routes/
session.rs

1use super::*;
2
3/// Where `getSession` is served, which a DPoP proof binds to.
4const GET_SESSION_PATH: &str = "/xrpc/com.atproto.server.getSession";
5
6/// The handle atproto reports for an account whose handle does not resolve.
7const INVALID_HANDLE: &str = "handle.invalid";
8
9/// `GET /xrpc/com.atproto.server.getSession`
10///
11/// The account the presented credential signs in as: its DID, its handle,
12/// its DID document, and whether its repository is active. Takes the
13/// credentials the write routes take, an agent token or a DPoP-bound OAuth
14/// token. `active` and `status` are what `com.atproto.sync.getRepoStatus`
15/// reports for the same account. Validating a session is a use of it, so
16/// the e-stop refuses this under either setting.
17pub(crate) async fn get_session(State(state): State<AppState>, headers: HeaderMap) -> Response {
18    match session(&state, &headers) {
19        Ok(body) => Json(body).into_response(),
20        Err(err) => err.into_response(),
21    }
22}
23
24fn session(state: &AppState, headers: &HeaderMap) -> Result<serde_json::Value, ApiError> {
25    require_lifecycle(state, |p| p.serves_reads, "com.atproto.server.getSession")?;
26    state.estop.check_use(EstopRefusal::Operation)?;
27    let writer = auth::require_agent_token_or_dpop(
28        state.registry.as_ref(),
29        &state.oauth,
30        headers,
31        "GET",
32        GET_SESSION_PATH,
33    )?;
34    let did = writer.did();
35    let account = state
36        .registry
37        .account(did)
38        .ok_or_else(|| ApiError::repo_not_found(did))?;
39    let document = state.registry.did_document(account.did.host());
40    // Read out of the document, as `describeRepo` does, so this reports the
41    // handle a resolver would find.
42    let handle = document
43        .as_ref()
44        .and_then(didbot_identity::DidDocument::handle)
45        .filter(|handle| state.registry.handle_did(handle).as_deref() == Some(did))
46        .unwrap_or(INVALID_HANDLE);
47    let mut body = json!({ "did": did, "handle": handle });
48    if let Some(document) = &document {
49        body["didDoc"] = serde_json::to_value(document).map_err(|error| {
50            ApiError::new(
51                StatusCode::INTERNAL_SERVER_ERROR,
52                "DidDocumentUnavailable",
53                error.to_string(),
54            )
55        })?;
56    }
57    let status = account.sync_status();
58    body["active"] = json!(status.is_some_and(|status| status.active));
59    if let Some(reason) = status.and_then(|status| status.status) {
60        body["status"] = json!(reason.as_str());
61    }
62    debug!(did, handle, "answered getSession");
63    Ok(body)
64}