octocrate_core/
app_authorization.rs1use 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}