Skip to content

Commit

Permalink
Shield Axum: Add API routes
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielleHuisman committed Dec 26, 2024
1 parent 724538b commit 4855ba1
Show file tree
Hide file tree
Showing 15 changed files with 157 additions and 4 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions examples/leptos-axum/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ async fn main() {
use leptos_axum::{generate_route_list, LeptosRoutes};
use shield::{DummyProvider, DummyStorage, Shield};
use shield_examples_leptos_axum::app::*;
use shield_leptos_axum::{provide_axum_integration, ShieldLayer};
use shield_leptos_axum::{auth_router, provide_axum_integration, ShieldLayer};
use shield_oidc::{KeycloakBuilder, OidcProvider};
use time::Duration;
use tokio::net::TcpListener;
Expand Down Expand Up @@ -40,7 +40,7 @@ async fn main() {
)
.client_secret("xcpQsaGbRILTljPtX4npjmYMBjKrariJ")
.redirect_url(&format!(
"http://localhost:{}/api/auth/oidc/keycloak/callback",
"http://localhost:{}/api/auth/sign-in/callback/oidc/keycloak",
addr.port()
))
.build()]),
Expand All @@ -51,6 +51,7 @@ async fn main() {

// Initialize app
let app = Router::new()
.nest("/api/auth", auth_router())
.leptos_routes_with_context(
&leptos_options,
routes,
Expand Down
2 changes: 1 addition & 1 deletion keycloak/Shield-realm.json
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"secret": "xcpQsaGbRILTljPtX4npjmYMBjKrariJ",
"redirectUris": ["http://localhost:3000/api/auth/oidc/keycloak/callback"],
"redirectUris": ["http://localhost:3000/api/auth/sign-in/callback/oidc/keycloak"],
"webOrigins": ["http://localhost:3000"],
"notBefore": 0,
"bearerOnly": false,
Expand Down
1 change: 1 addition & 0 deletions packages/core/shield/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ pub struct SignInRequest {
pub struct SignInCallbackRequest {
pub provider_id: String,
pub subprovider_id: Option<String>,
pub query: Option<Value>,
pub data: Option<Value>,
}

Expand Down
2 changes: 2 additions & 0 deletions packages/integrations/shield-axum/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,7 @@ version.workspace = true
[dependencies]
async-trait.workspace = true
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
shield = { path = "../../core/shield" }
shield-tower = { path = "../shield-tower" }
19 changes: 19 additions & 0 deletions packages/integrations/shield-axum/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use axum::{
http::StatusCode,
response::{IntoResponse, Response},
};
use shield::ShieldError;

pub struct RouteError(ShieldError);

impl IntoResponse for RouteError {
fn into_response(self) -> Response {
(StatusCode::INTERNAL_SERVER_ERROR, self.0.to_string()).into_response()
}
}

impl From<ShieldError> for RouteError {
fn from(value: ShieldError) -> Self {
Self(value)
}
}
6 changes: 6 additions & 0 deletions packages/integrations/shield-axum/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
mod error;
mod extract;
mod path;
mod response;
mod router;
mod routes;

pub use shield_tower::*;

pub use extract::*;
pub use router::*;
7 changes: 7 additions & 0 deletions packages/integrations/shield-axum/src/path.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use serde::Deserialize;

#[derive(Deserialize)]
pub struct AuthPath {
pub provider_id: String,
pub subprovider_id: Option<String>,
}
17 changes: 17 additions & 0 deletions packages/integrations/shield-axum/src/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
use axum::response::{IntoResponse, Redirect, Response};

pub struct RouteResponse(shield::Response);

impl IntoResponse for RouteResponse {
fn into_response(self) -> Response {
match self.0 {
shield::Response::Redirect(url) => Redirect::to(&url).into_response(),
}
}
}

impl From<shield::Response> for RouteResponse {
fn from(value: shield::Response) -> Self {
Self(value)
}
}
18 changes: 18 additions & 0 deletions packages/integrations/shield-axum/src/router.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use axum::{
routing::{get, post},
Router,
};

use crate::routes::{sign_in, sign_in_callback, sign_out};

pub fn auth_router<S: Clone + Send + Sync + 'static>() -> Router<S> {
Router::new()
.route("/sign-in/:provider_id", post(sign_in))
.route("/sign-in/:provider_id/:subprovider_id", post(sign_in))
.route("/sign-in/callback/:provider_id", get(sign_in_callback))
.route(
"/sign-in/callback/:provider_id/:subprovider_id",
get(sign_in_callback),
)
.route("/sign-out", post(sign_out))
}
7 changes: 7 additions & 0 deletions packages/integrations/shield-axum/src/routes.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod sign_in;
mod sign_in_callback;
mod sign_out;

pub use sign_in::*;
pub use sign_in_callback::*;
pub use sign_out::*;
32 changes: 32 additions & 0 deletions packages/integrations/shield-axum/src/routes/sign_in.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use axum::extract::Path;
use shield::SignInRequest;

use crate::{
error::RouteError,
extract::{ExtractSession, ExtractShield},
path::AuthPath,
response::RouteResponse,
};

pub async fn sign_in(
Path(AuthPath {
provider_id,
subprovider_id,
}): Path<AuthPath>,
ExtractShield(shield): ExtractShield,
ExtractSession(session): ExtractSession,
) -> Result<RouteResponse, RouteError> {
let response = shield
.sign_in(
SignInRequest {
provider_id,
subprovider_id,
data: None,
form_data: None,
},
session,
)
.await?;

Ok(response.into())
}
34 changes: 34 additions & 0 deletions packages/integrations/shield-axum/src/routes/sign_in_callback.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use axum::extract::{Path, Query};
use serde_json::Value;
use shield::SignInCallbackRequest;

use crate::{
error::RouteError,
extract::{ExtractSession, ExtractShield},
path::AuthPath,
response::RouteResponse,
};

pub async fn sign_in_callback(
Path(AuthPath {
provider_id,
subprovider_id,
}): Path<AuthPath>,
Query(query): Query<Value>,
ExtractShield(shield): ExtractShield,
ExtractSession(session): ExtractSession,
) -> Result<RouteResponse, RouteError> {
let response = shield
.sign_in_callback(
SignInCallbackRequest {
provider_id,
subprovider_id,
query: Some(query),
data: None,
},
session,
)
.await?;

Ok(response.into())
}
1 change: 1 addition & 0 deletions packages/integrations/shield-axum/src/routes/sign_out.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub async fn sign_out() {}
8 changes: 7 additions & 1 deletion packages/providers/shield-oidc/src/provider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ impl Provider for OidcProvider {
.map(|(_, pkce_code_verifier)| pkce_code_verifier.secret().clone());
}

session.update().await?;

Ok(Response::Redirect(auth_url.to_string()))
}

Expand All @@ -161,7 +163,11 @@ impl Provider for OidcProvider {

let client = subprovider.oidc_client().await?;

let authorization_code = "".to_owned();
let authorization_code = request
.query
.and_then(|query| query.get("code").cloned())
.and_then(|code| code.as_str().map(|s| s.to_string()))
.ok_or_else(|| ShieldError::Verification("Missing authorization code.".to_owned()))?;

let mut token_request = client.exchange_code(AuthorizationCode::new(authorization_code));

Expand Down

0 comments on commit 4855ba1

Please sign in to comment.