octocrate_core/
app_authorization.rs

1use std::sync::Mutex;
2
3use crate::error::Error;
4use chrono::{DateTime, Utc};
5use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
6use octocrate_types::ExpirableToken;
7use serde::{Deserialize, Serialize};
8
9#[derive(Serialize, Deserialize, Debug)]
10pub struct AuthorizationPayload {
11  iat: i64,
12  exp: i64,
13  iss: String,
14}
15
16#[derive(Debug, Clone)]
17pub struct GeneratedToken {
18  token: String,
19  expires_at: DateTime<Utc>,
20}
21
22pub struct AppAuthorizationInner {
23  token: Mutex<Option<GeneratedToken>>,
24}
25
26pub struct AppAuthorization {
27  app_id: String,
28  private_key: String,
29  inner: AppAuthorizationInner,
30}
31
32impl AppAuthorization {
33  pub fn new(app_id: impl Into<String>, private_key: impl Into<String>) -> Self {
34    Self {
35      app_id: app_id.into(),
36      private_key: private_key.into(),
37      inner: AppAuthorizationInner {
38        token: Mutex::new(None),
39      },
40    }
41  }
42
43  fn generate_token(&self) -> Result<GeneratedToken, Error> {
44    let now = chrono::Utc::now().timestamp();
45    let expires_at = chrono::Utc::now() + chrono::Duration::seconds(60);
46    let payload = AuthorizationPayload {
47      iat: now,
48      exp: expires_at.timestamp(),
49      iss: self.app_id.clone(),
50    };
51
52    let token = encode(
53      &Header::new(Algorithm::RS256),
54      &payload,
55      &EncodingKey::from_rsa_pem(self.private_key.as_bytes()).unwrap(),
56    )
57    .map_err(|_| Error::Error("Failed to generate token".to_string()))?;
58
59    Ok(GeneratedToken { token, expires_at })
60  }
61}
62
63impl ExpirableToken for AppAuthorization {
64  fn get_token(&self) -> Option<String> {
65    let mut token = self.inner.token.lock().unwrap();
66
67    if let Some(token) = &*token {
68      if token.expires_at > chrono::Utc::now() {
69        return Some(token.token.clone());
70      }
71    }
72
73    match self.generate_token() {
74      Ok(new_token) => {
75        *token = Some(new_token.clone());
76        Some(new_token.token)
77      }
78      Err(_) => None,
79    }
80  }
81}