ironshield_api/handler/
response.rs

1//! # Response Handler and functions.
2
3use axum::extract::Json;
4
5use ironshield_types::{
6    load_private_key_from_env, 
7    load_public_key_from_env,
8    IronShieldChallengeResponse, 
9    IronShieldToken
10};
11use crate::constant;
12use crate::handler::{
13    error::{
14        ErrorHandler,
15        PUB_KEY_FAIL,
16        SIG_KEY_FAIL,
17        INVALID_SOLUTION,
18        SIGNATURE_FAIL
19    },
20    result::ResultHandler
21};
22
23use serde_json::{
24    json,
25    Value
26};
27
28pub async fn handle_challenge_response(
29    Json(payload): Json<IronShieldChallengeResponse>,
30) -> ResultHandler<Json<Value>> {
31    // Validate the challenge response.
32    validate_challenge_response(&payload)?;
33    
34    // Verify the proof-of-work solution and generate a token.
35    let token = verify_and_generate_token(payload).await?;
36    
37    // Return the authentication token.
38    Ok(Json(json!({
39        "status":  constant::STATUS_OK,
40        "message": constant::STATUS_OK_MSG,
41        "token":   token
42    })))
43}
44
45fn validate_challenge_response(
46    response: &IronShieldChallengeResponse
47) -> ResultHandler<()> {
48    if response.solution < 0 {
49        return Err(ErrorHandler::InvalidRequest(format!("{}: {}", INVALID_SOLUTION, response.solution)));
50    }
51    
52    Ok(())
53}
54
55async fn verify_and_generate_token(
56    response: IronShieldChallengeResponse
57) -> ResultHandler<IronShieldToken> {
58    // TODO: Retrieve the original challenge from the cache.
59    // TODO: Verify the solution using ironshield-core.
60    
61    // Allow for one-hour validity for the token.
62    let valid_for = chrono::Utc::now().timestamp_millis() + (60 * 60 * 1000);
63
64    // Signatures should cover challenge_signature + valid_for
65    // to prevent tampering.
66    let signing_key = load_private_key_from_env()
67        .map_err(|e| ErrorHandler::ProcessingError(format!("{}: {}", SIG_KEY_FAIL, e)))?;
68    let public_key = load_public_key_from_env()
69        .map_err(|e| ErrorHandler::ProcessingError(format!("{}: {}", PUB_KEY_FAIL, e)))?
70        .to_bytes();
71    
72    let auth_msg = format!(
73        "{}|{}",
74        hex::encode(response.solved_challenge.challenge_signature),
75        valid_for
76    );
77    
78    // Signing the authentication message.
79    let auth_signature = ironshield_types::generate_signature(&signing_key, &auth_msg)
80        .map_err(|e| ErrorHandler::ProcessingError(format!("{}: {}", SIGNATURE_FAIL, e)))?;
81    
82    let token = IronShieldToken::new(
83        response.solved_challenge.challenge_signature,
84        valid_for,
85        public_key,
86        auth_signature,
87    );
88    
89    Ok(token)
90}