mirror of
https://codeberg.org/pfzetto/axum-oidc
synced 2025-12-08 06:05:16 +01:00
Merge PR #40
This commit is contained in:
commit
a766608f55
5 changed files with 35 additions and 30 deletions
|
|
@ -7,7 +7,6 @@ version = "0.1.0"
|
||||||
axum = { version = "0.8", features = ["macros"] }
|
axum = { version = "0.8", features = ["macros"] }
|
||||||
axum-oidc = { path = "./../.." }
|
axum-oidc = { path = "./../.." }
|
||||||
dotenvy = "0.15"
|
dotenvy = "0.15"
|
||||||
openidconnect = "4.0.1"
|
|
||||||
tokio = { version = "1.48.0", features = ["macros", "net", "rt-multi-thread"] }
|
tokio = { version = "1.48.0", features = ["macros", "net", "rt-multi-thread"] }
|
||||||
tower = "0.5"
|
tower = "0.5"
|
||||||
tower-sessions = "0.14"
|
tower-sessions = "0.14"
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
|
Router,
|
||||||
error_handling::HandleErrorLayer,
|
error_handling::HandleErrorLayer,
|
||||||
http::Uri,
|
http::Uri,
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
routing::{any, get},
|
routing::{any, get},
|
||||||
Router,
|
|
||||||
};
|
};
|
||||||
use axum_oidc::{
|
use axum_oidc::{
|
||||||
error::MiddlewareError, handle_oidc_redirect, Audience, ClientId, ClientSecret,
|
|
||||||
EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcClient, OidcLoginLayer,
|
EmptyAdditionalClaims, OidcAuthLayer, OidcClaims, OidcClient, OidcLoginLayer,
|
||||||
OidcRpInitiatedLogout,
|
OidcRpInitiatedLogout,
|
||||||
|
error::MiddlewareError,
|
||||||
|
handle_oidc_redirect,
|
||||||
|
openidconnect::{Audience, ClientId, ClientSecret, IssuerUrl, Scope},
|
||||||
};
|
};
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tower::ServiceBuilder;
|
use tower::ServiceBuilder;
|
||||||
use tower_sessions::{
|
use tower_sessions::{
|
||||||
cookie::{time::Duration, SameSite},
|
|
||||||
Expiry, MemoryStore, SessionManagerLayer,
|
Expiry, MemoryStore, SessionManagerLayer,
|
||||||
|
cookie::{SameSite, time::Duration},
|
||||||
};
|
};
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
|
|
||||||
|
|
@ -47,15 +49,19 @@ async fn main() {
|
||||||
.with_default_http_client()
|
.with_default_http_client()
|
||||||
.with_redirect_url(Uri::from_static("http://localhost:8080/oidc"))
|
.with_redirect_url(Uri::from_static("http://localhost:8080/oidc"))
|
||||||
.with_client_id(ClientId::new(client_id))
|
.with_client_id(ClientId::new(client_id))
|
||||||
.add_scope("profile")
|
.add_scope(Scope::new("profile".into()))
|
||||||
.add_scope("email")
|
.add_scope(Scope::new("email".into()))
|
||||||
// Optional: add untrusted audiences. If the `aud` claim contains any of these audiences, the token is rejected.
|
// Optional: add untrusted audiences. If the `aud` claim contains any of these audiences, the token is rejected.
|
||||||
.add_untrusted_audience(Audience::new("123456789".to_string()));
|
.add_untrusted_audience(Audience::new("123456789".to_string()));
|
||||||
|
|
||||||
if let Some(client_secret) = client_secret {
|
if let Some(client_secret) = client_secret {
|
||||||
oidc_client = oidc_client.with_client_secret(ClientSecret::new(client_secret));
|
oidc_client = oidc_client.with_client_secret(ClientSecret::new(client_secret));
|
||||||
}
|
}
|
||||||
let oidc_client = oidc_client.discover(issuer).await.unwrap().build();
|
let oidc_client = oidc_client
|
||||||
|
.discover(IssuerUrl::new(issuer.into()).expect("Invalid IssuerUrl"))
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.build();
|
||||||
|
|
||||||
let oidc_auth_service = ServiceBuilder::new()
|
let oidc_auth_service = ServiceBuilder::new()
|
||||||
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
|
.layer(HandleErrorLayer::new(|e: MiddlewareError| async {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use http::Uri;
|
use http::Uri;
|
||||||
use openidconnect::{Audience, ClientId, ClientSecret, IssuerUrl};
|
use openidconnect::{
|
||||||
|
Audience, AuthenticationContextClass, ClientId, ClientSecret, IssuerUrl, Scope,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{error::Error, AdditionalClaims, Client, OidcClient, ProviderMetadata};
|
use crate::{error::Error, AdditionalClaims, Client, OidcClient, ProviderMetadata};
|
||||||
|
|
||||||
|
|
@ -21,8 +23,8 @@ pub struct Builder<AC: AdditionalClaims, Credentials, Client, HttpClient, Redire
|
||||||
http_client: HttpClient,
|
http_client: HttpClient,
|
||||||
redirect_url: RedirectUrl,
|
redirect_url: RedirectUrl,
|
||||||
end_session_endpoint: Option<Uri>,
|
end_session_endpoint: Option<Uri>,
|
||||||
scopes: Vec<Box<str>>,
|
scopes: Vec<Scope>,
|
||||||
auth_context_class: Option<Box<str>>,
|
auth_context_class: Option<AuthenticationContextClass>,
|
||||||
untrusted_audiences: Vec<Audience>,
|
untrusted_audiences: Vec<Audience>,
|
||||||
_ac: PhantomData<AC>,
|
_ac: PhantomData<AC>,
|
||||||
}
|
}
|
||||||
|
|
@ -41,7 +43,7 @@ impl<AC: AdditionalClaims> Builder<AC, (), (), (), ()> {
|
||||||
http_client: (),
|
http_client: (),
|
||||||
redirect_url: (),
|
redirect_url: (),
|
||||||
end_session_endpoint: None,
|
end_session_endpoint: None,
|
||||||
scopes: vec![Box::from("openid")],
|
scopes: vec![Scope::new("openid".to_string())],
|
||||||
auth_context_class: None,
|
auth_context_class: None,
|
||||||
untrusted_audiences: Vec::new(),
|
untrusted_audiences: Vec::new(),
|
||||||
_ac: PhantomData,
|
_ac: PhantomData,
|
||||||
|
|
@ -58,20 +60,20 @@ impl<AC: AdditionalClaims> OidcClient<AC> {
|
||||||
|
|
||||||
impl<AC: AdditionalClaims, CREDS, CLIENT, HTTP, RURL> Builder<AC, CREDS, CLIENT, HTTP, RURL> {
|
impl<AC: AdditionalClaims, CREDS, CLIENT, HTTP, RURL> Builder<AC, CREDS, CLIENT, HTTP, RURL> {
|
||||||
/// add a scope to existing (default) scopes
|
/// add a scope to existing (default) scopes
|
||||||
pub fn add_scope(mut self, scope: impl Into<Box<str>>) -> Self {
|
pub fn add_scope(mut self, scope: Scope) -> Self {
|
||||||
self.scopes.push(scope.into());
|
self.scopes.push(scope);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// replace scopes (including default)
|
/// replace scopes (including default)
|
||||||
pub fn with_scopes(mut self, scopes: impl Iterator<Item = impl Into<Box<str>>>) -> Self {
|
pub fn with_scopes(mut self, scopes: Vec<Scope>) -> Self {
|
||||||
self.scopes = scopes.map(|x| x.into()).collect::<Vec<_>>();
|
self.scopes = scopes;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// authenticate with Authentication Context Class Reference
|
/// authenticate with Authentication Context Class Reference
|
||||||
pub fn with_auth_context_class(mut self, acr: impl Into<Box<str>>) -> Self {
|
pub fn with_auth_context_class(mut self, acr: AuthenticationContextClass) -> Self {
|
||||||
self.auth_context_class = Some(acr.into());
|
self.auth_context_class = Some(acr);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,14 +214,13 @@ impl<AC: AdditionalClaims> Builder<AC, ClientCredentials, (), HttpClient, Redire
|
||||||
/// discover issuer details
|
/// discover issuer details
|
||||||
pub async fn discover(
|
pub async fn discover(
|
||||||
self,
|
self,
|
||||||
issuer: String,
|
issuer: IssuerUrl,
|
||||||
) -> Result<
|
) -> Result<
|
||||||
Builder<AC, ClientCredentials, OpenidconnectClient<AC>, HttpClient, RedirectUrl>,
|
Builder<AC, ClientCredentials, OpenidconnectClient<AC>, HttpClient, RedirectUrl>,
|
||||||
Error,
|
Error,
|
||||||
> {
|
> {
|
||||||
let issuer_url = IssuerUrl::new(issuer)?;
|
|
||||||
let http_client = self.http_client.0.clone();
|
let http_client = self.http_client.0.clone();
|
||||||
let provider_metadata = ProviderMetadata::discover_async(issuer_url, &http_client);
|
let provider_metadata = ProviderMetadata::discover_async(issuer, &http_client);
|
||||||
|
|
||||||
Self::manual(self, provider_metadata.await?)
|
Self::manual(self, provider_metadata.await?)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/lib.rs
12
src/lib.rs
|
|
@ -12,9 +12,9 @@ use openidconnect::{
|
||||||
CoreResponseMode, CoreResponseType, CoreRevocableToken, CoreRevocationErrorResponse,
|
CoreResponseMode, CoreResponseType, CoreRevocableToken, CoreRevocationErrorResponse,
|
||||||
CoreSubjectIdentifierType, CoreTokenIntrospectionResponse, CoreTokenType,
|
CoreSubjectIdentifierType, CoreTokenIntrospectionResponse, CoreTokenType,
|
||||||
},
|
},
|
||||||
AccessToken, CsrfToken, EmptyExtraTokenFields, EndpointMaybeSet, EndpointNotSet, EndpointSet,
|
AccessToken, Audience, AuthenticationContextClass, ClientId, CsrfToken, EmptyExtraTokenFields,
|
||||||
IdTokenFields, Nonce, PkceCodeVerifier, RefreshToken, StandardErrorResponse,
|
EndpointMaybeSet, EndpointNotSet, EndpointSet, IdTokenFields, Nonce, PkceCodeVerifier,
|
||||||
StandardTokenResponse,
|
RefreshToken, Scope, StandardErrorResponse, StandardTokenResponse,
|
||||||
};
|
};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -27,7 +27,7 @@ mod middleware;
|
||||||
pub use extractor::{OidcAccessToken, OidcClaims, OidcRpInitiatedLogout, OidcUserInfo};
|
pub use extractor::{OidcAccessToken, OidcClaims, OidcRpInitiatedLogout, OidcUserInfo};
|
||||||
pub use handler::handle_oidc_redirect;
|
pub use handler::handle_oidc_redirect;
|
||||||
pub use middleware::{OidcAuthLayer, OidcAuthMiddleware, OidcLoginLayer, OidcLoginMiddleware};
|
pub use middleware::{OidcAuthLayer, OidcAuthMiddleware, OidcLoginLayer, OidcLoginMiddleware};
|
||||||
pub use openidconnect::{Audience, ClientId, ClientSecret};
|
pub use openidconnect;
|
||||||
|
|
||||||
const SESSION_KEY: &str = "axum-oidc";
|
const SESSION_KEY: &str = "axum-oidc";
|
||||||
|
|
||||||
|
|
@ -102,12 +102,12 @@ pub type BoxError = Box<dyn std::error::Error + Send + Sync>;
|
||||||
/// OpenID Connect Client
|
/// OpenID Connect Client
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct OidcClient<AC: AdditionalClaims> {
|
pub struct OidcClient<AC: AdditionalClaims> {
|
||||||
scopes: Vec<Box<str>>,
|
scopes: Vec<Scope>,
|
||||||
client_id: ClientId,
|
client_id: ClientId,
|
||||||
client: Client<AC>,
|
client: Client<AC>,
|
||||||
http_client: reqwest::Client,
|
http_client: reqwest::Client,
|
||||||
end_session_endpoint: Option<Uri>,
|
end_session_endpoint: Option<Uri>,
|
||||||
auth_context_class: Option<Box<str>>,
|
auth_context_class: Option<AuthenticationContextClass>,
|
||||||
untrusted_audiences: Vec<Audience>,
|
untrusted_audiences: Vec<Audience>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,8 @@ use tower_sessions::Session;
|
||||||
|
|
||||||
use openidconnect::{
|
use openidconnect::{
|
||||||
core::{CoreAuthenticationFlow, CoreErrorResponseType, CoreGenderClaim, CoreJsonWebKey},
|
core::{CoreAuthenticationFlow, CoreErrorResponseType, CoreGenderClaim, CoreJsonWebKey},
|
||||||
AccessToken, AccessTokenHash, AuthenticationContextClass, CsrfToken, IdTokenClaims,
|
AccessToken, AccessTokenHash, CsrfToken, IdTokenClaims, IdTokenVerifier, Nonce,
|
||||||
IdTokenVerifier, Nonce, OAuth2TokenResponse, PkceCodeChallenge, RefreshToken,
|
OAuth2TokenResponse, PkceCodeChallenge, RefreshToken,
|
||||||
RequestTokenError::ServerResponse,
|
RequestTokenError::ServerResponse,
|
||||||
Scope, TokenResponse, UserInfoClaims,
|
Scope, TokenResponse, UserInfoClaims,
|
||||||
};
|
};
|
||||||
|
|
@ -143,8 +143,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(acr) = oidcclient.auth_context_class {
|
if let Some(acr) = oidcclient.auth_context_class {
|
||||||
auth = auth
|
auth = auth.add_auth_context_value(acr);
|
||||||
.add_auth_context_value(AuthenticationContextClass::new(acr.into()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auth.set_pkce_challenge(pkce_challenge).url()
|
auth.set_pkce_challenge(pkce_challenge).url()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue