mirror of
https://github.com/pfzetto/axum-oidc.git
synced 2024-11-23 12:02:49 +01:00
Merge branch 'ci'
Added CI and moved examples to `examples` folder.
This commit is contained in:
commit
2741a3ee28
7 changed files with 158 additions and 49 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
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
/target
|
target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
|
@ -17,10 +17,10 @@ axum-core = "0.4"
|
||||||
axum = { version = "0.7", default-features = false, features = [ "query" ] }
|
axum = { version = "0.7", default-features = false, features = [ "query" ] }
|
||||||
tower-service = "0.3.2"
|
tower-service = "0.3.2"
|
||||||
tower-layer = "0.3"
|
tower-layer = "0.3"
|
||||||
tower-sessions = { version = "0.9", default-features = false, features = [ "axum-core" ] }
|
tower-sessions = { version = "0.11", default-features = false, features = [ "axum-core" ] }
|
||||||
http = "1.0"
|
http = "1.1"
|
||||||
async-trait = "0.1"
|
async-trait = "0.1"
|
||||||
openidconnect = "3.4"
|
openidconnect = "3.5"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
futures-util = "0.3"
|
futures-util = "0.3"
|
||||||
reqwest = { version = "0.11", default-features = false }
|
reqwest = { version = "0.11", default-features = false }
|
||||||
|
|
47
README.md
47
README.md
|
@ -15,51 +15,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);
|
|
||||||
|
|
||||||
let listener = TcpListener::bind("[::]:8080").await.unwrap();
|
|
||||||
axum::serve(listener, app.into_make_service()).await.unwrap();
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
# Example Projects
|
|
||||||
Here is a place for projects that are using this library.
|
|
||||||
- [zettoIT ARS - AudienceResponseSystem](https://git2.zettoit.eu/zettoit/ars) (by me)
|
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
I'm happy about any contribution in any form.
|
I'm happy about any contribution in any form.
|
||||||
|
|
13
examples/basic/Cargo.toml
Normal file
13
examples/basic/Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
[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"] }
|
||||||
|
axum = "0.7.4"
|
||||||
|
axum-oidc = { path = "./../.." }
|
||||||
|
tower = "0.4.13"
|
||||||
|
tower-sessions = "0.11.0"
|
72
examples/basic/src/main.rs
Normal file
72
examples/basic/src/main.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
use axum::{
|
||||||
|
error_handling::HandleErrorLayer, http::Uri, response::IntoResponse, routing::get, Router,
|
||||||
|
};
|
||||||
|
use axum_oidc::{
|
||||||
|
error::MiddlewareError, EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcLoginLayer,
|
||||||
|
};
|
||||||
|
use tokio::net::TcpListener;
|
||||||
|
use tower::ServiceBuilder;
|
||||||
|
use tower_sessions::{
|
||||||
|
cookie::{time::Duration, SameSite},
|
||||||
|
Expiry, MemoryStore, SessionManagerLayer,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let session_store = MemoryStore::default();
|
||||||
|
let session_layer = SessionManagerLayer::new(session_store)
|
||||||
|
.with_secure(false)
|
||||||
|
.with_same_site(SameSite::Lax)
|
||||||
|
.with_expiry(Expiry::OnInactivity(Duration::seconds(120)));
|
||||||
|
|
||||||
|
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("http://localhost:8080"),
|
||||||
|
"https://auth.zettoit.eu/realms/zettoit".to_string(),
|
||||||
|
"oxicloud".to_string(),
|
||||||
|
Some("IvBcDOfp9WBfGNmwIbiv67bxCwuQUGbl".to_owned()),
|
||||||
|
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_layer);
|
||||||
|
|
||||||
|
let listener = TcpListener::bind("[::]:8080").await.unwrap();
|
||||||
|
axum::serve(listener, app.into_make_service())
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn authenticated(claims: OidcClaims<EmptyAdditionalClaims>) -> impl IntoResponse {
|
||||||
|
format!("Hello {}", claims.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.subject().as_str()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
"Hello anon!".to_string()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
use crate::{error::ExtractorError, AdditionalClaims};
|
use crate::{error::ExtractorError, AdditionalClaims};
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use axum_core::extract::FromRequestParts;
|
use axum_core::extract::FromRequestParts;
|
||||||
|
@ -27,6 +29,23 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<AC: AdditionalClaims> Deref for OidcClaims<AC> {
|
||||||
|
type Target = IdTokenClaims<AC, CoreGenderClaim>;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<AC> AsRef<IdTokenClaims<AC, CoreGenderClaim>> for OidcClaims<AC>
|
||||||
|
where
|
||||||
|
AC: AdditionalClaims,
|
||||||
|
{
|
||||||
|
fn as_ref(&self) -> &IdTokenClaims<AC, CoreGenderClaim> {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extractor for the OpenID Connect Access Token.
|
/// Extractor for the OpenID Connect Access Token.
|
||||||
///
|
///
|
||||||
/// This Extractor will only return the Access Token when the cached session is valid and [crate::middleware::OidcAuthMiddleware] is loaded.
|
/// This Extractor will only return the Access Token when the cached session is valid and [crate::middleware::OidcAuthMiddleware] is loaded.
|
||||||
|
@ -48,3 +67,17 @@ where
|
||||||
.ok_or(ExtractorError::Unauthorized)
|
.ok_or(ExtractorError::Unauthorized)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Deref for OidcAccessToken {
|
||||||
|
type Target = str;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AsRef<str> for OidcAccessToken {
|
||||||
|
fn as_ref(&self) -> &str {
|
||||||
|
self.0.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue