mirror of
https://github.com/Jackzmc/storage.git
synced 2025-05-05 20:33:20 +00:00
Working SSO test
This commit is contained in:
parent
60813de8cb
commit
97424ca524
2 changed files with 24 additions and 11 deletions
|
@ -17,5 +17,7 @@ openid_enabled = true
|
||||||
openid_issuer_url = "https://accounts.example.com"
|
openid_issuer_url = "https://accounts.example.com"
|
||||||
openid_client_id = ""
|
openid_client_id = ""
|
||||||
openid_client_secret = ""
|
openid_client_secret = ""
|
||||||
|
openid_claims = []
|
||||||
|
|
||||||
[smtp]
|
[smtp]
|
||||||
# TODO:
|
# TODO:
|
||||||
|
|
|
@ -3,24 +3,36 @@ use std::net::IpAddr;
|
||||||
use std::sync::{LazyLock, OnceLock};
|
use std::sync::{LazyLock, OnceLock};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
|
use log::warn;
|
||||||
use moka::future::Cache;
|
use moka::future::Cache;
|
||||||
use rocket::{get, post, uri};
|
use rocket::{get, post, uri};
|
||||||
use rocket::response::Redirect;
|
use rocket::response::Redirect;
|
||||||
use rocket_session_store::Session;
|
use rocket_session_store::Session;
|
||||||
use crate::guards::AuthUser;
|
use crate::guards::AuthUser;
|
||||||
use crate::SessionData;
|
use crate::SessionData;
|
||||||
use openidconnect::{reqwest, AccessTokenHash, AuthenticationFlow, AuthorizationCode, Client, ClientId, ClientSecret, CsrfToken, EmptyAdditionalClaims, HttpClientError, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, ProviderMetadata, RedirectUrl, Scope, StandardErrorResponse, TokenResponse};
|
use openidconnect::{reqwest, AccessTokenHash, AsyncHttpClient, AuthenticationFlow, AuthorizationCode, Client, ClientId, ClientSecret, CsrfToken, EmptyAdditionalClaims, HttpClientError, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, ProviderMetadata, RedirectUrl, Scope, StandardErrorResponse, TokenResponse};
|
||||||
use openidconnect::core::{CoreAuthDisplay, CoreAuthPrompt, CoreAuthenticationFlow, CoreClient, CoreGenderClaim, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm, CoreProviderMetadata, CoreTokenResponse, CoreUserInfoClaims};
|
use openidconnect::core::{CoreAuthDisplay, CoreAuthPrompt, CoreAuthenticationFlow, CoreClient, CoreGenderClaim, CoreJsonWebKey, CoreJweContentEncryptionAlgorithm, CoreProviderMetadata, CoreTokenResponse, CoreUserInfoClaims};
|
||||||
|
use openidconnect::http::HeaderValue;
|
||||||
|
use reqwest::header::HeaderMap;
|
||||||
// TODO: not have this lazy somehow, move to OnceLock and have fn to refresh it? (own module?)
|
// TODO: not have this lazy somehow, move to OnceLock and have fn to refresh it? (own module?)
|
||||||
// and/or also move to State<>
|
// and/or also move to State<>
|
||||||
|
|
||||||
static HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
static HTTP_CLIENT: LazyLock<reqwest::Client> = LazyLock::new(|| {
|
||||||
reqwest::ClientBuilder::new()
|
let mut headers = HeaderMap::new();
|
||||||
|
// TODO: pull from config.
|
||||||
|
// Set referrer as some providers (authentik) block POST w/o referrer
|
||||||
|
headers.insert("Referer", HeaderValue::from_static("http://localhost:8080"));
|
||||||
|
let mut builder = reqwest::ClientBuilder::new()
|
||||||
// Following redirects opens the client up to SSRF vulnerabilities.
|
// Following redirects opens the client up to SSRF vulnerabilities.
|
||||||
.redirect(reqwest::redirect::Policy::none())
|
.redirect(reqwest::redirect::Policy::none())
|
||||||
.build()
|
.default_headers(headers);
|
||||||
.expect("Client should build")
|
if var("DANGER_DEV_PROXY").is_ok() {
|
||||||
|
warn!("DANGER_DEV_PROXY set, requests are being proxied & ignoring certificates");
|
||||||
|
builder = builder
|
||||||
|
.proxy(reqwest::Proxy::https("https://localhost:8082").unwrap())
|
||||||
|
.danger_accept_invalid_certs(true)
|
||||||
|
};
|
||||||
|
builder.build().expect("Client should build")
|
||||||
});
|
});
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct SSOSessionData {
|
struct SSOSessionData {
|
||||||
|
@ -34,15 +46,14 @@ static SSO_SESSION_CACHE: LazyLock<Cache<IpAddr, SSOSessionData>> = LazyLock::ne
|
||||||
.max_capacity(100)
|
.max_capacity(100)
|
||||||
.build());
|
.build());
|
||||||
#[get("/auth/sso")]
|
#[get("/auth/sso")]
|
||||||
pub async fn page(session: Session<'_, SessionData>, ip: IpAddr) -> Redirect {
|
pub async fn page(ip: IpAddr) -> Redirect {
|
||||||
let s = session.get().await.unwrap().unwrap();
|
|
||||||
let http_client = HTTP_CLIENT.clone();
|
let http_client = HTTP_CLIENT.clone();
|
||||||
// FIXME: temp, remove
|
// FIXME: temp, remove
|
||||||
let provider_metadata = CoreProviderMetadata::discover_async(
|
let provider_metadata = CoreProviderMetadata::discover_async(
|
||||||
/* TODO: pull from config */
|
/* TODO: pull from config */
|
||||||
IssuerUrl::new(var("SSO_ISSUER_URL").expect("dev: missing sso url")).expect("bad issuer url"),
|
IssuerUrl::new(var("SSO_ISSUER_URL").expect("dev: missing sso url")).expect("bad issuer url"),
|
||||||
&http_client,
|
&http_client,
|
||||||
).await.expect("discovery failed");
|
).await.map_err(|e| e.to_string()).expect("discovery failed");
|
||||||
let client =
|
let client =
|
||||||
CoreClient::from_provider_metadata(
|
CoreClient::from_provider_metadata(
|
||||||
provider_metadata,
|
provider_metadata,
|
||||||
|
@ -65,8 +76,8 @@ pub async fn page(session: Session<'_, SessionData>, ip: IpAddr) -> Redirect {
|
||||||
)
|
)
|
||||||
// Set the desired scopes.
|
// Set the desired scopes.
|
||||||
// TODO: change scopes
|
// TODO: change scopes
|
||||||
.add_scope(Scope::new("read".to_string()))
|
.add_scope(Scope::new("email".to_string()))
|
||||||
.add_scope(Scope::new("write".to_string()))
|
.add_scope(Scope::new("name".to_string()))
|
||||||
// Set the PKCE code challenge.
|
// Set the PKCE code challenge.
|
||||||
.set_pkce_challenge(pkce_challenge)
|
.set_pkce_challenge(pkce_challenge)
|
||||||
.url();
|
.url();
|
||||||
|
@ -82,7 +93,7 @@ pub async fn page(session: Session<'_, SessionData>, ip: IpAddr) -> Redirect {
|
||||||
// process.
|
// process.
|
||||||
}
|
}
|
||||||
|
|
||||||
#[post("/auth/sso/cb?<state>&<code>")]
|
#[get("/auth/sso/cb?<code>&<state>")]
|
||||||
pub async fn callback(session: Session<'_, SessionData>, ip: IpAddr, code: String, state: String) -> Result<String, String> {
|
pub async fn callback(session: Session<'_, SessionData>, ip: IpAddr, code: String, state: String) -> Result<String, String> {
|
||||||
let session_data = SSO_SESSION_CACHE.remove(&ip).await.ok_or_else(|| "no sso session started".to_string())?;
|
let session_data = SSO_SESSION_CACHE.remove(&ip).await.ok_or_else(|| "no sso session started".to_string())?;
|
||||||
// Now you can exchange it for an access token and ID token.
|
// Now you can exchange it for an access token and ID token.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue