use crate::token::Token;
use crate::ResultUntagged;
use std::time::Duration;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum GrantType {
RefreshToken,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AccessTokenRequest {
pub grant_type: GrantType,
pub refresh_token: Token,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum TokenType {
Bearer,
}
pub type AccessTokenResponse = ResultUntagged<AccessTokenResponseBody, AccessTokenResponseError>;
#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct AccessTokenResponseBody {
pub access_token: Token,
pub token_type: TokenType,
#[serde(with = "token_expiration")]
pub expires_in: Option<Duration>,
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize, thiserror::Error)]
#[serde(
rename_all = "snake_case",
tag = "error",
content = "error_description"
)]
pub enum AccessTokenResponseError {
#[error("invalid request, description: {0:?}")]
InvalidRequest(Option<String>),
#[error("invalid clientid or secret, description: {0:?}")]
InvalidClient(Option<String>),
#[error("invalid grant, description: {0:?}")]
InvalidGrant(Option<String>),
#[error("invalid scope, description: {0:?}")]
InvalidScope(Option<String>),
#[error("unauthorized client, description: {0:?}")]
UnauthorizedClient(Option<String>),
#[error("unsupported grant type, description: {0:?}")]
UnsupportedGrantType(Option<String>),
}
#[cfg(feature = "actix")]
impl actix_web::ResponseError for AccessTokenResponseError {
fn status_code(&self) -> actix_web::http::StatusCode {
use actix_web::http::StatusCode;
match self {
Self::InvalidRequest(_) => StatusCode::BAD_REQUEST,
Self::InvalidClient(_) => StatusCode::UNAUTHORIZED,
Self::InvalidGrant(_) => StatusCode::BAD_REQUEST,
Self::InvalidScope(_) => StatusCode::BAD_REQUEST,
Self::UnauthorizedClient(_) => StatusCode::BAD_REQUEST,
Self::UnsupportedGrantType(_) => StatusCode::BAD_REQUEST,
}
}
fn error_response(&self) -> actix_web::HttpResponse {
let response = AccessTokenResponse::Err(self.clone());
let json = actix_web::web::Json(response);
actix_web::HttpResponse::build(self.status_code()).json(json)
}
}
mod token_expiration {
use super::*;
use serde::{
de::{self, Visitor},
ser,
};
pub struct TokenExpirationVisitor;
impl<'de> Visitor<'de> for TokenExpirationVisitor {
type Value = Option<Duration>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("duration in seconds")
}
fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
where
E: de::Error,
{
Ok(Some(Duration::from_secs(value)))
}
fn visit_some<D>(self, d: D) -> Result<Option<Duration>, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_i64(Self)
}
fn visit_none<E>(self) -> Result<Option<Duration>, E>
where
E: de::Error,
{
Ok(None)
}
fn visit_unit<E>(self) -> Result<Option<Duration>, E>
where
E: de::Error,
{
Ok(None)
}
}
pub fn serialize<S>(duration: &Option<Duration>, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
match *duration {
Some(duration) => serializer.serialize_some(&duration.as_secs()),
None => serializer.serialize_none(),
}
}
pub fn deserialize<'de, D>(d: D) -> Result<Option<Duration>, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_option(TokenExpirationVisitor)
}
}