summaryrefslogtreecommitdiff
path: root/server-old/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'server-old/src/main.rs')
-rw-r--r--server-old/src/main.rs168
1 files changed, 168 insertions, 0 deletions
diff --git a/server-old/src/main.rs b/server-old/src/main.rs
new file mode 100644
index 0000000..411ea47
--- /dev/null
+++ b/server-old/src/main.rs
@@ -0,0 +1,168 @@
+#[macro_use]
+extern crate rocket;
+
+use chrono::{DateTime, Utc};
+use rocket::fs::FileServer;
+use rocket::http::uri::Reference;
+use rocket::http::{Cookie, CookieJar, Status};
+use rocket::outcome::Outcome;
+use rocket::request::{self, FromRequest};
+use rocket::response::{Redirect, content};
+use rocket::Request;
+use serde::{Deserialize, Serialize};
+use std::result::Result;
+use rocket::serde::json::Json;
+
+use openidconnect::core::{
+ CoreAuthenticationFlow, CoreClient, CoreProviderMetadata, CoreResponseType, CoreUserInfoClaims,
+};
+use openidconnect::reqwest::async_http_client;
+use openidconnect::url::Url;
+use openidconnect::{
+ AccessTokenHash, AuthenticationFlow, AuthorizationCode, ClientId, ClientSecret, CsrfToken,
+ IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, RedirectUrl, Scope, TokenResponse,
+};
+
+const USER_COOKIE: &'static str = "user";
+
+#[derive(Serialize, Deserialize)]
+struct UserCookie {
+ access_token: openidconnect::AccessToken,
+ expiration: DateTime<Utc>,
+ refresh_token: openidconnect::RefreshToken,
+}
+
+struct User {}
+
+impl User {
+ async fn from_request_helper(req: &Request<'_>) -> Result<User, anyhow::Error> {
+ let cookie = req
+ .cookies()
+ .get_private(USER_COOKIE)
+ .ok_or(anyhow::anyhow!("no cookie"))?;
+ let user_cookie: UserCookie = serde_json::from_str(cookie.value())?;
+ let client = keycloak_client().await;
+
+ let token = client
+ .exchange_refresh_token(&user_cookie.refresh_token)
+ .request_async(async_http_client)
+ .await?;
+ let user_info: CoreUserInfoClaims = client
+ .user_info(token.access_token().clone(), None)?
+ .request_async(async_http_client)
+ .await?;
+ log::info!("Got user_info: {:?}", user_info);
+ Ok(User {})
+ }
+}
+
+#[rocket::async_trait]
+impl<'r> FromRequest<'r> for User {
+ type Error = anyhow::Error;
+
+ async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
+ match User::from_request_helper(req).await {
+ Ok(user) => Outcome::Success(User {}),
+ Err(error) => Outcome::Failure((Status::Forbidden, error)),
+ }
+ }
+}
+
+#[get("/")]
+fn index(user: Option<User>) -> String {
+ match user {
+ None => "Not logged in".to_string(),
+ Some(user) => "Logged in".to_string(),
+ }
+}
+
+#[get("/test")]
+fn test() -> Json<String> {
+ Json(String::from("test"))
+}
+
+async fn keycloak_client() -> CoreClient {
+ // // Use OpenID Connect Discovery to fetch the provider metadata.
+ let provider_metadata = CoreProviderMetadata::discover_async(
+ IssuerUrl::new("https://auth.orbekk.com/realms/test".to_string()).unwrap(),
+ async_http_client,
+ )
+ .await
+ .unwrap();
+
+ let client = CoreClient::from_provider_metadata(
+ provider_metadata,
+ ClientId::new("test-client".to_string()),
+ Some(ClientSecret::new(
+ "EbIMIpGnYPrG1GBl6eZtVM5zIhiuu5p1".to_string(),
+ )),
+ )
+ // Set the URL the user will be redirected to after the authorization process.
+ .set_redirect_uri(
+ RedirectUrl::new("https://bridge.orbekk.com/keycloak-callback".to_string()).unwrap(),
+ );
+
+ client
+}
+
+#[get("/login")]
+async fn login() -> Redirect {
+ let (auth_url, csrf_token, nonce) = keycloak_client()
+ .await
+ .authorize_url(
+ AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
+ CsrfToken::new_random,
+ Nonce::new_random,
+ )
+ .add_scope(Scope::new("email".to_string()))
+ .add_scope(Scope::new("profile".to_string()))
+ .url();
+
+ log::info!("{:?}", auth_url);
+ Redirect::to(Reference::parse_owned(auth_url.into()).unwrap())
+}
+
+#[get("/keycloak-callback?<code>&<state>")]
+async fn keycloak_callback(jar: &CookieJar<'_>, code: &str, state: &str) -> Redirect {
+ // TODO: Validate state
+ let request_time = Utc::now();
+ let token = keycloak_client()
+ .await
+ .exchange_code(AuthorizationCode::new(code.to_string()))
+ .request_async(async_http_client)
+ .await
+ .unwrap();
+
+ log::info!("token: {:?}", token);
+ log::info!("access token {:?}", token.access_token().secret());
+ log::info!(
+ "refresh token {:?}",
+ token.refresh_token().unwrap().secret()
+ );
+ let expiration =
+ request_time + chrono::Duration::from_std(token.expires_in().unwrap()).unwrap();
+ jar.add_private(Cookie::new(
+ USER_COOKIE,
+ serde_json::to_string(&UserCookie {
+ access_token: token.access_token().clone(),
+ expiration,
+ refresh_token: token.refresh_token().unwrap().clone(),
+ })
+ .unwrap(),
+ ));
+
+ Redirect::to(uri!(index))
+}
+
+#[rocket::main]
+async fn main() -> Result<(), anyhow::Error> {
+ dotenv::dotenv().ok();
+ env_logger::init();
+
+ rocket::build()
+ .mount("/api", routes![index, test, login, keycloak_callback])
+ .mount("/", FileServer::from(std::env::var("WEBAPP_PATH").unwrap()))
+ .launch()
+ .await?;
+ Ok(())
+}