Skip to main content

github_app_forge/
jwt.rs

1//! JWT generation for GitHub App authentication.
2//!
3//! After app creation we have the App ID + private key (PEM). For any API
4//! call that operates on the App's identity (e.g. listing installations,
5//! issuing installation access tokens), we sign a short-lived JWT with the
6//! private key as RS256. GitHub then trades that JWT for an installation
7//! access token scoped to a specific installation.
8
9use anyhow::{Context, Result};
10use chrono::Utc;
11use jsonwebtoken::{encode, EncodingKey, Header};
12use serde::Serialize;
13
14#[derive(Debug, Serialize)]
15struct Claims {
16    iat: i64,
17    exp: i64,
18    iss: String,
19}
20
21/// Sign a JWT for the given app id + PEM private key. Lives 9 minutes
22/// (GitHub's max is 10; subtract 1 for clock skew).
23pub fn sign_app_jwt(app_id: u64, private_key_pem: &str) -> Result<String> {
24    let now = Utc::now().timestamp();
25    let claims = Claims {
26        iat: now - 60, // 1 min back-dated for clock skew
27        exp: now + 9 * 60,
28        iss: app_id.to_string(),
29    };
30    let key = EncodingKey::from_rsa_pem(private_key_pem.as_bytes())
31        .context("failed to parse RSA private key from PEM")?;
32    encode(&Header::new(jsonwebtoken::Algorithm::RS256), &claims, &key)
33        .context("failed to sign JWT")
34}