Rust jwtの発行と検証

個人開発したアプリの宣伝
目的地が設定できる手帳のような使い心地のTODOアプリを公開しています。
Todo with Location

Todo with Location

  • Yoshiko Ichikawa
  • Productivity
  • Free

スポンサードリンク

actix-webでjwtの発行と検証を行ったので記録しておきます。

Cargo.toml

こちら GitHub - Keats/jsonwebtoken: JWT lib in rust を使用しました。

jsonwebtoken = "7"


jwtの生成

例として8時間まで有効のjwtを生成。尚、expireを指定しないとjwt decode時にtimeout tokenとみなされるので、 GitHub - Keats/jsonwebtoken: JWT lib in rust こちらにある通り、expフィールドは必須になります。

use jsonwebtoken::{encode, Header, Algorithm, EncodingKey};
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
    account_id: String,
    iat: i64,
    exp: i64,
}

pub fn make_jwt( secret: &String, account_id: &String ) -> String {
  let mut header = Header::default();
  header.typ = Some("JWT".to_string());
  header.alg = Algorithm::HS256;
  let now = Utc::now();
  let iat = now.timestamp();
  let exp = (now + Duration::hours(8)).timestamp();
  let my_claims =
      Claims { 
          account_id: account_id.clone(),
          iat: iat,
          exp: exp,
      };

  encode(&header, &my_claims, &EncodingKey::from_secret( secret.as_ref())).unwrap()
}

ここで生成したjwtをクライアントへレスポンスしクライアント側で保存を行います。


jwtの送信例

例としてaxiosでAuthorizationヘッダーにjwtを送信する例を示します。Bearer schema typeで送信するのが一般的のようです。

axios.get("http://localhost:8080/auth", {
  headers: {
    Authorization: `Bearer ${jwt_token}`
  }
});


jwtの検証

サーバ側でのjwtの検証

actix-webでのHTTPリクエストヘッダー参照

HttpRequestでbindすることができます。req.headers()でHTTPリクエストヘッダーが格納されたHashMapを取得できます。

#[get("/auth")]
async fn auth(pool: web::Data<DbPool>, req: HttpRequest) -> Result<HttpResponse> {
    let authorization = req.headers().get("authorization");
   //authorizationヘッダーの解析
   let result = decode_authorization_header(authorization);
   let decode = match result {
       Err(error) => // error時の処理 ,
       // jwtのデコード処理
       Ok(jwt) => decode_jwt( jwt, your_secret ),
   }

  // match decode ...認証成功、失敗の処理
}


authorizationヘッダーの解析

上記で取得したAuthorizationヘッダーがサーバが意図したヘッダーであるか解析を行います。

  • Authorizationが存在しない:NG
  • schema Bearer が指定されていない:NG
  • Bearerに続くjwtが送信されていない:NG

みたいな感じです。

//Authorizationヘッダーのdecode
fn decode_authorization_header(authorization:Option<&HeaderValue>) -> Result<&str, &str> {
    let token = match authorization {
        None => return Err("notfound authorization header"),
        Some(token) => token.to_str().unwrap(),
    };
    let mut split_token = token.split_whitespace();
    match split_token.next() {
        None => return Err("not found schema type"),
        Some(schema_type) => {
            if schema_type != "Bearer" {
                return Err("invalid schema type");
            }
        }
    };
    let jwt = match split_token.next() {
        None => return Err("not found jwt token"),
        Some(jwt) => jwt
    };
    Ok(jwt)
}


jwtのデコード

bindするClaimsstructはjwt生成時にした使用したClaimsと同じstructを指します。secretは勿論、生成時と同じものを指定します。

use jsonwebtoken::{TokenData, DecodingKey, Validation, decode};
fn decode_jwt(jwt: &str, secret: &str) -> Result<TokenData<Claims>, jsonwebtoken::errors::Error> {
    let secret = std::env::var(secret).expect("secret is not set");
    decode::<Claims>(
        jwt, &DecodingKey::from_secret(secret.as_ref()),
        &Validation::default())
}

上記でデコードが成功するとこのようなTokenDataを参照することができます。

Ok(TokenData { header: Header { typ: Some("JWT"), alg: HS256, cty: None, jku: None, kid: None, x5u: None, x5t: None }, claims: Claims { account_id: "****-****-****", iat: 1607908963, exp: 1607937763 } })