use std::time::SystemTime;
use uuid::Uuid;
use crate::config::OAuthConfig;
use crate::error::{OAuthError, Result};
use crate::token_store::TokenRecord;
use super::TokenRequest;
use super::TokenResponse;
pub fn issue_token(config: &OAuthConfig, request: &TokenRequest) -> Result<TokenResponse> {
let client_id = request
.client_id
.as_deref()
.ok_or(OAuthError::InvalidClient)?;
let client_secret = request
.client_secret
.as_deref()
.ok_or(OAuthError::InvalidClient)?;
config.verify_client(client_id, client_secret)?;
if let Some(scope) = &request.scope {
if scope.trim().is_empty() {
return Err(OAuthError::InvalidScope("scope must not be blank".into()));
}
}
let access_token = Uuid::new_v4().to_string();
let refresh_token = if config.refresh_tokens_enabled() {
Some(Uuid::new_v4().to_string())
} else {
None
};
let expires_at = SystemTime::now()
.checked_add(config.access_token_ttl())
.ok_or_else(|| OAuthError::Internal("failed to compute access token expiry".into()))?;
let record = TokenRecord::new(
access_token.clone(),
refresh_token.clone(),
client_id.to_string(),
request.scope.clone(),
expires_at,
);
config.token_store().save(record)?;
Ok(TokenResponse::bearer(
access_token,
config.access_token_ttl(),
refresh_token,
request.scope.clone(),
))
}