strip iss & use new example structure

strip the iss query parameters from urls
use the new test structure and CI
This commit is contained in:
Paul Zinselmeyer 2024-04-18 16:07:35 +02:00
parent 830e54a443
commit dbecc439d8
Signed by: pfzetto
GPG key ID: 142847B253911DB0
7 changed files with 140 additions and 55 deletions

34
.github/workflows/ci.yml vendored Normal file
View file

@ -0,0 +1,34 @@
name: Cargo Build & Test
on:
push:
pull_request:
schedule:
- cron: '0 0 1,7,14,21 * *'
env:
CARGO_TERM_COLOR: always
jobs:
build_and_test:
name: axum-oidc - latest
runs-on: ubuntu-latest
strategy:
matrix:
toolchain:
- stable
- nightly
steps:
- uses: actions/checkout@v3
- run: rustup update ${{ matrix.toolchain }} && rustup default ${{ matrix.toolchain }}
- run: cargo build --verbose
- run: cargo test --verbose
build_examples:
name: axum-oidc - examples
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: rustup update stable && rustup default stable
- run: cargo build --verbose
working-directory: ./examples/basic

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
/target target
.env
Cargo.lock Cargo.lock

View file

@ -1,7 +1,7 @@
[package] [package]
name = "axum-oidc" name = "axum-oidc"
description = "A wrapper for the openidconnect crate for axum" description = "A wrapper for the openidconnect crate for axum"
version = "0.1.1" version = "0.1.2"
edition = "2021" edition = "2021"
authors = [ "Paul Z <info@pfz4.de>" ] authors = [ "Paul Z <info@pfz4.de>" ]
readme = "README.md" readme = "README.md"

View file

@ -1,3 +1,5 @@
**THIS IS AN OLD VERSION! PLEASE USE THE LATEST VERSION IF POSSIBLE!**
This Library allows using [OpenID Connect](https://openid.net/developers/how-connect-works/) with [axum](https://github.com/tokio-rs/axum). This Library allows using [OpenID Connect](https://openid.net/developers/how-connect-works/) with [axum](https://github.com/tokio-rs/axum).
It authenticates the user with the OpenID Conenct Issuer and provides Extractors. It authenticates the user with the OpenID Conenct Issuer and provides Extractors.
@ -15,49 +17,8 @@ The `OidcAccessToken`-extractor can be used to get the OpenId Connect Access Tok
Your OIDC-Client must be allowed to redirect to **every** subpath of your application base url. Your OIDC-Client must be allowed to redirect to **every** subpath of your application base url.
```rust # Examples
#[tokio::main] Take a look at the `examples` folder for examples.
async fn main() {
let session_store = MemoryStore::default();
let session_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|_: BoxError| async {
StatusCode::BAD_REQUEST
}))
.layer(SessionManagerLayer::new(session_store).with_same_site(SameSite::Lax));
let oidc_login_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
e.into_response()
}))
.layer(OidcLoginLayer::<EmptyAdditionalClaims>::new());
let oidc_auth_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
e.into_response()
}))
.layer(
OidcAuthLayer::<EmptyAdditionalClaims>::discover_client(
Uri::from_static("https://example.com"),
"<issuer>".to_string(),
"<client_id>".to_string(),
"<client_secret>".to_owned(),
vec![],
).await.unwrap(),
);
let app = Router::new()
.route("/", get(|| async { "Hello, authenticated World!" }))
.layer(oidc_login_service)
.layer(oidc_auth_service)
.layer(session_service);
axum::Server::bind(&"[::]:8080".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
```
# Example Projects # Example Projects
Here is a place for projects that are using this library. Here is a place for projects that are using this library.

15
examples/basic/Cargo.toml Normal file
View file

@ -0,0 +1,15 @@
[package]
name = "basic"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
tokio = { version = "1.36.0", features = ["net", "macros", "rt-multi-thread"] }
axum = "0.6"
axum-oidc = { path = "./../.." }
tower = "0.4"
tower-sessions = "0.4"
dotenvy = "0.15.7"

View file

@ -0,0 +1,79 @@
use axum::{
error_handling::HandleErrorLayer,
http::{StatusCode, Uri},
response::IntoResponse,
routing::get,
BoxError, Router,
};
use axum_oidc::{
error::MiddlewareError, EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcLoginLayer,
};
use tower::ServiceBuilder;
use tower_sessions::{cookie::SameSite, MemoryStore, SessionManagerLayer};
#[tokio::main]
async fn main() {
dotenvy::dotenv().ok();
let app_url = std::env::var("APP_URL").expect("APP_URL env variable");
let issuer = std::env::var("ISSUER").expect("ISSUER env variable");
let client_id = std::env::var("CLIENT_ID").expect("CLIENT_ID env variable");
let client_secret = std::env::var("CLIENT_SECRET").ok();
let session_store = MemoryStore::default();
let session_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|_: BoxError| async {
StatusCode::BAD_REQUEST
}))
.layer(SessionManagerLayer::new(session_store).with_same_site(SameSite::Lax));
let oidc_login_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
e.into_response()
}))
.layer(OidcLoginLayer::<EmptyAdditionalClaims>::new());
let oidc_auth_service = ServiceBuilder::new()
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
e.into_response()
}))
.layer(
OidcAuthLayer::<EmptyAdditionalClaims>::discover_client(
Uri::from_maybe_shared(app_url).expect("valid APP_URL"),
issuer,
client_id,
client_secret,
vec![],
)
.await
.unwrap(),
);
let app = Router::new()
.route("/foo", get(authenticated))
.layer(oidc_login_service)
.route("/bar", get(maybe_authenticated))
.layer(oidc_auth_service)
.layer(session_service);
axum::Server::bind(&"[::]:8080".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
async fn authenticated(claims: OidcClaims<EmptyAdditionalClaims>) -> impl IntoResponse {
format!("Hello {}", claims.0.subject().as_str())
}
async fn maybe_authenticated(
claims: Option<OidcClaims<EmptyAdditionalClaims>>,
) -> impl IntoResponse {
if let Some(claims) = claims {
format!(
"Hello {}! You are already logged in from another Handler.",
claims.0.subject().as_str()
)
} else {
"Hello anon!".to_string()
}
}

View file

@ -1,6 +1,5 @@
use std::{ use std::{
marker::PhantomData, marker::PhantomData,
str::FromStr,
task::{Context, Poll}, task::{Context, Poll},
}; };
@ -16,20 +15,15 @@ use tower_service::Service;
use tower_sessions::Session; use tower_sessions::Session;
use openidconnect::{ use openidconnect::{
core::{ core::CoreAuthenticationFlow, reqwest::async_http_client, AccessTokenHash, AuthorizationCode,
CoreAuthenticationFlow, CoreGenderClaim, CoreIdTokenFields, CoreJsonWebKeyType, CsrfToken, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, Scope,
CoreJweContentEncryptionAlgorithm, CoreJwsSigningAlgorithm, TokenResponse,
},
reqwest::async_http_client,
AccessTokenHash, AuthorizationCode, CsrfToken, ExtraTokenFields, IdTokenFields, Nonce,
OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier, RedirectUrl, RefreshToken, Scope,
StandardTokenResponse, TokenResponse, TokenType,
}; };
use crate::{ use crate::{
error::{Error, MiddlewareError}, error::{Error, MiddlewareError},
extractor::{OidcAccessToken, OidcClaims}, extractor::{OidcAccessToken, OidcClaims},
AdditionalClaims, BoxError, IdToken, OidcClient, OidcQuery, OidcSession, SESSION_KEY, AdditionalClaims, BoxError, OidcClient, OidcQuery, OidcSession, SESSION_KEY,
}; };
/// Layer for the [OidcLoginMiddleware]. /// Layer for the [OidcLoginMiddleware].
@ -419,6 +413,7 @@ pub fn strip_oidc_from_path(base_url: Uri, uri: &Uri) -> Result<Uri, MiddlewareE
!x.starts_with("code") !x.starts_with("code")
&& !x.starts_with("state") && !x.starts_with("state")
&& !x.starts_with("session_state") && !x.starts_with("session_state")
&& !x.starts_with("iss")
}) })
.map(|x| x.to_string()) .map(|x| x.to_string())
.reduce(|acc, x| acc + "&" + &x) .reduce(|acc, x| acc + "&" + &x)