Skip to content

Commit 52f5d72

Browse files
feat(workos): add OAuth and SSO buttons to index action (#163)
1 parent b437eaa commit 52f5d72

24 files changed

Lines changed: 269 additions & 94 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

examples/dioxus-axum/.env.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# WorkOS (optional)
2+
# WORKOS_API_KEY =

examples/dioxus-axum/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ server = [
1818
"dep:shield-dioxus-axum",
1919
"dep:shield-memory",
2020
"dep:shield-oidc",
21+
"dep:shield-workos",
2122
"dep:tokio",
2223
"dep:tower-sessions",
2324
"dioxus/server",
@@ -35,6 +36,7 @@ shield-dioxus.workspace = true
3536
shield-dioxus-axum = { workspace = true, optional = true }
3637
shield-memory = { workspace = true, optional = true }
3738
shield-oidc = { workspace = true, features = ["native-tls"], optional = true }
39+
shield-workos = { workspace = true, optional = true }
3840
tokio = { workspace = true, features = ["rt-multi-thread"], optional = true }
3941
tower-sessions = { workspace = true, optional = true }
4042
tracing.workspace = true

examples/dioxus-axum/src/main.rs

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,19 @@ fn main() {
1515
#[cfg(feature = "server")]
1616
#[tokio::main]
1717
async fn main() {
18-
use std::sync::Arc;
18+
use std::{env, sync::Arc};
1919

2020
use axum::Router;
2121
use dioxus::{
2222
cli_config::fullstack_address_or_localhost,
2323
prelude::{DioxusRouterExt, *},
2424
};
25-
use shield::{Shield, ShieldOptions};
25+
use shield::{ErasedMethod, Method, Shield, ShieldOptions};
2626
use shield_bootstrap::BootstrapDioxusStyle;
2727
use shield_dioxus_axum::{AxumDioxusIntegration, ShieldLayer};
2828
use shield_memory::{MemoryStorage, User};
2929
use shield_oidc::{Keycloak, OidcMethod};
30+
use shield_workos::{WorkosMethod, WorkosOauthProvider, WorkosOptions};
3031
use tokio::net::TcpListener;
3132
use tower_sessions::{Expiry, MemoryStore, SessionManagerLayer, cookie::time::Duration};
3233
use tracing::{Level, info};
@@ -45,21 +46,39 @@ async fn main() {
4546
let storage = MemoryStorage::new();
4647
let shield = Shield::new(
4748
storage.clone(),
48-
vec![Arc::new(
49-
OidcMethod::new(storage).with_providers([Keycloak::builder(
50-
"keycloak",
51-
"http://localhost:18080/realms/Shield",
52-
"client1",
53-
)
54-
.client_secret("xcpQsaGbRILTljPtX4npjmYMBjKrariJ")
55-
.redirect_url(format!(
56-
"http://localhost:{}/api/auth/oidc/sign-in-callback/keycloak",
57-
dioxus::cli_config::devserver_raw_addr()
58-
.map(|addr| addr.port())
59-
.unwrap_or_else(|| addr.port())
60-
))
61-
.build()]),
62-
)],
49+
[
50+
Some(Arc::new(
51+
OidcMethod::new(storage).with_providers([Keycloak::builder(
52+
"keycloak",
53+
"http://localhost:18080/realms/Shield",
54+
"client1",
55+
)
56+
.client_secret("xcpQsaGbRILTljPtX4npjmYMBjKrariJ")
57+
.redirect_url(format!(
58+
"http://localhost:{}/api/auth/oidc/sign-in-callback/keycloak",
59+
dioxus::cli_config::devserver_raw_addr()
60+
.map(|addr| addr.port())
61+
.unwrap_or_else(|| addr.port())
62+
))
63+
.build()]),
64+
) as Arc<dyn ErasedMethod>),
65+
env::var("WORKOS_API_KEY").ok().map(|api_key| {
66+
Arc::new(
67+
WorkosMethod::from_api_key(&api_key).with_options(
68+
WorkosOptions::builder()
69+
.oauth_providers(vec![
70+
WorkosOauthProvider::AppleOAuth,
71+
WorkosOauthProvider::GoogleOAuth,
72+
WorkosOauthProvider::MicrosoftOAuth,
73+
])
74+
.build(),
75+
),
76+
) as Arc<dyn ErasedMethod>
77+
}),
78+
]
79+
.into_iter()
80+
.flatten()
81+
.collect(),
6382
ShieldOptions::default(),
6483
);
6584
let shield_layer = ShieldLayer::new(shield.clone());

packages/core/shield/src/action.rs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ pub trait Action<P: Provider>: ErasedAction + Send + Sync {
4040
Ok(true)
4141
}
4242

43-
fn forms(&self, provider: P) -> Vec<Form>;
43+
async fn forms(&self, provider: P) -> Result<Vec<Form>, ShieldError>;
4444

4545
async fn call(
4646
&self,
@@ -62,7 +62,10 @@ pub trait ErasedAction: Send + Sync {
6262
session: Session,
6363
) -> Result<bool, ShieldError>;
6464

65-
fn erased_forms(&self, provider: Box<dyn Any + Send + Sync>) -> Vec<Form>;
65+
async fn erased_forms(
66+
&self,
67+
provider: Box<dyn Any + Send + Sync>,
68+
) -> Result<Vec<Form>, ShieldError>;
6669

6770
async fn erased_call(
6871
&self,
@@ -89,8 +92,8 @@ macro_rules! erased_action {
8992
self.condition(provider.downcast_ref().expect("TODO"), session)
9093
}
9194

92-
fn erased_forms(&self, provider: Box<dyn std::any::Any + Send + Sync>) -> Vec<$crate::Form> {
93-
self.forms(*provider.downcast().expect("TODO"))
95+
async fn erased_forms(&self, provider: Box<dyn std::any::Any + Send + Sync>) -> Result<Vec<$crate::Form>, $crate::ShieldError> {
96+
self.forms(*provider.downcast().expect("TODO")).await
9497
}
9598

9699
async fn erased_call(

packages/core/shield/src/actions/sign_out.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@ impl SignOutAction {
3131
}))
3232
}
3333

34-
pub fn forms<P: Provider>(_provider: P) -> Vec<Form> {
35-
vec![Form {
34+
pub async fn forms<P: Provider>(_provider: P) -> Result<Vec<Form>, ShieldError> {
35+
Ok(vec![Form {
3636
inputs: vec![Input {
3737
name: "submit".to_owned(),
3838
label: None,
3939
r#type: InputType::Submit(InputTypeSubmit {}),
4040
value: Some(Self::name()),
4141
}],
42-
}]
42+
}])
4343
}
4444
}

packages/core/shield/src/shield.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl<U: User> Shield<U> {
107107
continue;
108108
}
109109

110-
let forms = action.erased_forms(provider);
110+
let forms = action.erased_forms(provider).await?;
111111
for form in forms {
112112
provider_forms.push(ActionProviderForm {
113113
id: provider_id.clone(),

packages/methods/shield-credentials/src/actions/sign_in.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ impl<U: User + 'static, D: DeserializeOwned + 'static> Action<CredentialsProvide
3131
SignInAction::name()
3232
}
3333

34-
fn forms(&self, _provider: CredentialsProvider) -> Vec<Form> {
35-
vec![self.credentials.form()]
34+
async fn forms(&self, _provider: CredentialsProvider) -> Result<Vec<Form>, ShieldError> {
35+
Ok(vec![self.credentials.form()])
3636
}
3737

3838
async fn call(

packages/methods/shield-credentials/src/actions/sign_out.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ impl Action<CredentialsProvider> for CredentialsSignOutAction {
2323
SignOutAction::condition(provider, session)
2424
}
2525

26-
fn forms(&self, provider: CredentialsProvider) -> Vec<Form> {
27-
SignOutAction::forms(provider)
26+
async fn forms(&self, provider: CredentialsProvider) -> Result<Vec<Form>, ShieldError> {
27+
SignOutAction::forms(provider).await
2828
}
2929

3030
async fn call(

packages/methods/shield-oauth/src/actions/sign_in.rs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use async_trait::async_trait;
22
use oauth2::{CsrfToken, PkceCodeChallenge, Scope, url::form_urlencoded::parse};
33
use shield::{
4-
Action, ConfigurationError, Form, Request, Response, Session, SessionError, ShieldError,
5-
SignInAction, erased_action,
4+
Action, ConfigurationError, Form, Input, InputType, InputTypeSubmit, Provider, Request,
5+
Response, Session, SessionError, ShieldError, SignInAction, erased_action,
66
};
77

88
use crate::{
@@ -23,8 +23,15 @@ impl Action<OauthProvider> for OauthSignInAction {
2323
SignInAction::name()
2424
}
2525

26-
fn forms(&self, _provider: OauthProvider) -> Vec<Form> {
27-
vec![Form { inputs: vec![] }]
26+
async fn forms(&self, provider: OauthProvider) -> Result<Vec<Form>, ShieldError> {
27+
Ok(vec![Form {
28+
inputs: vec![Input {
29+
name: "submit".to_owned(),
30+
label: None,
31+
r#type: InputType::Submit(InputTypeSubmit::default()),
32+
value: Some(format!("Sign in with {}", provider.name())),
33+
}],
34+
}])
2835
}
2936

3037
async fn call(

0 commit comments

Comments
 (0)