Upgrade to Pro — share decks privately, control downloads, hide ads and more …

estie、Rustで新プロダクト作るってよ - 2022/2/16

estie、Rustで新プロダクト作るってよ - 2022/2/16

2022/2/16に開催されたイベントで使用した資料です。
https://seriesa.estie.jp/event_rust

# イベント概要
estie(エスティ)は2022年1月12日、約10億円のシリーズA資金調達を発表いたしました。調達した資金を活用して、商業用不動産業界のデジタルトランスフォーメーションをより加速すべく、主力サービスである「estie pro」を拡張するマルチプロダクト戦略のもと、新しいプロダクトをどんどん開発しています。
そのうちの1つは、最近注目があつまるRustをバックエンドに採用したWebアプリです。どのようにRustを活用してWebアプリを開発するのか、新しい言語としてRustを採用したestieでの例をご紹介します!

# 企業ページ
https://www.estie.jp/corp/

# エンジニア採用情報
https://aspiring-step-dba.notion.site/estie-ba5b74c80e39475ebc8c3f15f879e776

estie | エスティ

February 16, 2022
Tweet

More Decks by estie | エスティ

Other Decks in Programming

Transcript


  1. ొஃऀ঺հ
    ࢘ձ

    View full-size slide


  2. ຊ೔ͷྲྀΕ
    Φʔϓχϯά r FTUJFͱ͸ʁ <NJO>
    LFOLPPPP ʰ3VTUͰ΢ΣϒΞϓϦʱ<NJONJO2">
    ฏా౦ເʰ3VTUº/FYUKTº"VUIͰϢʔβʔೝূʱ<NJONJO2">
    શମΛ௨ͯ͠ͷ࣭ٙɾΫϩʔδϯά <NJO>
    )BTIUBHFTUJF@ST
    2"ػೳΛͥͻ͝׆༻͍ͩ͘͞ʂ

    View full-size slide

  3. ৽ίʔϙϨʔταΠτ ʢ IUUQTXXXFTUJFKQDPSQ ʣ ͷ͝঺հ

    View full-size slide

  4. ͜Ε·ͰͷϓϩμΫτ܈

    View full-size slide

  5. ͦΕΛࢧ͑ΔਐԽ͠ଓ͚ΔσʔλύΠϓϥΠϯ

    View full-size slide

  6. 20/4
    20/5
    20/6
    20/7
    20/8
    20/9
    20/10
    20/11
    20/12
    21/1
    21/2
    21/3
    21/4
    21/5
    21/6
    21/7
    21/8
    21/9
    21/10
    21/11

    ϏδωεΠϯύΫτ
    ओͳಋೖاۀ
    ԻॱɺҰ෦ൈਮ

    BOENPSF
    .33
    Y
    ௚ۙϲ݄

    View full-size slide


  7. 1IBTF
    ۭࣨɾاۀ৘ใͷ
    σʔλج൫ߏங
    1IBTF
    1IBTF
    τϥϯβΫγϣϯ
    ϓϥοτϑΥʔϜ

    View full-size slide


  8. ෳ਺ϓϩμΫτͷ૬৐ޮՌͰاۀՁ஋ΛരൃతʹߴΊΔ
    1SPEVDU
    ۭࣨɾاۀ৘ใͷ
    σʔλج൫ߏங
    1SPEVDU
    1SPEVDU
    τϥϯβΫγϣϯ
    ϓϥοτϑΥʔϜ
    "1*ߏ૝

    ଟ༷ͳݴޠ
    ৽͍͠ϓϩμΫτͷ͕ͭόοΫΤϯυΛ3VTUͰॻ͍ͯ·͢ʂ
    ʢࢣఋ੍౓Λ੔͑࢝Ί͍ͯ·͢ʣ

    View full-size slide


  9. ͬͦ͘͞-5͍͖·͠ΐ͏ʂ

    View full-size slide

  10. 3VTUͰ΢ΣϒΞϓϦ
    ΞϧόΠτʢਂ໷ɾٳ೔γϑτʣ
    LFOLPPPP

    View full-size slide


  11. 3VTUͷಛ௕
    $ฒʹ଎͍
    ΋ͱ΋ͱ'JSFGPY༻ݴޠ
    -JOVYΧʔωϧʹ࢖ΘΕͦ͏
    "84Ͱ͸7.΍04ͳͲʹ࢖ΘΕ͍ͯΔ
    ίϯύΠϥ͕Ϧιʔε؅ཧʹ͍ͭͯνΣοΫ͠·͘Δ

    View full-size slide


  12. Ͱ΋ɺ೉͍͠ΜͰ͠ΐ͏ʜʁ

    View full-size slide


  13. 3VTU͕೉͍͠ͱ͍͏ӟ
    ίϯύΠϥͷνΣοΫ͕ݫ͗ͯ͢͠Ұੜ௨Βͳ͍ʜ
    ॴ༗ݖʁ஌Βͳ͍֓೦͕ͨ͘͞Μग़ͯ͘Δʜ

    View full-size slide


  14. ͦ΋ͦ΋ϓϩάϥϛϯά͕೉͍͠ʂʂʂ

    View full-size slide


  15. void listOperation(List list) {
    list.add("Awesome!");
    }
    Javaではよく⾒る関数だが…

    View full-size slide


  16. void listOperation(List list) {
    list.add("Awesome!");
    }
    Javaではよく⾒る関数だが…
    • 実は List がミュータブルであることを強制
    • ImmutableList を渡すと Runtime Exception
    • 呼び出し元でも予期しないかも?

    View full-size slide


  17. void listOperation(List list) {
    list.add("Awesome!");
    }
    Javaではよく⾒る関数だが…
    • 実は List がミュータブルであることを強制
    • ImmutableList を渡すと Runtime Exception
    • 呼び出し元でも予期しないかも?
    →コメントをつける・関数名を⼯夫するしかない

    View full-size slide


  18. fn list_operation(list: Vec) {…}
    • 呼び出し元では list は使われない
    • move しているので
    fn list_operation(list: &Vec) {…}
    • イミュータブルな参照
    fn list_operation(list: &mut Vec) {…}
    • ミュータブルな参照

    View full-size slide


  19. ϓϩάϥϛϯά͸೉͍͠ʜ
    ͦ΋ͦ΋ϓϩάϥϛϯά͸೉͍͠ʜ
    ϓϩάϥϛϯάͷ೉͍͠ϙΠϯτΛਓྗͰνΣοΫ͢Δͷ͸େมʜ
    ฒྻϓϩάϥϛϯά͸ਓྨʹ͸ෆՄೳ
    3VTUʹ೉͍͠ϙΠϯτΛԡ͚ͭ͠Α͏ʂ

    View full-size slide


  20. -JOVY࡞Βͳ͍ͷͰʜ

    View full-size slide


  21. 3VTU͸௿ϨΠϠʔઐ༻ݴޠͰ͸ͳ͍ʂ
    ศརͳݴޠػೳ͕͋Δ
    ݸਓతͳΠνΦγ͸ USBJU
    $BSHPͱ͍͏ඪ४ͷύοέʔδ؅ཧπʔϧ͕͋Δ
    ීஈͷϓϩάϥϛϯάݴޠͱͯ͠Φεεϝ

    View full-size slide

  22. 3VTUͰ΢Σϒ։ൃ

    View full-size slide


  23. 3VTUΛऔΓר͘΢Σϒ։ൃࣄ৘
    ೥൒લʹඇಉظߏจ BTZODBXBJU͕҆ఆԽ
    ͦΕҎલ΋ඇಉظॲཧ͸ग़དྷ͕ͨɺ؆୯ʹॻ͚ΔΑ͏ʹͳͬͨ
    ೥͔͚ͯϥΠϒϥϦɾϑϨʔϜϫʔΫ͕ॆ࣮
    ݹࢀϥΠϒϥϦ͕ BTZODBXBJUରԠ
    ॻ͖΍͔͢͞Β৽͍͠ϥΠϒϥϦ΋ଓʑ

    View full-size slide


  24. 3VTUͰ΢ΣϒΞϓϦ
    • ΢ΣϒϑϨʔϜϫʔΫBDUJYXFC
    • +BWBͷ 4QSJOHɺ1ZUIPOͷ 'MBTL
    • 42-υϥΠόʔTRMY
    • .Z42-Ͱ࢖༻
    • 03.Ͱ͸ͳ͘ɺΫΤϦ௚ॻ͖
    • 4ΫϥΠΞϯτSVTPUP
    • ݹࢀ "84ϥΠϒϥϦ

    View full-size slide


  25. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }

    View full-size slide


  26. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }
    JSONとして受け取るように指定
    FWでパース等やってもらう

    View full-size slide


  27. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }
    FWからSQLコネクションプールを
    もらってくる

    View full-size slide


  28. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }
    ヘッダからトークンを取り出して
    IDを取得しておいてもらう

    View full-size slide


  29. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }
    返り値は Result

    View full-size slide


  30. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }

    View full-size slide


  31. let id = Building::insert(&pool, &building).await?;

    View full-size slide


  32. let id = Building::insert(&pool, &building).await?;

    View full-size slide


  33. 3VTUʹ͓͚Δ
    3FTVMUܕͷ FBSMZSFUVSO
    value, err := f()
    if err != nil {
    return nil, err
    }
    let value = f()?;

    View full-size slide


  34. #[post("/api/buildings")]
    pub async fn create_building(
    request: Json,
    pool: Data,
    auth0_id: ReqData,
    ) -> Result {
    let building = Building::from(request.building);
    verify_user(&pool, &auth0_id.0).await?;
    let id = Building::insert(&pool, &building).await?;
    Ok(Json(CreateBuildingResponse { building_id: id }))
    }
    マクロで⾊々⽣成してもらう

    View full-size slide


  35. BDUJYXFC·ͱΊ
    • ΄͔ͷݴޠͱಉ͘͡Βָ͍ʹ "1*͕ॻ͚Δ
    • ඇಉظॲཧ͸ BTZODBXBJU
    • ΤϥʔϋϯυϦϯά͸ 3FTVMUͱ

    View full-size slide

  36. TRMYͱ 3VTUϚΫϩ

    View full-size slide


  37. 3VTUͷ42-؀ڥ
    • %JFTFMͱ͍͏ݹࢀ03.
    • ඇಉظɺඇରԠʜ
    • ඇಉظରԠͷ 42-ϥΠϒϥϦ TRMY
    • ΫΤϦ௚ॻ͖
    • ڧྗͳϚΫϩ

    View full-size slide



  38. async fn insert(pool: &MySqlPool, unit: &Unit) -> Result {
    let unit_id = sqlx::query(r#"
    INSERT INTO units (
    area,
    floor,
    building_id
    ) VALUES (?,?,?)"#,
    )
    .bind(unit.area)
    .bind(unit.floor)
    .bind(unit.building_id)
    .execute(pool)
    .await?
    .last_insert_id();
    Ok(unit_id)
    }

    View full-size slide



  39. async fn find_by_bid(pool: &MySqlPool, bid: i64) -> Result> {
    let units = sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    .fetch_all(pool)
    .await?;
    Ok(units)
    }

    View full-size slide



  40. async fn find_by_bid(pool: &MySqlPool, bid: i64) -> Result> {
    let units = sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    .fetch_all(pool)
    .await?;
    Ok(units)
    }

    View full-size slide



  41. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )

    View full-size slide



  42. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )

    View full-size slide



  43. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    struct RawUnit {
    unit_id: i64,
    area: Decimal,
    floor: u8,
    building_id: i64,
    }

    View full-size slide



  44. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    struct RawUnit {
    unit_id: i64,
    area: Decimal,
    floor: u8,
    building_id: i64,
    }
    もし NULL が含まれていたら……?

    View full-size slide



  45. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    struct RawUnit {
    unit_id: i64,
    area: Decimal,
    floor: u8,
    building_id: i64,
    }

    このクエリの返り値の型が RawUnit と同じか、
    コンパイル時にチェックする。

    View full-size slide


  46. 42-ΫΤϦͷฦΓ஋ͷܕΛ
    ίϯύΠϧ࣌ʹνΣοΫ͢Δʙʂʁʂʁ

    View full-size slide


  47. 3VTUϚΫϩͷେ·͔ͳಈ͖
    τʔΫϯྻΛड͚औΔ
    ͍Ζ͍Ζॲཧ͢Δ
    ͜ͷ෦෼΋3VTUͰॻ͚Δ
    ίʔυΛੜ੒͢Δ

    View full-size slide



  48. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )

    View full-size slide



  49. sqlx::query_as!(
    RawUnit,
    "SELECT * FROM units WHERE building_id=?",
    bid
    )
    トークン列

    View full-size slide


  50. 3VTUϚΫϩͷେ·͔ͳಈ͖
    τʔΫϯྻΛड͚औΔ
    ͍Ζ͍Ζॲཧ͢Δ
    ͜ͷ෦෼΋3VTUͰॻ͚Δ
    ίʔυΛੜ੒͢Δ

    View full-size slide


  51. 3VTUϚΫϩͷେ·͔ͳಈ͖
    τʔΫϯྻΛड͚औΔ
    ͍Ζ͍Ζॲཧ͢Δ
    ͜ͷ෦෼΋3VTUͰॻ͚Δˠ42-ʹ઀ଓͯ͠ɺΫΤϦͷܕΛ֬ೝ
    ίʔυΛੜ੒͢Δ

    View full-size slide

  52. ⼈類 コンパイラ MySQL
    コード
    クエリ

    型チェッ

    View full-size slide


  53. ·ͱΊɿίϯύΠϧ࣌42-ܕνΣοΫ

    View full-size slide


  54. શମͷ·ͱΊ
    3VTU͸೉͍͠ʁ ˠ ϓϩάϥϛϯά͕೉͍͠ʂ
    3VTU͸௿ϨΠϠʔ޲͚ʁ ˠ ΢Σϒ։ൃ͠Α͏ʂ
    ϚΫϩ͍͢͝ʁ ˠ ͍͢͝Αʜ

    View full-size slide

  55. 3VTUº/FYUKTº"VUIͰϢʔβʔೝূ

    48&
    5PNV )JSBUB !UPNV@EFTVZP

    View full-size slide


  56. എܠ
    "VUIº/FYUKT
    "VUIº"DUJY@XFC
    "HFOEB

    View full-size slide

  57. • estieはマルチプロダクト化を進めている
    • それぞれのサービスは将来統合していく想定がある
    • その際ユーザー情報も統⼀的に管理しきたい
    എܠ

    View full-size slide

  58. 個々のサービスそれぞれで認証機能を作りたくはない
    全社的な認証基盤を作り始める前にIDaaSの技術検証を進めたい
    新規プロダクトでAuth0認証を使うことに
    എܠ

    View full-size slide

  59. ϓϩμΫτͷߏ੒
    Backend
    ID Provider
    BFF
    Frontend

    View full-size slide

  60. ೝূϑϩʔ
    Backend
    ID Provider
    BFF
    Frontend
    ID Token
    ID & Password
    セッションCookie
    JWK
    ID Token
    トークンを検証
    レスポンス

    View full-size slide


  61. എܠ
    "VUIº/FYUKT
    "VUIº"DUJY@XFC
    "HFOEB

    View full-size slide

  62. • クライアント側で認証情報を保持したい
    • トークンの偽装を難しくしたい
    ୡ੒͍ͨ͜͠ͱ

    View full-size slide

  63. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    ID Token
    ID & Password

    View full-size slide

  64. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    ID Token
    ID & Password
    セッションCookie

    View full-size slide

  65. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    ID Token
    ID & Password
    セッションCookie
    トークンはサーバーサイド
    で暗号化・復号化

    View full-size slide


  66. *%τʔΫϯΠϯ
    ペイロード部分の例
    +855PLFOͷߏ଄
    • ϔομʔ෦
    • ॺ໊ݕূ৘ใΛؚΉ
    • ϖΠϩʔυ෦
    • ΞΧ΢ϯτͷଐੑ৘ใͳͲΛؚΉ
    • ॺ໊෦
    • ೝূࡁΈͰ͋Δ͜ͱΛݕূ͢Δॺ໊ΛؚΉ

    View full-size slide


  67. /FYUKTº"VUIΠϯ
    "VUI3FBDUKT 4%, IUUQTHJUIVCDPNBVUIBVUISFBDU

    • /FYUKTͰ4UBUJD)5.-&YQPSUΛ࢖༻͍ͯ͠Δ৔߹
    • αʔόʔαΠυϨϯμϦϯά࣌ʹϢʔβʔσʔλʹΞΫηε͢Δඞཁ͕ͳ͍৔߹
    • /FYUKTͷ"1*3PVUFTΛϓϩΩγͱͯ͠࢖༻ͯ͠֎෦"1*Λݺͼग़͢ͷͰ͸ͳ͘ɺΞΫηε
    τʔΫϯΛऔಘͯ͠ϑϩϯτΤϯυϨΠϠʔ͔Β௚઀֎෦"1*Λݺͼग़͍ͨ͠৔߹
    "VUI/FYUKT 4%, IUUQTHJUIVCDPNBVUIOFYUKTBVUI

    • ͦΕҎ֎ͷ৔߹
    • $PPLJFͰͷηογϣϯͷ؅ཧ͕Մೳ

    View full-size slide


  68. എܠ
    "VUIº/FYUKT
    "VUIº"DUJY@XFC
    "HFOEB

    View full-size slide

  69. • リクエストしてきているユーザーの認証を⾏いたい
    • リクエストに付属したトークンの検証を⾏いたい
    ୡ੒͍ͨ͜͠ͱ

    View full-size slide

  70. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    ID Token

    View full-size slide

  71. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    JWKを要求
    ID Token

    View full-size slide

  72. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    JWK
    JWKを要求
    ID Token

    View full-size slide

  73. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    JWK
    JWTの検証
    JWKを要求
    ID Token

    View full-size slide

  74. ϑϩϯτΤϯυ͔ΒͷϩάΠϯ
    Backend
    ID Provider
    BFF
    Frontend
    JWK
    ID Token
    JWTの検証
    レスポンス
    JWKを要求

    View full-size slide


  75. όοΫΤϯυͰͷϢʔβʔͷೝূΠϯ
    "DUJY@XFCͷϛυϧ΢ΣΞͰ*%5PLFOΛݕূ͢Δ
    • "VUIPSJ[BUJPOϔομʔ͔Β+85τʔΫϯΛ֫ಘ
    • "VUIʹݕূ༻ͷ+8,ΛϦΫΤετ
    • τʔΫϯΛݕূ

    View full-size slide

  76. όοΫΤϯυͰͷϢʔβʔͷೝূ
    pub struct Auth0Middleware {
    service: Rc,
    client: Rc,
    }
    impl Auth0Middleware {
    fn call(&self, req: ServiceRequest) -> Self::Future {
    let token = parse_bearer_token(&req)?;
    let auth0_id = match client.get_auth0_id_from_bearer(token);
    req.extensions_mut().insert(auth0_id);
    service.call(req).await.map(|res| res.map_into_left_body())
    }
    }

    View full-size slide

  77. όοΫΤϯυͰͷϢʔβʔͷೝূ
    pub struct Auth0Middleware {
    service: Rc,
    client: Rc,
    }
    impl Auth0Middleware {
    fn call(&self, req: ServiceRequest) -> Self::Future {
    let token = parse_bearer_token(&req)?;
    let auth0_id = match client.get_auth0_id_from_bearer(token);
    req.extensions_mut().insert(auth0_id);
    service.call(req).await.map(|res| res.map_into_left_body())
    }
    }
    HTTPの
    Authorization
    HeaderからJWT
    トークンを抽出

    View full-size slide

  78. όοΫΤϯυͰͷϢʔβʔͷೝূ
    pub struct Auth0Middleware {
    service: Rc,
    client: Rc,
    }
    impl Auth0Middleware {
    fn call(&self, req: ServiceRequest) -> Self::Future {
    let token = parse_bearer_token(&req)?;
    let auth0_id = match client.get_auth0_id_from_bearer(token);
    req.extensions_mut().insert(auth0_id);
    service.call(req).await.map(|res| res.map_into_left_body())
    }
    }
    JWTトークンを検証
    し、auth0のIDを抽
    出する

    View full-size slide

  79. όοΫΤϯυͰͷϢʔβʔͷೝূ
    fn parse_bearer_token(request: &ServiceRequest) -> Result<&str> {
    let header = request.headers()
    .get(&actix_web::http::header::AUTHORIZATION)?;
    let mut parts = header.to_str()?.splitn(2, ' ');
    let token = parts[1].ok_or_else(|| anyhow::anyhow!("no token"))?;
    Ok(token)
    }
    • BDUJYXFCͷBQJΛ༻͍ͯ"VUIPSJ[BUJPOϔομʔΛநग़
    • ϔομʔͷܗࣜ͸ #FBSFSYYYYYYYYYYYYY

    View full-size slide

  80. όοΫΤϯυͰͷϢʔβʔͷೝূ
    +85τʔΫϯΛݕূ͢ΔΫϥΠΞϯτ
    pub struct Auth0Client {
    client: reqwest::Client,
    base_url: String,
    }
    impl Auth0Client {
    pub(super) async fn get_auth0_id_from_bearer(&self, bearer_token: &str) ->
    Result
    {
    self.validate_jwt_token(bearer_token).await?.sub //JWT payloadのsub
    }
    /////ここに⾊々なメソッド
    }

    View full-size slide

  81. όοΫΤϯυͰͷϢʔβʔͷೝূ
    +85τʔΫϯΛݕূ͢ΔҰ࿈ͷॲཧ
    async fn validate_jwt_token(&self, token: &str) -> Result {
    let jwks = self.fetch_jwks().await?;
    let kid = match decode_header(token)?.kid.unwrap();
    let jwk = match jwks.find(&kid) {
    Some(res) => res,
    None => return Err(anyhow::anyhow!("Specified key not found in set")),
    };
    Auth0Client::dec_jwt(jwk, token)
    }

    View full-size slide

  82. όοΫΤϯυͰͷϢʔβʔͷೝূ
    +85τʔΫϯΛݕূ͢ΔҰ࿈ͷॲཧ
    async fn validate_jwt_token(&self, token: &str) -> Result {
    let jwks = self.fetch_jwks().await?;
    let kid = match decode_header(token)?.kid.unwrap();
    let jwk = match jwks.find(&kid) {
    Some(res) => res,
    None => return Err(anyhow::anyhow!("Specified key not found in set")),
    };
    Auth0Client::dec_jwt(jwk, token)
    }
    Auth0に検証⽤の鍵
    をリクエスト

    View full-size slide

  83. +85τʔΫϯΛݕূ͢ΔҰ࿈ͷॲཧ
    async fn validate_jwt_token(&self, token: &str) -> Result {
    let jwks = self.fetch_jwks().await?;
    let kid = match decode_header(token)?.kid.unwrap();
    let jwk = match jwks.find(&kid) {
    Some(res) => res,
    None => return Err(anyhow::anyhow!("Specified key not found in set")),
    };
    Auth0Client::dec_jwt(jwk, token)
    }
    署名部を⽤いて
    トークンを検証
    όοΫΤϯυͰͷϢʔβʔͷೝূ

    View full-size slide

  84. +8,ΛϦΫΤετ͢Δ
    async fn fetch_jwks(&self) -> Result {
    let uri = format!("{}/.well-known/jwks.json", self.base_url);
    let res = self.client.get(uri).send().await?;
    let val = res.json::().await?;
    Ok(val)
    }
    όοΫΤϯυͰͷϢʔβʔͷೝূ

    View full-size slide

  85. όοΫΤϯυͰͷϢʔβʔͷೝূ
    +85τʔΫϯΛݕূ͢Δ
    fn dec_jwt(jwk: &Jwk, jwt: &str) -> Result {
    match decode::(
    jwt,
    &DecodingKey::from_rsa_components(&jwk.n, &jwk.e),
    &Validation::new(Algorithm::RS256),
    ) {
    Ok(c) => Ok(c.claims),
    e => Err(anyhow::anyhow!("failed to decode jwt: {:?}", e)),
    }
    }

    View full-size slide

  86. όοΫΤϯυͰͷϢʔβʔͷೝূ
    BQJଆͰBVUI@JEΛ࢖༻͢Δ
    #[post("/api/buildings")]
    pub(crate) async fn create_building(
    request: web::Json,
    pool: web::Data,
    auth0_id: web::ReqData,
    ) -> ApiResult {
    let request = request.into_inner();
    let building_info = BuildingInfo::try_from(request.building_info).invalid_request()?;
    verify_user(pool.as_ref(), &auth0_id.0).await?;
    let building_id = Building::insert(pool.as_ref(), &building_info)
    .await
    .log_db_err()?;
    Ok(web::Json(CreateBuildingResponse { building_id }))
    }

    View full-size slide

  87. • Auth0×Actix_web×Next.jsでユーザー認証を⾏った
    • クライアントがAuth0サーバーから獲得したJWTトーク
    ンをRustバックエンドで検証してレスポンスを返す
    ·ͱΊ

    View full-size slide


  88. ͝੩ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠

    View full-size slide


  89. ຊ೔͸͝ࢀՃ͍͖ͨͩ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ
    ڵຯΛ࣋ͨΕͨํ͸ͥͻ IUUQTKPCTFTUJFKQPS!JX@UBUTV ·Ͱʂ

    View full-size slide