1use actix_web::{post, web, HttpResponse};
2use arc_auth_core::IdentityStore;
3use arc_core::session::{SessionRecord, SessionStore};
4use arc_web::{ArcAppBuilder, ArcPlugin};
5use serde::Deserialize;
6
7pub use arc_web::http::middlewares::jwt_middleware::JwtMiddleware as RequireJwt;
8
9#[derive(Deserialize)]
10struct SignIn {
11 email: String,
12 password: String,
13}
14#[post("/api/session")]
15async fn signin(
16 body: web::Json<SignIn>,
17 identities: web::Data<dyn IdentityStore>,
18 sessions: web::Data<dyn SessionStore>,
19) -> HttpResponse {
20 let user = match identities.authenticate(&body.email, &body.password).await {
21 Ok(user) => user,
22 Err(_) => {
23 return HttpResponse::Unauthorized()
24 .json(serde_json::json!({"error":"invalid credentials"}))
25 }
26 };
27 let (token, jti) = match arc_web::helpers::jwt::create_token(&user.id) {
28 Ok(pair) => pair,
29 Err(_) => return HttpResponse::InternalServerError().finish(),
30 };
31 let now = std::time::SystemTime::now()
32 .duration_since(std::time::UNIX_EPOCH)
33 .unwrap_or_default()
34 .as_micros() as i64;
35 let expires = now + (arc_web::helpers::jwt::get_jwt_expiry() as i64 * 3_600_000_000);
36 if sessions
37 .record_session(SessionRecord {
38 jti,
39 actor_id: user.id,
40 created_at_us: now,
41 expires_at_us: expires,
42 revoked_at_us: None,
43 })
44 .await
45 .is_err()
46 {
47 return HttpResponse::ServiceUnavailable().finish();
48 }
49 HttpResponse::Ok().json(serde_json::json!({"token":token,"token_type":"Bearer"}))
50}
51fn routes(cfg: &mut web::ServiceConfig) {
52 cfg.service(signin);
53}
54pub struct JwtAuthPlugin;
55#[async_trait::async_trait]
56impl ArcPlugin for JwtAuthPlugin {
57 fn name(&self) -> &'static str {
58 "auth-jwt"
59 }
60 fn register(&self, builder: ArcAppBuilder) -> ArcAppBuilder {
61 builder.register_routes(routes)
62 }
63}