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> { 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, jwt_ttl: Data ) -> Result { 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) -> Result { Ok(HttpResponse::Ok().json(authenticated)) } #[get("/logout")] async fn logout( invalidated_jwts: Data, authenticated: Authenticated ) -> Result { invalidated_jwts.add_to_invalidated(authenticated).await; Ok(HttpResponse::Ok().json(EmptyResponse {})) }