-
Notifications
You must be signed in to change notification settings - Fork 351
Oauth integrations #1214
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Oauth integrations #1214
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
[package] | ||
name = "oauth-callback" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
hypr-oauth-core = { workspace = true } | ||
hypr-oauth-providers = { workspace = true } | ||
|
||
tower = { workspace = true } | ||
|
||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true } | ||
serde_qs = { workspace = true } | ||
|
||
base64 = { workspace = true } | ||
thiserror = { workspace = true } | ||
uuid = { workspace = true, features = ["v4"] } | ||
|
||
futures-util = { workspace = true } | ||
reqwest = { workspace = true } | ||
tracing = { workspace = true } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub mod service; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
use futures_util::task::Poll; | ||
use futures_util::Future; | ||
use hypr_oauth_core::{OAuthError, OAuthRequest, OAuthResponse}; | ||
use hypr_oauth_providers::ProviderRegistry; | ||
use std::pin::Pin; | ||
use std::sync::Arc; | ||
use std::task::Context; | ||
use tower::Service; | ||
|
||
#[derive(Clone)] | ||
pub struct OAuthServerService { | ||
providers: Arc<ProviderRegistry>, | ||
secret_store: Arc<dyn SecretStore>, | ||
} | ||
|
||
pub trait SecretStore: Send + Sync { | ||
fn get_client_secret(&self, provider: &str) -> Option<String>; | ||
} | ||
|
||
impl OAuthServerService { | ||
pub fn new(providers: Arc<ProviderRegistry>, secret_store: Arc<dyn SecretStore>) -> Self { | ||
Self { | ||
providers, | ||
secret_store, | ||
} | ||
} | ||
} | ||
|
||
impl Service<OAuthRequest> for OAuthServerService { | ||
type Response = OAuthResponse; | ||
type Error = OAuthError; | ||
type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>; | ||
|
||
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> { | ||
Poll::Ready(Ok(())) | ||
} | ||
|
||
fn call(&mut self, mut req: OAuthRequest) -> Self::Future { | ||
let providers = self.providers.clone(); | ||
let secret_store = self.secret_store.clone(); | ||
|
||
Box::pin(async move { | ||
let provider = providers | ||
.get(&req.provider) | ||
.ok_or(OAuthError::ProviderNotFound)?; | ||
|
||
// Add client secret if needed | ||
if provider.metadata().requires_client_secret { | ||
req.config.client_secret = secret_store.get_client_secret(&req.provider); | ||
} | ||
|
||
// Generate state | ||
let state = req | ||
.state | ||
.unwrap_or_else(|| uuid::Uuid::new_v4().to_string()); | ||
|
||
// Build auth URL | ||
let auth_url = provider | ||
.build_auth_url(&req.config, &state, req.pkce_challenge.as_deref()) | ||
.unwrap(); | ||
|
||
Ok(OAuthResponse { | ||
authorization_url: auth_url.to_string(), | ||
state, | ||
session_id: Some(uuid::Uuid::new_v4().to_string()), | ||
}) | ||
}) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
[package] | ||
name = "oauth-client" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
hypr-oauth-core = { workspace = true } | ||
|
||
Comment on lines
+6
to
+8
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Broken dependency name – crate will not resolve.
-hypr-oauth-core = { workspace = true }
+oauth-core = { workspace = true } 🤖 Prompt for AI Agents
|
||
reqwest = { workspace = true } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
pub const a: i32 = 1; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Placeholder constant provides no functionality.
-pub const a: i32 = 1;
+// TODO: implement OAuth client functions (token request, refresh, etc.) 🤖 Prompt for AI Agents
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[package] | ||
name = "oauth-core" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true } | ||
|
||
anyhow = { workspace = true } | ||
async-trait = { workspace = true } | ||
url = { workspace = true } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mod provider; | ||
pub use provider::*; | ||
|
||
mod types; | ||
pub use types::*; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use async_trait::async_trait; | ||
use url::Url; | ||
|
||
use crate::types::*; | ||
|
||
#[async_trait] | ||
pub trait OAuthProvider: Send + Sync { | ||
fn name(&self) -> &'static str; | ||
|
||
fn build_auth_url( | ||
&self, | ||
config: &OAuthConfig, | ||
state: &str, | ||
pkce_challenge: Option<&str>, | ||
) -> Result<Url, anyhow::Error>; | ||
|
||
async fn exchange_code( | ||
&self, | ||
code: &str, | ||
config: &OAuthConfig, | ||
pkce_verifier: Option<&str>, | ||
) -> Result<TokenResponse, anyhow::Error>; | ||
|
||
async fn refresh_token( | ||
&self, | ||
refresh_token: &str, | ||
config: &OAuthConfig, | ||
) -> Result<TokenResponse, anyhow::Error>; | ||
|
||
fn metadata(&self) -> ProviderMetadata { | ||
ProviderMetadata::default() | ||
} | ||
} | ||
|
||
#[derive(Debug, Clone, Default)] | ||
pub struct ProviderMetadata { | ||
pub supports_pkce: bool, | ||
pub supports_refresh: bool, | ||
pub requires_client_secret: bool, | ||
pub auth_url: &'static str, | ||
pub token_url: &'static str, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
use serde::{Deserialize, Serialize}; | ||
use std::collections::HashMap; | ||
|
||
pub enum OAuthError { | ||
ProviderNotFound, | ||
} | ||
Comment on lines
+4
to
+6
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Add standard error traits for proper error handling. The enum should implement standard error traits for better integration with Rust's error handling ecosystem. +use std::fmt;
+
+#[derive(Debug)]
pub enum OAuthError {
ProviderNotFound,
}
+
+impl fmt::Display for OAuthError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ OAuthError::ProviderNotFound => write!(f, "OAuth provider not found"),
+ }
+ }
+}
+
+impl std::error::Error for OAuthError {}
🤖 Prompt for AI Agents
|
||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct OAuthConfig { | ||
pub client_id: String, | ||
#[serde(skip_serializing_if = "Option::is_none")] | ||
pub client_secret: Option<String>, | ||
pub redirect_uri: String, | ||
pub scopes: Vec<String>, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct OAuthRequest { | ||
pub provider: String, | ||
pub config: OAuthConfig, | ||
pub state: Option<String>, | ||
pub pkce_challenge: Option<String>, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct OAuthResponse { | ||
pub authorization_url: String, | ||
pub state: String, | ||
pub session_id: Option<String>, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct CallbackRequest { | ||
pub code: String, | ||
pub state: String, | ||
pub session_id: Option<String>, | ||
pub pkce_verifier: Option<String>, | ||
} | ||
|
||
#[derive(Debug, Clone, Serialize, Deserialize)] | ||
pub struct TokenResponse { | ||
pub access_token: String, | ||
pub refresh_token: Option<String>, | ||
pub expires_in: Option<i64>, | ||
pub token_type: String, | ||
#[serde(flatten)] | ||
pub extra: HashMap<String, serde_json::Value>, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
[package] | ||
name = "oauth-providers" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
[dependencies] | ||
hypr-oauth-core = { workspace = true } | ||
|
||
anyhow = { workspace = true } | ||
async-trait = { workspace = true } | ||
serde = { workspace = true, features = ["derive"] } | ||
serde_json = { workspace = true } | ||
url = { workspace = true } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Replace unwrap() with proper error handling.
The
unwrap()
call can cause panics if URL building fails. Use proper error propagation instead.📝 Committable suggestion
🤖 Prompt for AI Agents