Skip to content

Commit 49761f8

Browse files
Amp AIclaude
andcommitted
feat(core): Implement OAuth flows and DefaultTokenManager (#9)
Introduce comprehensive OAuth 2.0 support with Authorization Code + PKCE and device code flows, along with a complete TokenManager implementation. - Add `ProviderConfig` for OAuth provider metadata - Add `ProviderRegistry` with pre-configured GitHub, Spotify, and Google providers - Store provider endpoints, capabilities (PKCE, device code), and default scopes - **PKCE Flow** (`oauth::pkce`): Authorization Code + PKCE for web/native apps - Generate PKCE code verifier and challenge - Build authorization URLs with CSRF state protection - Exchange authorization codes for tokens - Local callback listener for redirect handling - **Device Code Flow** (`oauth::device_code`): For headless/limited-input devices - Request device and user codes - Poll token endpoint with proper error handling - Support authorization_pending, slow_down, expired_token errors - Implement `DefaultTokenManager<S: SecretStore>` - Automatic token refresh when expired (5-minute buffer) - Store/retrieve access tokens, refresh tokens, expiry, and scopes - Client credentials management via SecretStore - Rich error reporting (NotFound, Expired, RefreshFailed, etc.) - Add `TokenScopes` credential type for persisting OAuth scopes - Add `oauth2` v4.4 for OAuth protocol handling - Add `reqwest` v0.12 for HTTP client operations - Add `rand` v0.8 for secure random string generation - Add `wiremock` v0.6 for integration testing - Comprehensive integration tests for token refresh scenarios - Mock OAuth endpoints with wiremock - Test expiry detection, refresh success/failure, persistence - Test multi-account support and scope handling Closes #9 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent 1a6e8c2 commit 49761f8

File tree

10 files changed

+3075
-10
lines changed

10 files changed

+3075
-10
lines changed

Cargo.lock

Lines changed: 712 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sigilforge-core/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,15 @@ keyring = { workspace = true, optional = true }
4141
# OAuth2
4242
oauth2 = { workspace = true, optional = true }
4343
reqwest = { workspace = true, optional = true }
44+
rand = { version = "0.8", optional = true }
4445

4546
[features]
4647
default = ["keyring-store"]
4748
keyring-store = ["dep:keyring"]
48-
oauth = ["dep:oauth2", "dep:reqwest"]
49+
oauth = ["dep:oauth2", "dep:reqwest", "dep:rand"]
4950
full = ["keyring-store", "oauth"]
5051

5152
[dev-dependencies]
5253
tokio = { workspace = true, features = ["test-util", "macros"] }
5354
tempfile = "3.13"
55+
wiremock = "0.6"

sigilforge-core/src/lib.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,15 @@ pub mod resolve;
2727
pub mod error;
2828
pub mod account_store;
2929

30+
#[cfg(feature = "oauth")]
31+
pub mod provider;
32+
33+
#[cfg(feature = "oauth")]
34+
pub mod token_manager;
35+
36+
#[cfg(feature = "oauth")]
37+
pub mod oauth;
38+
3039
// Re-export commonly used types at crate root
3140
pub use model::{
3241
ServiceId,
@@ -67,3 +76,12 @@ pub use account_store::{
6776
AccountStore,
6877
AccountStoreError,
6978
};
79+
80+
#[cfg(feature = "oauth")]
81+
pub use provider::{
82+
ProviderConfig,
83+
ProviderRegistry,
84+
};
85+
86+
#[cfg(feature = "oauth")]
87+
pub use token_manager::DefaultTokenManager;

sigilforge-core/src/model.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,9 @@ pub enum CredentialType {
165165
/// OAuth client secret (usually at provider level).
166166
ClientSecret,
167167

168+
/// OAuth scopes (comma-separated).
169+
TokenScopes,
170+
168171
/// Custom credential type.
169172
Custom(String),
170173
}
@@ -179,6 +182,7 @@ impl CredentialType {
179182
Self::ApiKey => "api_key",
180183
Self::ClientId => "client_id",
181184
Self::ClientSecret => "client_secret",
185+
Self::TokenScopes => "token_scopes",
182186
Self::Custom(s) => s,
183187
}
184188
}

0 commit comments

Comments
 (0)