use crate::Result;
use crate::UptrakitClient;
use crate::types_impl::auth::{
AuthResponse, LoginRequest, LogoutRequest, RefreshRequest, RefreshResponse, RegisterRequest,
UserResponse,
};
use crate::types_impl::device_auth::{DeviceAuthApproveRequest, DeviceAuthApproveResponse};
use crate::types_impl::oauth::{
DeviceAuthDenyRequest, DeviceAuthDenyResponse, DeviceAuthLookupQuery, DeviceAuthLookupResponse,
DeviceAuthorizationRequest, DeviceAuthorizationResponse, OAuthAuthorizationServerMetadata,
OAuthTokenRequest, OAuthTokenResponse,
};
use crate::types_impl::oidc_auth::AuthMethodsResponse;
impl UptrakitClient {
pub async fn register(&self, req: &RegisterRequest) -> Result<AuthResponse> {
self.post_json_unauth(crate::paths::auth::REGISTER, req)
.await
}
pub async fn login(&self, req: &LoginRequest) -> Result<AuthResponse> {
self.post_json_unauth(crate::paths::auth::LOGIN, req).await
}
pub async fn refresh(&self, req: &RefreshRequest) -> Result<RefreshResponse> {
self.post_json_unauth(crate::paths::auth::REFRESH, req)
.await
}
pub async fn logout(&self, req: &LogoutRequest) -> Result<()> {
self.post_json_no_content(crate::paths::auth::LOGOUT, req)
.await
}
pub async fn auth_methods(&self) -> Result<AuthMethodsResponse> {
self.get_unauth(crate::paths::auth::METHODS).await
}
pub async fn device_auth_approve(
&self,
req: &DeviceAuthApproveRequest,
) -> Result<DeviceAuthApproveResponse> {
self.post_json(crate::paths::auth::DEVICE_APPROVE, req)
.await
}
pub async fn me(&self) -> Result<UserResponse> {
self.get(crate::paths::auth::ME).await
}
pub async fn oauth_device_authorization(
&self,
req: &DeviceAuthorizationRequest,
) -> Result<DeviceAuthorizationResponse> {
self.post_form_unauth(crate::paths::oauth::DEVICE_AUTHORIZATION, req)
.await
}
pub async fn oauth_token(&self, req: &OAuthTokenRequest) -> Result<OAuthTokenResponse> {
self.post_form_unauth(crate::paths::oauth::TOKEN, req).await
}
pub async fn oauth_authorization_server_metadata(
&self,
) -> Result<OAuthAuthorizationServerMetadata> {
self.get_unauth(crate::paths::oauth::METADATA).await
}
pub async fn device_auth_deny(
&self,
req: &DeviceAuthDenyRequest,
) -> Result<DeviceAuthDenyResponse> {
self.post_json(crate::paths::auth::DEVICE_DENY, req).await
}
pub async fn device_auth_lookup(
&self,
query: &DeviceAuthLookupQuery,
) -> Result<DeviceAuthLookupResponse> {
self.get_with_query(crate::paths::auth::DEVICE_LOOKUP, query)
.await
}
}
#[cfg(test)]
mod tests {
use crate::types_impl::SecretString;
use crate::types_impl::auth::{LoginRequest, LogoutRequest, RefreshRequest, RegisterRequest};
use crate::types_impl::device_auth::DeviceAuthApproveRequest;
use crate::types_impl::oauth::{
DeviceAuthDenyRequest, DeviceAuthorizationRequest, OAuthTokenRequest,
};
#[test]
fn register_request_serialization() {
let req = RegisterRequest {
email: "admin@example.com".to_string(),
first_name: "Admin".to_string(),
last_name: "User".to_string(),
password: SecretString::new("SecurePass123"),
registration_token: None,
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["email"], "admin@example.com");
assert_eq!(json["first_name"], "Admin");
assert_eq!(json["last_name"], "User");
assert_eq!(json["password"], "SecurePass123");
}
#[test]
fn register_request_with_token_serialization() {
let req = RegisterRequest {
email: "admin@example.com".to_string(),
first_name: "Admin".to_string(),
last_name: "User".to_string(),
password: SecretString::new("SecurePass123"),
registration_token: Some(SecretString::new("invite-tok-abc")),
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["registration_token"], "invite-tok-abc");
}
#[test]
fn login_request_serialization() {
let req = LoginRequest {
email: "admin@example.com".to_string(),
password: SecretString::new("SecurePass123"),
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["email"], "admin@example.com");
assert_eq!(json["password"], "SecurePass123");
}
#[test]
fn logout_request_serialization() {
let req = LogoutRequest {
refresh_token: Some(SecretString::new("refresh-tok-xyz")),
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["refresh_token"], "refresh-tok-xyz");
}
#[test]
fn refresh_request_serialization() {
let req = RefreshRequest {
refresh_token: Some(SecretString::new("refresh-tok-xyz")),
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["refresh_token"], "refresh-tok-xyz");
}
#[test]
fn device_auth_approve_request_serialization() {
let req = DeviceAuthApproveRequest {
user_code: "ABCD-1234".to_string(),
};
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["user_code"], "ABCD-1234");
}
#[test]
fn device_authorization_request_form_serialization() {
use serde_urlencoded;
let req = DeviceAuthorizationRequest::new(
"uptrakit-cli".into(),
None,
Some("cli-host-2026-05-12".into()),
);
let encoded = serde_urlencoded::to_string(&req).expect("encode");
assert!(encoded.contains("client_id=uptrakit-cli"));
assert!(encoded.contains("client_name=cli-host-2026-05-12"));
assert!(!encoded.contains("scope="), "scope omitted when None");
}
#[test]
fn oauth_token_request_form_serialization() {
use serde_urlencoded;
let req = OAuthTokenRequest::new(
"urn:ietf:params:oauth:grant-type:device_code".into(),
Some("abc-123".into()),
Some("uptrakit-cli".into()),
);
let encoded = serde_urlencoded::to_string(&req).expect("encode");
assert!(
encoded.contains("grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Adevice_code")
|| encoded.contains("grant_type=urn:ietf:params:oauth:grant-type:device_code"),
"grant_type URI preserved verbatim"
);
assert!(encoded.contains("device_code=abc-123"));
assert!(encoded.contains("client_id=uptrakit-cli"));
}
#[test]
fn device_auth_deny_request_serialization() {
let req = DeviceAuthDenyRequest::new("ABCD-EFGH".into());
let json = serde_json::to_value(&req).expect("serialize");
assert_eq!(json["user_code"], "ABCD-EFGH");
}
}