DDtKey / protect-endpoints

Authorization extension for popular web-frameworks to protect your endpoints
Apache License 2.0
211 stars 16 forks source link

Getting a 401 after implementing protect attribute #92

Closed EvilWatermelon closed 7 months ago

EvilWatermelon commented 7 months ago

After implementing #[protect(any("ADMIN"))] I get a 401 with JWT in actix-web-httpauth. If I do it the manual way with an if expression then it works well.

#[utoipa::path(
    context_path = "/management",
    tag = "management",
    request_body = Signup,
    responses(
        (status = 201, description = "Successfully created user."),
        (status = 401, description = "Access token is missing or invalid"),
        (status = 500, 
            description = "Failed to register user", 
            body = ErrorResponse, 
            example = json!(ErrorResponse::BadGateway(String::from("Cannot create new user")))
        ),
        (status = 403, description = "Only admins can create new users", body = String)
    ),
    security(
        ("bearerAuth" = [])
    )
)]
#[post("/teacher/signup")]
#[protect(any("ADMIN"))]
pub async fn signup_teacher(
    user_data: web::Json<User>,
) -> impl Responder {

    let user: Signup = Signup { 
        user_id: user_data.user_id.clone(),
        password: PasswordGenerator::new()
            .length(10)
            .numbers(true)
            .lowercase_letters(true)
            .uppercase_letters(true)
            .symbols(true)
            .spaces(false)
            .exclude_similar_characters(false)
            .strict(true)
            .generate_one()
            .unwrap(),
    };

    let _ = user_service::signup(user.clone(), false, None).await;

    HttpResponse::Created().json(user)
}

In your jwt-httpauth example you have a comment that says that I have to use use actix_web_grants::authorities::AttachAuthorities; but never used it.

DDtKey commented 7 months ago

Hi there 👋

Is it possible to provide the config of jwt-auth part? Would be much easier to figure out the reason

In your jwt-httpauth example you have a comment that says that I have to use use actix_web_grants::authorities::AttachAuthorities; but never used it

In the example you can see

req.attach(claims.permissions);

It's usage of the attach method from the trait. Otherwise how it's gonna be identified that user has any permission? Do you use actix-web extensions directly?

EvilWatermelon commented 7 months ago

It's usage of the attach method from the trait. Otherwise how it's gonna be identified that user has any permission?

I validate my tokens in these two functions. Do you think I have to replace req.extensions_mut().insert(res.claims); with req.attach(res.claims)? Or do I have to add it after req.extensions_mut().insert(res.claims);?

Do you use actix-web extensions directly?

Yes, I'm using the actix-web extensions directly.

/// Checking if a token is valid
async fn validator(
    req: ServiceRequest,
    credentials: BearerAuth,
) -> Result<ServiceRequest, (Error, ServiceRequest)> {
    let config: bearer::Config = req.app_data::<bearer::Config>()
            .cloned()
            .unwrap_or_default()
            .scope("urn:example:channel=HBO&urn:example:rating=G,PG-13");

    match auth_service::validate_token(credentials.token()) {
        Ok(res) => {
            info!("Valid request");
            req.extensions_mut().insert(res.claims);
            Ok(req)
        },
        Err(err) => {
            debug!("HttpResponse: \n{:#?}", err);
            Err((AuthenticationError::from(config).into(), req))
        }
    }
}
#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
pub struct Claims {
    pub sub: String,
    pub iat: usize,
    pub exp: usize,
    pub permissions: Vec<String>
}

pub fn validate_token(token: &str) -> Result<jsonwebtoken::TokenData<Claims>, HttpResponse> {
    info!("Validate token...");

    let env: String = std::env::var("PUB_KEY")
        .expect("PUB_KEY must be set");
    let read_from_file: String = std::fs::read_to_string(env).expect("Cannot find file");
    let key: &[u8] = read_from_file.as_bytes();

    match decode::<Claims>(&token, &DecodingKey::from_ec_pem(key).unwrap(), &Validation::new(Algorithm::ES256)) {
        Ok(c) => {
            info!("{:?}", c);
            Ok(c)
        },
        Err(err) => match *err.kind() {
            ErrorKind::InvalidToken => {
                debug!("{}", err);
                Err(HttpResponse::Unauthorized().body(err.to_string()))
            },
            ErrorKind::InvalidIssuer => {
                debug!("{}", err);
                Err(HttpResponse::BadRequest().body(err.to_string()))
            },
            _ => {
                debug!("{}", err);
                Err(HttpResponse::BadGateway().body(err.to_string()))
            },
        },
    }
}
EvilWatermelon commented 7 months ago

Adding req.attach(res.claims.permissions); fixed it!

I was confused because I do not know why use actix_web_grants::authorities::AttachAuthorities; allows me to call req.attach(). I think I have to learn more about traits