2024/08/30
目次
はじめに
RustやJWTを勉強するために、簡単なJWTライブラリを実装してみました。
リポジトリはこちら→https://github.com/ysuzuki19/rust-chibi-jwt/tree48cb525fa71ccae45244d8b57a687ddf30984fab
JWTについて
JWT(JSON Web Token)は、JSON形式で情報を安全にやり取りするための軽量な仕様で、認証や情報の交換に利用されるものとされている。
トークンの構成は、ヘッダ、ペイロード、署名の3つの部分からなり、それぞれBase64エンコードされている。
<header>.<payload>.<signature>
紹介するコードの順番
基本的に、実装が崩れない順番を意識してコードを紹介していく。
一部、mod <hoge>
や、testでの利用などが、紹介する順番では動かなくなるので、そこは無視して進めてもらうか、コメントアウトして貰えればと思う。
作るライブラリの使用例
まずは、どんな使い方をするものを作ったのかを紹介しておく。
以下のように、
- TokenをDecodeしてPayloadを取り出す
- PayloadをTokenにEncodeする(署名を含む)
といった使い方を想定して作ってみた。
一連の記事を進めていくと、以下のコードを実行できるようになる。
src/main.rs
use serde::{Deserialize, Serialize};
use rust_chibi_jwt::{Alg, Jwt};
// Define payload for sample
// `PartialEq` is unnecessary, but it's using for assert_eq! in main
#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct Payload {
sub: String,
name: String,
iat: u64,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// dummy token with secret "my-secret"
let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.EpM5XBzTJZ4J8AfoJEcJrjth8pfH28LWdjLo90sYb9g";
let secret: &[u8; 9] = b"my-secret";
let payload = Payload {
sub: "1234567890".to_string(),
name: "John Doe".to_string(),
iat: 1516239022,
};
{
// Decode and Encode
let jwt = Jwt::<Payload>::decode(token)?;
assert!(jwt.verify(secret)?);
assert_eq!(jwt.payload(), &payload);
let reencoded: String = jwt.encode()?;
assert_eq!(token, reencoded);
println!("Jwt Decode/Encode is OK");
}
{
// Sign and Encode
let mut jwt = Jwt::new(Alg::HS256, payload);
jwt.sign(secret)?;
let encoded: String = jwt.encode()?;
assert_eq!(token, encoded);
println!("Jwt Sign/Encode is OK");
}
Ok(())
}
実行すると、標準出力は以下のようになる。
rust-chibi-jwt $ cargo run
Jwt Decode/Encode is OK
Jwt Sign/Encode is OK
今回は、勉強目的での実装なので、多くの署名アルゴリズムをサポートせず、(サポート対象を追加できる構成で)HS256のみをサポートしている。
おわりに
参考
実装全体を通して参考にさせていただいたサイトやドキュメントなどです。
- https:/jwt.io
- https:/www.base64decode.org
- https://datatracker.ietf.org/doc/htmlrfc7519
- https://doc.rust-lang.org/cargo/commandscargo-test.html
- https://docs.rs/base64/latest/base64
- https://github.com/serde-rsjson
- https://docs.rs/hmac/latest/hmac
- https://stackoverflow.com/questions/4080988why-does-base64-encoding-require-padding-if-the-input-length-is-not-divisible-by