use std::future::Future;
use std::time::Duration;
use auths_verifier::AssuranceLevel;
use thiserror::Error;
use crate::ports::network::NetworkError;
pub fn derive_assurance_level(platform: &str, cross_verified: bool) -> AssuranceLevel {
match platform {
"auths" => AssuranceLevel::Sovereign,
"github" => AssuranceLevel::Authenticated,
"npm" => AssuranceLevel::TokenVerified,
"pypi" if cross_verified => AssuranceLevel::TokenVerified,
"pypi" => AssuranceLevel::SelfAsserted,
_ => AssuranceLevel::SelfAsserted,
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum PlatformError {
#[error("OAuth authorization pending")]
AuthorizationPending,
#[error("OAuth slow down")]
SlowDown,
#[error("OAuth access denied")]
AccessDenied,
#[error("device code expired")]
ExpiredToken,
#[error("network error: {0}")]
Network(#[source] NetworkError),
#[error("platform error: {message}")]
Platform {
message: String,
},
}
impl auths_crypto::AuthsErrorInfo for PlatformError {
fn error_code(&self) -> &'static str {
match self {
Self::AuthorizationPending => "AUTHS-E3801",
Self::SlowDown => "AUTHS-E3802",
Self::AccessDenied => "AUTHS-E3803",
Self::ExpiredToken => "AUTHS-E3804",
Self::Network(_) => "AUTHS-E3805",
Self::Platform { .. } => "AUTHS-E3806",
}
}
fn suggestion(&self) -> Option<&'static str> {
match self {
Self::AccessDenied => Some("Re-run the command and approve the authorization request"),
Self::ExpiredToken => Some("The device code expired — restart the flow"),
Self::Network(_) => Some("Check your internet connection"),
Self::AuthorizationPending => Some(
"Complete the authorization on the linked device, then the CLI will continue automatically",
),
Self::SlowDown => {
Some("The authorization server is rate-limiting; the CLI will retry automatically")
}
Self::Platform { .. } => {
Some("A platform-specific error occurred; run `auths doctor` to diagnose")
}
}
}
}
pub struct DeviceCodeResponse {
pub device_code: String,
pub user_code: String,
pub verification_uri: String,
pub expires_in: u64,
pub interval: u64,
}
pub struct PlatformUserProfile {
pub login: String,
pub name: Option<String>,
}
pub struct ClaimResponse {
pub message: String,
}
pub trait OAuthDeviceFlowProvider: Send + Sync {
fn request_device_code(
&self,
client_id: &str,
scopes: &str,
) -> impl Future<Output = Result<DeviceCodeResponse, PlatformError>> + Send;
fn poll_for_token(
&self,
client_id: &str,
device_code: &str,
interval: Duration,
expires_in: Duration,
) -> impl Future<Output = Result<String, PlatformError>> + Send;
fn fetch_user_profile(
&self,
access_token: &str,
) -> impl Future<Output = Result<PlatformUserProfile, PlatformError>> + Send;
}
pub trait PlatformProofPublisher: Send + Sync {
fn publish_proof(
&self,
access_token: &str,
claim_json: &str,
) -> impl Future<Output = Result<String, PlatformError>> + Send;
}
pub trait RegistryClaimClient: Send + Sync {
fn submit_claim(
&self,
registry_url: &str,
did: &str,
proof_url: &str,
) -> impl Future<Output = Result<ClaimResponse, PlatformError>> + Send;
}
pub trait SshSigningKeyUploader: Send + Sync {
fn upload_signing_key(
&self,
access_token: &str,
public_key: &str,
title: &str,
) -> impl Future<Output = Result<String, PlatformError>> + Send;
}