wasmer_cli/commands/auth/login/
auth_server.rs1use super::AuthorizationState;
2use http_body_util::BodyExt;
3use hyper::{Request, Response, StatusCode, body::Incoming};
4use reqwest::{Body, Method};
5use tokio::net::TcpListener;
6
7#[derive(Clone)]
9pub(super) struct BrowserAuthContext {
10 pub server_shutdown_tx: tokio::sync::mpsc::Sender<bool>,
11 pub token_tx: tokio::sync::mpsc::Sender<AuthorizationState>,
12}
13
14#[derive(Clone, Debug, serde::Deserialize)]
16#[serde(rename_all = "lowercase")]
17pub(super) enum TokenStatus {
18 Cancelled,
20 Authorized,
22}
23
24#[inline]
25pub(super) async fn setup_listener() -> Result<(TcpListener, String), anyhow::Error> {
26 let listener = TcpListener::bind("127.0.0.1:0").await?;
27 let addr = listener.local_addr()?;
28 let port = addr.port();
29
30 let server_url = format!("http://localhost:{port}");
31
32 Ok((listener, server_url))
33}
34
35#[derive(Clone, Debug, serde::Deserialize)]
39pub(super) struct ValidatedNonceOutput {
40 pub token: Option<String>,
42 pub status: TokenStatus,
44}
45
46pub(super) async fn service_router(
47 context: BrowserAuthContext,
48 req: Request<Incoming>,
49) -> Result<Response<Body>, anyhow::Error> {
50 match *req.method() {
51 Method::OPTIONS => preflight(req).await,
52 Method::POST => handle_post_save_token(context, req).await,
53 _ => handle_unknown_method(context).await,
54 }
55}
56
57async fn preflight(_: Request<Incoming>) -> Result<Response<Body>, anyhow::Error> {
58 let response = Response::builder()
59 .status(http::StatusCode::OK)
60 .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Headers", "Content-Type")
62 .header("Access-Control-Allow-Methods", "POST, OPTIONS")
63 .body(Body::default())?;
64 Ok(response)
65}
66
67async fn handle_post_save_token(
68 context: BrowserAuthContext,
69 req: Request<Incoming>,
70) -> Result<Response<Body>, anyhow::Error> {
71 let BrowserAuthContext {
72 server_shutdown_tx,
73 token_tx,
74 } = context;
75 let (.., body) = req.into_parts();
76 let body = body.collect().await?.to_bytes();
77
78 let ValidatedNonceOutput {
79 token,
80 status: token_status,
81 } = serde_json::from_slice::<ValidatedNonceOutput>(&body)?;
82
83 let (response_message, parse_failure) = match token_status {
85 TokenStatus::Cancelled => {
86 token_tx
87 .send(AuthorizationState::Cancelled)
88 .await
89 .expect("Failed to send token");
90
91 ("Token Cancelled by the user", false)
92 }
93 TokenStatus::Authorized => {
94 if let Some(token) = token {
95 token_tx
96 .send(AuthorizationState::TokenSuccess(token.clone()))
97 .await
98 .expect("Failed to send token");
99 ("Token Authorized", false)
100 } else {
101 ("Token not found", true)
102 }
103 }
104 };
105
106 server_shutdown_tx
107 .send(true)
108 .await
109 .expect("Failed to send shutdown signal");
110
111 let status = if parse_failure {
112 StatusCode::BAD_REQUEST
113 } else {
114 StatusCode::OK
115 };
116
117 Ok(Response::builder()
118 .status(status)
119 .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Headers", "Content-Type")
121 .header("Access-Control-Allow-Methods", "POST, OPTIONS")
122 .body(Body::from(response_message))?)
123}
124
125async fn handle_unknown_method(
126 context: BrowserAuthContext,
127) -> Result<Response<Body>, anyhow::Error> {
128 let BrowserAuthContext {
129 server_shutdown_tx,
130 token_tx,
131 } = context;
132
133 token_tx
134 .send(AuthorizationState::UnknownMethod)
135 .await
136 .expect("Failed to send token");
137
138 server_shutdown_tx
139 .send(true)
140 .await
141 .expect("Failed to send shutdown signal");
142
143 Ok(Response::builder()
144 .status(StatusCode::METHOD_NOT_ALLOWED)
145 .body(Body::from("Method not allowed"))?)
146}