diff --git a/crates/tranquil-oauth-server/src/endpoints/par.rs b/crates/tranquil-oauth-server/src/endpoints/par.rs index dcaa075..c90e5d7 100644 --- a/crates/tranquil-oauth-server/src/endpoints/par.rs +++ b/crates/tranquil-oauth-server/src/endpoints/par.rs @@ -187,32 +187,6 @@ fn validate_scope( ))); } - let has_transition = requested_scopes.iter().any(|s| { - matches!( - parse_scope(s), - ParsedScope::TransitionGeneric - | ParsedScope::TransitionChat - | ParsedScope::TransitionEmail - ) - }); - let has_granular = requested_scopes.iter().any(|s| { - matches!( - parse_scope(s), - ParsedScope::Repo(_) - | ParsedScope::Blob(_) - | ParsedScope::Rpc(_) - | ParsedScope::Account(_) - | ParsedScope::Identity(_) - | ParsedScope::Include(_) - ) - }); - - if has_transition && has_granular { - return Err(OAuthError::InvalidScope( - "Cannot mix transition scopes with granular scopes. Use either transition:* scopes OR granular scopes (repo:*, blob:*, rpc:*, account:*, include:*), not both.".to_string() - )); - } - if let Some(client_scope) = &client_metadata.scope { let client_scopes: Vec<&str> = client_scope.split_whitespace().collect(); if let Some(unregistered) = requested_scopes @@ -300,3 +274,42 @@ fn parse_prompt(value: Option<&str>) -> Result, OAuthError> { ))), } } + +#[cfg(test)] +mod tests { + use super::validate_scope; + use tranquil_pds::oauth::OAuthError; + + #[test] + fn allows_transition_and_granular_scopes_together() { + let scope = Some( + "transition:generic repo:app.bsky.feed.post?action=create blob:image/*".to_string(), + ); + let client_metadata = tranquil_pds::oauth::ClientMetadata { + scope: scope.clone(), + ..Default::default() + }; + + assert_eq!(validate_scope(&scope, &client_metadata).unwrap(), scope); + } + + #[test] + fn still_rejects_unknown_scopes() { + let scope = Some("transition:generic unsupported:scope".to_string()); + let error = validate_scope(&scope, &Default::default()).unwrap_err(); + + assert!(matches!(error, OAuthError::InvalidScope(_))); + } + + #[test] + fn still_rejects_scopes_not_registered_by_client() { + let scope = Some("transition:generic repo:app.bsky.feed.post?action=create".to_string()); + let client_metadata = tranquil_pds::oauth::ClientMetadata { + scope: Some("transition:generic".to_string()), + ..Default::default() + }; + let error = validate_scope(&scope, &client_metadata).unwrap_err(); + + assert!(matches!(error, OAuthError::InvalidScope(_))); + } +}