Add authentication checking to api
This commit is contained in:
parent
81b4447c84
commit
00742c14a9
|
@ -3518,7 +3518,9 @@ dependencies = [
|
||||||
"rocket",
|
"rocket",
|
||||||
"rocket_cors",
|
"rocket_cors",
|
||||||
"serde",
|
"serde",
|
||||||
|
"substring",
|
||||||
"tenebrous-rpc",
|
"tenebrous-rpc",
|
||||||
|
"thiserror",
|
||||||
"tonic",
|
"tonic",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
]
|
]
|
||||||
|
|
|
@ -9,6 +9,8 @@ log = "0.4"
|
||||||
tracing-subscriber = "0.2"
|
tracing-subscriber = "0.2"
|
||||||
tonic = { version = "0.4" }
|
tonic = { version = "0.4" }
|
||||||
prost = "0.7"
|
prost = "0.7"
|
||||||
|
thiserror = "1.0"
|
||||||
|
substring = "1.4"
|
||||||
jsonwebtoken = "7.2"
|
jsonwebtoken = "7.2"
|
||||||
chrono = "0.4"
|
chrono = "0.4"
|
||||||
serde = {version = "1.0", features = ["derive"] }
|
serde = {version = "1.0", features = ["derive"] }
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::auth::User;
|
||||||
use crate::config::create_config;
|
use crate::config::create_config;
|
||||||
use crate::schema::{self, Context, Schema};
|
use crate::schema::{self, Context, Schema};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
@ -26,7 +27,9 @@ async fn post_graphql_handler(
|
||||||
context: &State<Context>,
|
context: &State<Context>,
|
||||||
request: juniper_rocket_async::GraphQLRequest,
|
request: juniper_rocket_async::GraphQLRequest,
|
||||||
schema: &State<Schema>,
|
schema: &State<Schema>,
|
||||||
|
user: User,
|
||||||
) -> juniper_rocket_async::GraphQLResponse {
|
) -> juniper_rocket_async::GraphQLResponse {
|
||||||
|
println!("User is {:?}", user);
|
||||||
request.execute(&*schema, &*context).await
|
request.execute(&*schema, &*context).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,59 @@
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
|
use crate::errors::ApiError;
|
||||||
use chrono::{Duration, Utc};
|
use chrono::{Duration, Utc};
|
||||||
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
|
||||||
use rocket::http::{Cookie, CookieJar};
|
use rocket::request::{self, FromRequest, Request};
|
||||||
use rocket::response::status::Custom;
|
use rocket::response::status::Custom;
|
||||||
use rocket::{
|
use rocket::{
|
||||||
http::Status,
|
http::Status,
|
||||||
serde::{json::Json, Deserialize, Serialize},
|
serde::{json::Json, Deserialize, Serialize},
|
||||||
};
|
};
|
||||||
|
use rocket::{
|
||||||
|
http::{Cookie, CookieJar},
|
||||||
|
outcome::Outcome,
|
||||||
|
};
|
||||||
use rocket::{routes, Route, State};
|
use rocket::{routes, Route, State};
|
||||||
use std::error::Error;
|
use substring::Substring;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct User {
|
||||||
|
username: String, //TODO more state and such here.
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decode_token(token: &str, config: &Config) -> Result<Claims, ApiError> {
|
||||||
|
let token_data = decode::<Claims>(
|
||||||
|
token,
|
||||||
|
&DecodingKey::from_secret(config.jwt_secret.as_bytes()),
|
||||||
|
&Validation::default(),
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(token_data.claims)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rocket::async_trait]
|
||||||
|
impl<'r> FromRequest<'r> for User {
|
||||||
|
type Error = ApiError;
|
||||||
|
|
||||||
|
async fn from_request(req: &'r Request<'_>) -> request::Outcome<Self, Self::Error> {
|
||||||
|
let config: Option<&Config> = req.rocket().state();
|
||||||
|
let auth_header = req
|
||||||
|
.headers()
|
||||||
|
.get_one("Authorization")
|
||||||
|
.map(|auth| auth.substring("Bearer ".len(), auth.len()));
|
||||||
|
|
||||||
|
let token = auth_header
|
||||||
|
.zip(config)
|
||||||
|
.map(|(encoded_token, app_cfg)| decode_token(encoded_token, app_cfg))
|
||||||
|
.unwrap_or(Err(ApiError::AuthenticationDenied("username".to_string())));
|
||||||
|
|
||||||
|
match token {
|
||||||
|
Err(e) => Outcome::Failure((Status::Forbidden, e)),
|
||||||
|
Ok(token) => Outcome::Success(User {
|
||||||
|
username: token.sub,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn routes() -> Vec<Route> {
|
pub(crate) fn routes() -> Vec<Route> {
|
||||||
routes![login]
|
routes![login]
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ApiError {
|
||||||
|
#[error("user account does not exist: {0}")]
|
||||||
|
UserDoesNotExist(String),
|
||||||
|
|
||||||
|
#[error("invalid password for user account: {0}")]
|
||||||
|
AuthenticationDenied(String),
|
||||||
|
|
||||||
|
#[error("authentication token missing from request")]
|
||||||
|
AuthenticationRequired,
|
||||||
|
|
||||||
|
#[error("error decoding token: {0}")]
|
||||||
|
TokenDecodingError(#[from] jsonwebtoken::errors::Error),
|
||||||
|
}
|
|
@ -1,4 +1,5 @@
|
||||||
pub mod api;
|
pub mod api;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod config;
|
pub mod config;
|
||||||
|
pub mod errors;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
|
Loading…
Reference in New Issue