diff options
Diffstat (limited to 'server-old/src/main.rs')
-rw-r--r-- | server-old/src/main.rs | 168 |
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(()) +} |