summaryrefslogtreecommitdiff
path: root/server/src/main.rs
diff options
context:
space:
mode:
authorKjetil Orbekk <kj@orbekk.com>2022-10-02 11:11:36 -0400
committerKjetil Orbekk <kj@orbekk.com>2022-10-02 11:11:36 -0400
commitfdafa6d17ea884cc75a0d616899e1db84ae4ba0d (patch)
tree1419e2c7bda303c305e98f2f2f9016f31385584c /server/src/main.rs
parentfe58cb13a8dfe9dd2feb994cd3e53094e73b2874 (diff)
Rocket setup
Diffstat (limited to 'server/src/main.rs')
-rw-r--r--server/src/main.rs160
1 files changed, 129 insertions, 31 deletions
diff --git a/server/src/main.rs b/server/src/main.rs
index f751c4d..aa615ea 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -1,53 +1,151 @@
-#[macro_use] extern crate rocket;
+#[macro_use]
+extern crate rocket;
-#[get("/")]
-fn index() -> &'static str {
- "Hello, World!"
+use std::result::Result;
+use chrono::{DateTime, Utc};
+use rocket::http::uri::Reference;
+use rocket::http::{Cookie, CookieJar,Status};
+use rocket::response::Redirect;
+use rocket::request::{self, FromRequest};
+use rocket::Request;
+use serde::{Deserialize, Serialize};
+use rocket::outcome::Outcome;
+
+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,
}
-#[rocket::main]
-async fn main() -> Result<(), anyhow::Error> {
- dotenv::dotenv().ok();
- env_logger::init();
- log::debug!("hello");
+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);
+ Ok(User {})
+ }
+}
- use openidconnect::core::{
- CoreAuthenticationFlow, CoreClient, CoreProviderMetadata, CoreResponseType,
- CoreUserInfoClaims,
- };
- use openidconnect::{
- AccessTokenHash, AuthenticationFlow, AuthorizationCode, ClientId, ClientSecret, CsrfToken,
- IssuerUrl, Nonce, PkceCodeChallenge, RedirectUrl, Scope,
- };
+#[rocket::async_trait]
+impl<'r> FromRequest<'r> for User {
+ type Error = anyhow::Error;
- use openidconnect::reqwest::async_http_client;
- use openidconnect::url::Url;
+ 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(),
+ }
+}
+async fn keycloak_client() -> CoreClient {
// // Use OpenID Connect Discovery to fetch the provider metadata.
- use openidconnect::{OAuth2TokenResponse, TokenResponse};
let provider_metadata = CoreProviderMetadata::discover_async(
- IssuerUrl::new("https://auth.orbekk.com/realms/test".to_string())?,
+ IssuerUrl::new("https://auth.orbekk.com/realms/test".to_string()).unwrap(),
async_http_client,
- ).await?;
+ )
+ .await
+ .unwrap();
- let client =
- CoreClient::from_provider_metadata(
+ let client = CoreClient::from_provider_metadata(
provider_metadata,
ClientId::new("test-client".to_string()),
- Some(ClientSecret::new("EbIMIpGnYPrG1GBl6eZtVM5zIhiuu5p1".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())?);
+ .set_redirect_uri(
+ RedirectUrl::new("https://bridge.orbekk.com/keycloak-callback".to_string()).unwrap(),
+ );
+
+ client
+}
- let (auth_url, csrf_token, nonce) = client
- .authorize_url(AuthenticationFlow::<CoreResponseType>::AuthorizationCode,
- CsrfToken::new_random,
- Nonce::new_random)
+#[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();
+ log::debug!("hello");
- rocket::build().mount("/", routes![index]).launch().await;
+ rocket::build()
+ .mount("/", routes![index, login, keycloak_callback])
+ .launch()
+ .await;
Ok(())
}