93 lines
2.8 KiB
Rust
93 lines
2.8 KiB
Rust
use std::collections::HashSet;
|
|
use std::ops::Add;
|
|
use std::sync::Arc;
|
|
use std::time::Duration;
|
|
|
|
use actix_jwt_authc::*;
|
|
use actix_http::StatusCode;
|
|
use actix_web::web::Data;
|
|
use actix_web::dev::{Service, ServiceResponse};
|
|
use actix_web::{get, test, App, HttpResponse};
|
|
use dashmap::DashSet;
|
|
use futures::channel::{mpsc, mpsc::{channel, Sender}};
|
|
use futures::SinkExt;
|
|
use futures::stream::Stream;
|
|
use jsonwebtoken::*;
|
|
use ring::rand::SystemRandom;
|
|
use ring::signature::{Ed25519KeyPair, KeyPair};
|
|
use serde::{Deserialize, Serialize};
|
|
use time::ext::*;
|
|
use time::OffsetDateTime;
|
|
use uuid::Uuid;
|
|
use tokio::sync::Mutex;
|
|
|
|
struct JwtSigningKeys {
|
|
encoding_key: EncodingKey,
|
|
decoding_key: DecodingKey,
|
|
}
|
|
|
|
impl JwtSigningKeys {
|
|
fn generate() -> Result<Self, Box<dyn std::error::Error>> {
|
|
let doc = Ed25519KeyPair::generate_pkcs8(&SystemRandom::new())?;
|
|
let keypair = Ed25519KeyPair::from_pkcs8(doc.as_ref())?;
|
|
let encoding_key = EncodingKey::from_ed_der(doc.as_ref());
|
|
let decoding_key = DecodingKey::from_ed_der(keypair.public_key().as_ref());
|
|
Ok(JwtSigningKeys {
|
|
encoding_key,
|
|
decoding_key,
|
|
})
|
|
}
|
|
}
|
|
|
|
const JWT_SIGNING_ALGO: Algorithm = Algorithm::EdDSA;
|
|
|
|
pub fn configure(config: &mut ServiceConfig) {
|
|
let jwt_signing_keys = JwtSigningKeys::generate()?;
|
|
let validator = Validation::new(JWT_SIGNING_ALGO);
|
|
let auth_middleware_settings = AuthenticateMiddlewareSettings {
|
|
jwt_decoding_key: jwt_signing_keys.decoding_key,
|
|
jwt_authorization_header_prefixes: Some(vec!["Bearer".to_string()]),
|
|
jwt_validator: validator,
|
|
jwt_session_key: jwt_signing_keys.encoding_key
|
|
};
|
|
}
|
|
|
|
#[get("/login")]
|
|
async fn login(
|
|
jwt_encoding_key: Data<EncodingKey>,
|
|
jwt_ttl: Data<JWTTtl>
|
|
) -> Result<HttpResponse, Error> {
|
|
let sub = format!("{}", Uuid::new_v4().as_u128());
|
|
let iat = OffsetDateTime::now_utc().unix_timestamp() as usize;
|
|
let expires_at = OffsetDateTime::now_utc().add(jwt_ttl.0);
|
|
let exp = expires_at.unix_timestamp() as usize;
|
|
|
|
let jwt_claims = Claims { iat, exp, sub };
|
|
let jwt_token = encode(
|
|
&Header::new(JWT_SIGNING_ALGO),
|
|
&jwt_claims,
|
|
&jwt_encoding_key,
|
|
)
|
|
.map_err(|_| Error::InternalError)?;
|
|
let login_response = LoginResponse {
|
|
bearer_token: jwt_token,
|
|
claims: jwt_claims,
|
|
};
|
|
|
|
Ok(HttpResponse::Ok().json(login_response))
|
|
}
|
|
|
|
#[get("/session")]
|
|
async fn session_info(authenticated: Authenticated<Claims>) -> Result<HttpResponse, Error> {
|
|
Ok(HttpResponse::Ok().json(authenticated))
|
|
}
|
|
|
|
#[get("/logout")]
|
|
async fn logout(
|
|
invalidated_jwts: Data<InvalidatedJWTStore>,
|
|
authenticated: Authenticated<Claims>
|
|
) -> Result<HttpResponse, Error> {
|
|
invalidated_jwts.add_to_invalidated(authenticated).await;
|
|
Ok(HttpResponse::Ok().json(EmptyResponse {}))
|
|
}
|