Actix-webのFromRequestで認証処理を共通化する
注意:不正確な内容が含まれている可能性があるため、下記の参考 URL をまずは確認頂くのが良いと思います。
Actix Web/Rust API: Authorization Code Sample
auth0-developer-hub / api_actix-web_rust_hello-world
認証フローを共通化したい
Rust の Actix_web フレームワークを利用している API サーバーで、トークン認証をかけたいルート下記のようなコードを書いていました。アクセスする側は Authorization Header に JWT トークンを付与してアクセス、API 側はトークンを検証して認証するタイプです。
イメージ
let server = HttpServer::new(move || {
App::new()
.wrap(TracingLogger::default())
.route("/book", web::post().to(add_book))
pub async fn add_book(
req: HttpRequest,
pool: web::Data<PgPool>,
form: web::Json<FormData>,
) -> HttpResponse {
// request headerからtokenを取得する処理
let jwt = match get_token(&req) {
Ok(v) => v,
_ => {
// 認証Errの処理
}
};
// tokenを検証しOKなら処理を進める
match validate_token(&jwt) {
Ok(v) => {
// 認証OKの処理
}
Err(_) => {
// 認証Errの処理
}
}
// 認証Errの処理
}
トークンを受け取る、トークンを検証する処理は認証用ルートでは同じものを使いまわしたいが方法が良くわからず、調べていると下記の記事やコード例で FromRequest を利用していたため真似しました。
Actix Web/Rust API: Authorization Code Sample
auth0-developer-hub / api_actix-web_rust_hello-world
actix_web の FromRequest を実装した struct を作成する
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub exp: i64,
_permissions: Option<HashSet<String>>,
}
impl FromRequest for Claims {
type Error = Error;
type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future {
let request = req.clone();
Box::pin(async move {
let jwt = match get_token(&request) {
Ok(v) => v,
_ => {
return Err(ErrorUnauthorized("this route is protected"))
}
};
match validate_jwt_token(&jwt) {
Ok(v) => {
Ok(v)
},
// ValidationErrorは独自に実装したエラー
Err(ValidationError::TokenExpired) => Err(ErrorUnauthorized("The access token expired.")),
Err(ValidationError::InvalidToken) => Err(ErrorUnauthorized("The access token invalid.")),
_ => Err(ErrorInternalServerError("internal server error."))
}
})
}
}
認証ルートで行っていた、トークンの取得、検証処理を from_request で行います。トークン認証が失敗したら 401 か 500 が返却されて処理が進みません。
元の認証ルートに Claims を利用する
元々の処理からトークン取得、検証の処理を外し、引数に Claims を受け取ればトークン検証が行われます。
pub async fn add_book(
_claims: Claims,
// req: HttpRequest,
pool: web::Data<PgPool>,
form: web::Json<FormData>,
) -> HttpResponse {
// トークン検証OKの時の処理の書く
}
コード例を真似して書いていたとき、Actix のバージョン違いなのもあって割と詰まりました。