Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
a71b602
Add Zendesk as an OAuth backend
augustuswm Apr 28, 2026
c1988ab
Add experimental crate for cli helpers
augustuswm Apr 29, 2026
7f99622
OAuth code proxy work
augustuswm Apr 29, 2026
eea958f
Fmt and lint
augustuswm Apr 30, 2026
3ada182
Lint and fmt
augustuswm Apr 30, 2026
cd8d1ee
A lot of refactoring of OAuth internals
augustuswm May 5, 2026
a5f6aec
Rough cut
augustuswm May 6, 2026
09711f5
Fmt
augustuswm May 6, 2026
8fea7ea
Clippy lints
augustuswm May 6, 2026
3aeddc4
Redirect fixes
augustuswm May 6, 2026
532feac
Filter proxy responses
augustuswm May 6, 2026
aa36bb1
More pkce support
augustuswm May 6, 2026
d4c6992
Early return on invalid scope
augustuswm May 6, 2026
5d94c95
Add check on provider
augustuswm May 6, 2026
b87a944
Error fixes
augustuswm May 6, 2026
77383ee
Adding redirect url validation
augustuswm May 6, 2026
014ea59
Validate pkce challenge
augustuswm May 6, 2026
12abe10
Handle idp user info errors
augustuswm May 6, 2026
0a5c7d5
Skip serialize on secret
augustuswm May 6, 2026
0f903fc
Cookie scoping
augustuswm May 6, 2026
68f7f3c
Fixes for login attempt state transitions
augustuswm May 6, 2026
418126d
Fmt
augustuswm May 6, 2026
1b284c2
More clippy fixes
augustuswm May 6, 2026
bf412d0
Fmt
augustuswm May 6, 2026
91b18c3
More spec compliance
augustuswm May 6, 2026
6efc48b
Enfore permission
augustuswm May 7, 2026
61646b1
Fmt
augustuswm May 7, 2026
a0ba072
Merge branch 'main' into oauth-rework
augustuswm May 7, 2026
e7de8f1
Merge fixes
augustuswm May 7, 2026
1733c8d
Fmt
augustuswm May 7, 2026
a539ffe
Remove extraneous dep
augustuswm May 7, 2026
0f9b4b3
Update v-cli-sdk/src/cmd/auth/oauth/code.rs
augustuswm May 7, 2026
29f2c7a
Update v-api/src/endpoints/login/oauth/remote/mod.rs
augustuswm May 7, 2026
e984b91
Update v-cli-sdk/src/cmd/auth/oauth/device.rs
augustuswm May 7, 2026
16257bb
Update v-api/src/endpoints/login/oauth/flow/mod.rs
augustuswm May 7, 2026
a1ce9ed
Fix local dev endpoints
augustuswm May 7, 2026
2c22bc0
Pass down provider
augustuswm May 7, 2026
450ade6
Fmt
augustuswm May 7, 2026
1a7b798
Permissions need to be resolved during login to determine idp token a…
augustuswm May 7, 2026
0e8bb73
Add support for Zendesk expires_in extension
augustuswm May 7, 2026
9dbfee4
More changes to align with spec
augustuswm May 7, 2026
2115ca4
Verify provider
augustuswm May 7, 2026
a20ef12
Fix for race with redirect uris
augustuswm May 7, 2026
8306f45
Version bump
augustuswm May 7, 2026
4bc588e
Remove percent-encoding
augustuswm May 7, 2026
59bc8a5
Merge branch 'main' into oauth-rework
augustuswm May 7, 2026
9b7d7db
Fix new provider info
augustuswm May 7, 2026
d0842df
Clean up from main merge. Fix config error consumed. Update state tra…
augustuswm May 7, 2026
09f4eed
One more error propagation fix
augustuswm May 7, 2026
51fb286
More cleanup and notes
augustuswm May 7, 2026
2d26489
Use trait mapping to HttpError
augustuswm May 7, 2026
1f26a14
Fix for revocation when user has not requested access to a token
augustuswm May 7, 2026
af5c72c
Fix incorrect debug message
augustuswm May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 68 additions & 8 deletions Cargo.lock

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

8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ members = [
"v-api-installer",
"v-api-param",
"v-api-permission-derive",
"v-cli-sdk",
"v-model",
"xtask"
]
Expand All @@ -12,7 +13,7 @@ resolver = "2"
[workspace.package]
publish = true
edition = "2024"
version = "0.3.0"
version = "0.4.0"

[workspace.dependencies]
anyhow = "1.0"
Expand All @@ -34,14 +35,16 @@ hex = "0.4.3"
http = "1"
http-body-util = "0.1.3"
hyper = "1.9.0"
hyper-util = "0.1"
jsonwebtoken = { version = "10.2", features = ["aws_lc_rs"] }
mockall = "0.14.0"
newtype-uuid = { version = "1.3.2", features = ["schemars08", "serde", "v4"] }
oauth2 = { version = "5.0.0", default-features = false }
oauth2-reqwest = "0.1.0-alpha.3"
owo-colors = "4.2.3"
partial-struct = { git = "https://github.com/oxidecomputer/partial-struct" }
percent-encoding = "2.3.2"
proc-macro2 = "1"
progenitor-client = "0.14.0"
quote = "1"
rand = "0.10.1"
rand_core = "0.10.1"
Expand All @@ -58,6 +61,7 @@ sha2 = "0.11.0"
slog = "2.8.2"
steno = { git = "https://github.com/oxidecomputer/steno" }
syn = "2"
tabwriter = "1.4.1"
tap = "1.0.1"
tempfile = "3"
thiserror = "2"
Expand Down
2 changes: 2 additions & 0 deletions v-api-permission-derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,7 @@ fn from_system_permission_tokens(
VPermission::ManageMagicLinkClientsAll => Self::ManageMagicLinkClientsAll,

VPermission::CreateAccessToken => Self::CreateAccessToken,
VPermission::RetrieveRemoteAccessToken => Self::RetrieveRemoteAccessToken,

VPermission::GetSagasAll => Self::GetSagasAll,
VPermission::ManageSagasAll => Self::ManageSagasAll,
Expand Down Expand Up @@ -722,6 +723,7 @@ fn system_permission_tokens() -> TokenStream {
ManageMagicLinkClientsAll,

CreateAccessToken,
RetrieveRemoteAccessToken,

#[v_api(scope(to = "saga:r", from = "saga:r"))]
GetSagasAll,
Expand Down
1 change: 0 additions & 1 deletion v-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ oauth2 = { workspace = true }
oauth2-reqwest = { workspace = true }
newtype-uuid = { workspace = true }
partial-struct = { workspace = true }
percent-encoding = { workspace = true }
rand = { workspace = true, features = ["std"] }
reqwest = { workspace = true }
rsa = { workspace = true, features = ["sha2"] }
Expand Down
108 changes: 97 additions & 11 deletions v-api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,23 @@ use jsonwebtoken::jwk::{
AlgorithmParameters, CommonParameters, Jwk, KeyAlgorithm, PublicKeyUse, RSAKeyParameters,
RSAKeyType,
};
use newtype_uuid::TypedUuid;
use partial_struct::partial;
use rsa::{
RsaPrivateKey, RsaPublicKey,
pkcs1v15::{SigningKey, VerifyingKey},
pkcs8::{DecodePrivateKey, DecodePublicKey},
traits::PublicKeyParts,
};
use secrecy::ExposeSecret;
use secrecy::{ExposeSecret, SecretString};
use serde::{
Deserialize, Deserializer,
de::{self, Visitor},
};
use std::path::PathBuf;
use thiserror::Error;
use v_api_param::StringParam;
use v_api_param::{ParamResolutionError, StringParam};
use v_model::OAuthClientId;

use crate::{
authn::{
Expand Down Expand Up @@ -151,25 +154,108 @@ pub struct SendGridConfig {
pub struct OAuthProviders {
pub github: Option<OAuthConfig>,
pub google: Option<OAuthConfig>,
pub zendesk: Option<OAuthConfig>,
}

#[derive(Debug, Deserialize)]
#[partial(ResolvedOAuthConfig)]
#[derive(Clone, Debug, Deserialize)]
pub struct OAuthConfig {
pub device: OAuthDeviceConfig,
pub web: OAuthWebConfig,
#[partial(ResolvedOAuthConfig(retype = Option<ResolvedOAuthDeviceConfig>))]
pub device: Option<OAuthDeviceConfig>,
#[partial(ResolvedOAuthConfig(retype = Option<ResolvedOAuthWebConfig>))]
pub web: Option<OAuthWebConfig>,
#[partial(ResolvedOAuthConfig(retype = Option<ResolvedOAuthWebProxyConfig>))]
pub proxy_web: Option<OAuthWebProxyConfig>,
}

#[derive(Debug, Deserialize)]
#[partial(ResolvedOAuthDeviceConfig)]
#[derive(Clone, Debug, Deserialize)]
pub struct OAuthDeviceConfig {
pub client_id: String,
pub client_secret: StringParam,
pub client_id: TypedUuid<OAuthClientId>,
pub remote_client_id: String,
#[partial(ResolvedOAuthDeviceConfig(retype = SecretString))]
pub remote_client_secret: StringParam,
}

#[derive(Debug, Deserialize)]
#[partial(ResolvedOAuthWebConfig)]
#[derive(Clone, Debug, Deserialize)]
pub struct OAuthWebConfig {
pub client_id: String,
pub client_secret: StringParam,
pub remote_client_id: String,
#[partial(ResolvedOAuthWebConfig(retype = SecretString))]
pub remote_client_secret: StringParam,
}

#[partial(ResolvedOAuthWebProxyConfig)]
#[derive(Clone, Debug, Deserialize)]
pub struct OAuthWebProxyConfig {
pub client_id: TypedUuid<OAuthClientId>,
pub redirect_uri: String,
pub proxy_port: u16,
}

impl OAuthConfig {
pub fn resolve(
&self,
base: Option<PathBuf>,
) -> Result<ResolvedOAuthConfig, ParamResolutionError> {
let device = self
.device
.as_ref()
.map(|d| d.resolve(base.clone()))
.transpose()?;
let web = self
.web
.as_ref()
.map(|w| w.resolve(base.clone()))
.transpose()?;
let proxy_web = self
.proxy_web
.as_ref()
.map(|p| p.resolve(base))
.transpose()?;
Ok(ResolvedOAuthConfig {
device,
web,
proxy_web,
})
}
}
impl OAuthDeviceConfig {
pub fn resolve(
&self,
base: Option<PathBuf>,
) -> Result<ResolvedOAuthDeviceConfig, ParamResolutionError> {
let remote_client_secret = self.remote_client_secret.resolve(base)?;
Ok(ResolvedOAuthDeviceConfig {
client_id: self.client_id,
remote_client_id: self.remote_client_id.clone(),
remote_client_secret,
})
}
}
impl OAuthWebConfig {
pub fn resolve(
&self,
base: Option<PathBuf>,
) -> Result<ResolvedOAuthWebConfig, ParamResolutionError> {
let remote_client_secret = self.remote_client_secret.resolve(base)?;
Ok(ResolvedOAuthWebConfig {
remote_client_id: self.remote_client_id.clone(),
remote_client_secret,
})
}
}
impl OAuthWebProxyConfig {
pub fn resolve(
&self,
_base: Option<PathBuf>,
) -> Result<ResolvedOAuthWebProxyConfig, ParamResolutionError> {
Ok(ResolvedOAuthWebProxyConfig {
client_id: self.client_id,
redirect_uri: self.redirect_uri.clone(),
proxy_port: self.proxy_port,
})
}
}

impl AsymmetricKey {
Expand Down
Loading