Skip to content

Commit 6a2278a

Browse files
authored
Merge pull request #72 from tankyleo/rust-jwt-auth
Add option to verify JWT tokens in the HTTP Authorization header
2 parents 56be2d9 + fed1cfe commit 6a2278a

File tree

6 files changed

+77
-28
lines changed

6 files changed

+77
-28
lines changed

rust/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/auth-impls/src/lib.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,11 @@ pub(crate) struct Claims {
4141
const BEARER_PREFIX: &str = "Bearer ";
4242

4343
impl JWTAuthorizer {
44-
/// Create new instance of [`JWTAuthorizer`]
45-
pub async fn new(jwt_issuer_key: DecodingKey) -> Self {
46-
Self { jwt_issuer_key }
44+
/// Creates a new instance of [`JWTAuthorizer`], fails on failure to parse the PEM formatted RSA public key
45+
pub async fn new(rsa_pem: &str) -> Result<Self, String> {
46+
let jwt_issuer_key =
47+
DecodingKey::from_rsa_pem(rsa_pem.as_bytes()).map_err(|e| e.to_string())?;
48+
Ok(Self { jwt_issuer_key })
4749
}
4850
}
4951

@@ -74,7 +76,7 @@ mod tests {
7476
use crate::JWTAuthorizer;
7577
use api::auth::Authorizer;
7678
use api::error::VssError;
77-
use jsonwebtoken::{encode, Algorithm, DecodingKey, EncodingKey, Header};
79+
use jsonwebtoken::{encode, Algorithm, EncodingKey, Header};
7880
use serde::{Deserialize, Serialize};
7981
use std::collections::HashMap;
8082
use std::time::SystemTime;
@@ -132,7 +134,7 @@ mod tests {
132134
)
133135
.expect("Failed to create Encoding Key.");
134136

135-
let decoding_key = DecodingKey::from_rsa_pem(
137+
let decoding_key = String::from(
136138
"-----BEGIN PUBLIC KEY-----\
137139
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAysGpKU+I9i9b+QZSANu/\
138140
ExaA6w4qiQdFZaXeReiz49r1oDfABwKIFW9gK/kNnrnL9H8P+pYfj7jqUJ/glmgq\
@@ -141,12 +143,10 @@ mod tests {
141143
8YsTa5piV8KgJpG/rwYTGXuu3lcCmnWwjmbeDq1zFFrCDDVkaIHkGJgRuFIDPXaH\
142144
yUw5H2HvKlP94ySbvTDLXWZj6TyzHEHDbstqs4DgvurB/bIhi/dQ7zK3EIXL8KRB\
143145
hwIDAQAB\
144-
-----END PUBLIC KEY-----"
145-
.as_bytes(),
146-
)
147-
.expect("Failed to create Decoding Key.");
146+
-----END PUBLIC KEY-----",
147+
);
148148

149-
let jwt_authorizer = JWTAuthorizer::new(decoding_key).await;
149+
let jwt_authorizer = JWTAuthorizer::new(&decoding_key).await.unwrap();
150150

151151
let valid_jwt_token =
152152
encode(&Header::new(Algorithm::RS256), &claims, &valid_encoding_key).unwrap();

rust/server/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ edition = "2021"
55

66
[dependencies]
77
api = { path = "../api" }
8+
auth-impls = { path = "../auth-impls" }
89
impls = { path = "../impls" }
910

1011
hyper = { version = "1", default-features = false, features = ["server", "http1"] }

rust/server/src/main.rs

Lines changed: 44 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,23 @@
1010
#![deny(missing_docs)]
1111

1212
use std::net::SocketAddr;
13+
use std::sync::Arc;
1314

1415
use tokio::net::TcpListener;
1516
use tokio::signal::unix::SignalKind;
1617

1718
use hyper::server::conn::http1;
1819
use hyper_util::rt::TokioIo;
1920

20-
use crate::vss_service::VssService;
2121
use api::auth::{Authorizer, NoopAuthorizer};
2222
use api::kv_store::KvStore;
23+
use auth_impls::JWTAuthorizer;
2324
use impls::postgres_store::{Certificate, PostgresPlaintextBackend, PostgresTlsBackend};
24-
use std::sync::Arc;
25+
use util::config::{Config, ServerConfig};
26+
use vss_service::VssService;
2527

26-
pub(crate) mod util;
27-
pub(crate) mod vss_service;
28+
mod util;
29+
mod vss_service;
2830

2931
fn main() {
3032
let args: Vec<String> = std::env::args().collect();
@@ -33,22 +35,21 @@ fn main() {
3335
std::process::exit(1);
3436
}
3537

36-
let config = match util::config::load_config(&args[1]) {
37-
Ok(cfg) => cfg,
38-
Err(e) => {
39-
eprintln!("Failed to load configuration: {}", e);
40-
std::process::exit(1);
41-
},
42-
};
43-
44-
let addr: SocketAddr =
45-
match format!("{}:{}", config.server_config.host, config.server_config.port).parse() {
46-
Ok(addr) => addr,
38+
let Config { server_config: ServerConfig { host, port }, jwt_auth_config, postgresql_config } =
39+
match util::config::load_config(&args[1]) {
40+
Ok(cfg) => cfg,
4741
Err(e) => {
48-
eprintln!("Invalid host/port configuration: {}", e);
42+
eprintln!("Failed to load configuration: {}", e);
4943
std::process::exit(1);
5044
},
5145
};
46+
let addr: SocketAddr = match format!("{}:{}", host, port).parse() {
47+
Ok(addr) => addr,
48+
Err(e) => {
49+
eprintln!("Invalid host/port configuration: {}", e);
50+
std::process::exit(1);
51+
},
52+
};
5253

5354
let runtime = match tokio::runtime::Builder::new_multi_thread().enable_all().build() {
5455
Ok(runtime) => Arc::new(runtime),
@@ -66,9 +67,33 @@ fn main() {
6667
std::process::exit(-1);
6768
},
6869
};
69-
let authorizer: Arc<dyn Authorizer> = Arc::new(NoopAuthorizer {});
70+
71+
let rsa_pem_env = match std::env::var("VSS_JWT_RSA_PEM") {
72+
Ok(env) => Some(env),
73+
Err(std::env::VarError::NotPresent) => None,
74+
Err(e) => {
75+
println!("Failed to load the VSS_JWT_RSA_PEM env var: {}", e);
76+
std::process::exit(-1);
77+
},
78+
};
79+
let rsa_pem = rsa_pem_env.or(jwt_auth_config.map(|config| config.rsa_pem));
80+
let authorizer: Arc<dyn Authorizer> = if let Some(pem) = rsa_pem {
81+
let authorizer = match JWTAuthorizer::new(pem.as_str()).await {
82+
Ok(auth) => auth,
83+
Err(e) => {
84+
println!("Failed to parse the PEM formatted RSA public key: {}", e);
85+
std::process::exit(-1);
86+
},
87+
};
88+
println!("Configured JWT authorizer with RSA public key");
89+
Arc::new(authorizer)
90+
} else {
91+
println!("No JWT authentication method configured");
92+
Arc::new(NoopAuthorizer {})
93+
};
94+
7095
let postgresql_config =
71-
config.postgresql_config.expect("PostgreSQLConfig must be defined in config file.");
96+
postgresql_config.expect("PostgreSQLConfig must be defined in config file.");
7297
let endpoint = postgresql_config.to_postgresql_endpoint();
7398
let db_name = postgresql_config.database;
7499
let store: Arc<dyn KvStore> = if let Some(tls_config) = postgresql_config.tls {
@@ -109,6 +134,7 @@ fn main() {
109134
Arc::new(postgres_plaintext_backend)
110135
};
111136
println!("Connected to PostgreSQL backend with DSN: {}/{}", endpoint, db_name);
137+
112138
let rest_svc_listener =
113139
TcpListener::bind(&addr).await.expect("Failed to bind listening port");
114140
println!("Listening for incoming connections on {}", addr);

rust/server/src/util/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use serde::Deserialize;
33
#[derive(Deserialize)]
44
pub(crate) struct Config {
55
pub(crate) server_config: ServerConfig,
6+
pub(crate) jwt_auth_config: Option<JwtAuthConfig>,
67
pub(crate) postgresql_config: Option<PostgreSQLConfig>,
78
}
89

@@ -12,6 +13,11 @@ pub(crate) struct ServerConfig {
1213
pub(crate) port: u16,
1314
}
1415

16+
#[derive(Deserialize)]
17+
pub(crate) struct JwtAuthConfig {
18+
pub(crate) rsa_pem: String,
19+
}
20+
1521
#[derive(Deserialize)]
1622
pub(crate) struct PostgreSQLConfig {
1723
pub(crate) username: Option<String>, // Optional in TOML, can be overridden by env

rust/server/vss-server-config.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,21 @@
22
host = "127.0.0.1"
33
port = 8080
44

5+
# Uncomment the table below to verify JWT tokens in the HTTP Authorization header against the given RSA public key,
6+
# can be overridden by env var `VSS_JWT_RSA_PEM`
7+
# [jwt_auth_config]
8+
# rsa_pem = """
9+
# -----BEGIN PUBLIC KEY-----
10+
# MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAstPJs4ut+tFAI0qrOyGt
11+
# /3FN5jWc5gLv/j9Rc6lgr4hm7lyR05PU/G+4rfxdXGNyGTlQ6dRqcVy78CjxWz9f
12+
# 8l08EKLERPh8JhE5el6vr+ehWD5iQxSP3ejpx0Mr977fKMNKg6jlFiL+y50hOEp2
13+
# 6iN9QzZQjLxotDT3aQvbCA/DZpI+fV6WKDKWGS+pZGDVgOz5x/RcStJQXxkX3ACK
14+
# WhVdrtN3h6mHlhIt7ZIqVvQmY4NL03QPyljt13sYHoiFaoxINF/funBMCjrfSLcB
15+
# ko1rWE2BWdOrFqi27RtBs5AHOSAWXuz/2SUGpFuTQuJi7U68QUfjKeQO46JpQf+v
16+
# kQIDAQAB
17+
# -----END PUBLIC KEY-----
18+
# """
19+
520
[postgresql_config]
621
username = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_USERNAME`
722
password = "postgres" # Optional in TOML, can be overridden by env var `VSS_POSTGRESQL_PASSWORD`

0 commit comments

Comments
 (0)