getting email from gitlab works

This commit is contained in:
Jacob Lifshay 2024-04-08 04:52:06 -07:00
parent 5eabc89280
commit 7527459446
Signed by: programmerjake
SSH key fingerprint: SHA256:B1iRVvUJkvd7upMIiMqn6OyxvD2SgJkAH3ZnUOj6z+c
6 changed files with 257 additions and 63 deletions

View file

@ -1,22 +1,21 @@
use crate::config::{Config, OIDCProvider, OIDCProviderState};
use crate::{
client::async_http_client,
config::{Config, OIDCProvider},
};
use actix_session::Session;
use actix_web::{
cookie::Cookie,
get,
http::{
header::{ContentType, LOCATION},
uri::PathAndQuery,
StatusCode, Uri,
},
http::header::{ContentType, LOCATION},
web::{self, ServiceConfig},
HttpResponse, HttpResponseBuilder, Responder,
HttpResponse, Responder,
};
use eyre::{ensure, Context, OptionExt};
use openidconnect::{
core::CoreAuthenticationFlow, http::HeaderValue, reqwest::async_http_client, url::Url,
AuthorizationCode, CsrfToken, EndUserEmail, Nonce, RedirectUrl, TokenResponse,
core::{CoreAuthenticationFlow, CoreUserInfoClaims},
AuthorizationCode, CsrfToken, EndUserEmail, Nonce, OAuth2TokenResponse,
};
use serde::{Deserialize, Serialize};
use std::{borrow::Cow, fmt::Write, marker::PhantomData};
use std::marker::PhantomData;
use tinytemplate::TinyTemplate;
pub trait DynTemplate {
@ -85,6 +84,14 @@ templates! {
pub struct SubscriptionLoggedOutTemplate {
pub providers: Vec<SubscriptionLoggedOutTemplateProviders>,
}
#[text = r#"<head><title>Subscription</title></head>
<body>
<p>Logged in as {email}</p>
</body>"#]
#[derive(Debug, Serialize)]
pub struct SubscriptionLoggedInTemplate {
pub email: EndUserEmail,
}
}
pub fn make_templates() -> TinyTemplate<'static> {
@ -146,49 +153,42 @@ pub async fn callback(
if SessionState::get(&session).is_some() {
return resp;
}
let Ok(Some(csrf_token)) = session.get::<CsrfToken>(CSRF_TOKEN) else {
return resp;
let body = async {
let csrf_token = session
.get::<CsrfToken>(CSRF_TOKEN)
.context("invalid csrf token")?
.ok_or_eyre("missing csrf token")?;
let nonce = session
.get::<Nonce>(NONCE)
.context("invalid nonce")?
.ok_or_eyre("missing nonce")?;
session.remove(CSRF_TOKEN);
session.remove(NONCE);
ensure!(
csrf_token.secret() == query.state.secret(),
"csrf token doesn't match"
);
let provider = config.oidc.get(&path.0).ok_or_eyre("unknown provider")?;
let token = provider
.state()
.client
.exchange_code(query.into_inner().code)
.request_async(async_http_client)
.await?;
let user_info: CoreUserInfoClaims = provider
.state()
.client
.user_info(token.access_token().clone(), None)?
.request_async(async_http_client)
.await?;
let email = user_info.email().ok_or_eyre("no email provided")?.clone();
SessionState { email }.set(&session);
Ok(())
};
let Ok(Some(nonce)) = session.get::<Nonce>(NONCE) else {
return resp;
};
session.remove(CSRF_TOKEN);
session.remove(NONCE);
if csrf_token.secret() != query.state.secret() {
return resp;
let result: eyre::Result<()> = body.await;
if let Err(e) = result {
log::warn!("callback error: {e}");
}
let Some(provider) = config.oidc.get(&path.0) else {
return resp;
};
let token = match provider
.state()
.client
.exchange_code(query.into_inner().code)
.request_async(async_http_client)
.await
{
Ok(token) => token,
Err(e) => {
log::warn!("Error getting token: {e}");
return resp;
}
};
dbg!(&token);
let claims = match token
.id_token()
.unwrap()
.claims(&provider.state().client.id_token_verifier(), &nonce)
{
Ok(claims) => claims,
Err(e) => {
log::warn!("Error verifying token: {e}");
return resp;
}
};
let Some(email) = claims.email().cloned() else {
return resp;
};
SessionState { email }.set(&session);
resp
}
@ -227,7 +227,10 @@ pub async fn login(
#[get("/subscription")]
pub async fn subscription(config: web::Data<Config>, session: Session) -> impl Responder {
if let Some(SessionState { email }) = SessionState::get(&session) {
todo!()
let mut template = SubscriptionLoggedInTemplate { email };
HttpResponse::Ok()
.content_type(ContentType::html())
.body(template.render().expect("rendering can't fail"))
} else {
let mut template = SubscriptionLoggedOutTemplate { providers: vec![] };
for (name, provider) in config.oidc.iter() {

18
src/client.rs Normal file
View file

@ -0,0 +1,18 @@
use openidconnect::{reqwest::Error, HttpRequest, HttpResponse};
pub async fn async_http_client(
request: HttpRequest,
) -> Result<HttpResponse, Error<reqwest::Error>> {
let method = request.method.clone();
let url = request.url.clone();
log::debug!("making client request: {method} {url}");
let response = match openidconnect::reqwest::async_http_client(request).await {
Ok(v) => v,
Err(e) => return Err(e),
};
log::debug!(
"client request finished with {}: {method} {url}",
response.status_code
);
Ok(response)
}

View file

@ -1,3 +1,4 @@
use crate::client::async_http_client;
use clap::builder::{TryMapValueParser, TypedValueParser, ValueParserFactory};
use clio::CachedInput;
use eyre::Context;
@ -66,11 +67,9 @@ impl OIDCProvider {
self.state.as_ref().expect("resolve called by main")
}
pub async fn resolve(&mut self) -> eyre::Result<()> {
let provider_metadata = CoreProviderMetadata::discover_async(
self.issuer_url.clone(),
openidconnect::reqwest::async_http_client,
)
.await?;
let provider_metadata =
CoreProviderMetadata::discover_async(self.issuer_url.clone(), async_http_client)
.await?;
let client = CoreClient::from_provider_metadata(
provider_metadata.clone(),
self.client_id.clone(),

View file

@ -1,23 +1,22 @@
use crate::cli::{Listen, ListenFdSocket, ListenFdSockets};
use actix_session::{
config::{BrowserSession, SessionMiddlewareBuilder},
storage::CookieSessionStore,
SessionMiddleware,
};
use actix_session::{storage::CookieSessionStore, SessionMiddleware};
use actix_web::{cookie::Key, web, App, HttpServer};
use clap::Parser;
use listenfd::ListenFd;
mod app;
mod cli;
mod client;
mod config;
#[tokio::main]
async fn main() -> eyre::Result<()> {
env_logger::init();
let mut listenfd = ListenFd::from_env();
color_eyre::install()?;
let mut args = cli::CLI::parse();
args.resolve(&mut listenfd).await?;
log::info!("retrieved config from auth servers");
let config = web::Data::new(args.config);
let cookie_session_key = Key::generate();
let mut server = HttpServer::new(move || {
@ -49,6 +48,7 @@ async fn main() -> eyre::Result<()> {
};
}
}
log::info!("started server");
server.run().await?;
Ok(())
}