mirror of
https://github.com/pfzetto/axum-oidc.git
synced 2024-11-27 13:44:56 +01:00
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:
parent
830e54a443
commit
dbecc439d8
7 changed files with 140 additions and 55 deletions
34
.github/workflows/ci.yml
vendored
Normal file
34
.github/workflows/ci.yml
vendored
Normal 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
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
target
|
||||||
|
.env
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
|
@ -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"
|
||||||
|
|
47
README.md
47
README.md
|
@ -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
15
examples/basic/Cargo.toml
Normal 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"
|
79
examples/basic/src/main.rs
Normal file
79
examples/basic/src/main.rs
Normal 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()
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue