Slide 1

Slide 1 text

© teamLab Inc. Auth.js から Better Auth への 移行に見る「型とランタイム」の 設計思想の変化 宇根 昇汰 チームラボ パッケージチーム 所属

Slide 2

Slide 2 text

© teamLab Inc. 所属| 2 自己紹介 2 パッケージチーム フロントエンド班 宇根 昇汰 Une Shota 2024年 新卒入社 3年目 家でできることが好き!

Slide 3

Slide 3 text

© teamLab Inc. Auth.js と Better Auth 3

Slide 4

Slide 4 text

© teamLab Inc. Auth.js と Better Auth 4 Webアプリの認証で広く使われてきた ライブラリ Auth.js では前身となる NextAuth から フレームワーク依存を排除した設計に 広げられた 特定のフレームワークに依存せず、 アプリ固有の認証モデルを拡張する プラグインが豊富 NextAuth の影響を受けて作られた 後発のライブラリ

Slide 5

Slide 5 text

© teamLab Inc. 2025年 9月 Auth.js の開発が Better Auth チームに移行されるとの発表 5 メンテナンス/開発を Better Auth チームが行う旨を発表(#13252) Auth.js 公式は Better Auth への移行を推奨するように この2つのライブラリで型はどう変わっているかを見る

Slide 6

Slide 6 text

© teamLab Inc. 型はどう「Better」になったのか 6

Slide 7

Slide 7 text

© teamLab Inc. App Server 認証成功後の情報の流れとライブラリの役割 7 DB Auth Runtime App Client - DB と接続し、セッションとして必要な情報を取得する - 取得した情報からアプリケーションに渡す情報を組み立てる - サーバー/クライアントサイドでセッションを取得するための関数の 提供

Slide 8

Slide 8 text

© teamLab Inc. App Server 認証成功後の情報の流れとライブラリの役割 8 DB Auth Runtime App Client user.role セッションにアプリケーション固有のプロパティを追加 アプリケーションサーバー/クライアントに型安全に渡したい 実際の値と型情報をどのように渡すのか比較

Slide 9

Slide 9 text

© teamLab Inc. Auth.js: 拡張した型を宣言し、ランタイムのコードを型に合わせる 9 App Server DB Auth Runtime App Client declare module "next-auth" { interface Session { user: { role: "user" | "admin"; } & DefaultSession["user"]; } interface User { role: "user" | "admin"; } }

Slide 10

Slide 10 text

© teamLab Inc. Auth.js: 拡張した型を宣言し、ランタイムのコードを型に合わせる 10 App Server DB Auth Runtime App Client declare module "next-auth" { interface Session { user: { role: "user" | "admin"; } & DefaultSession["user"]; } interface User { role: "user" | "admin"; } } callbacks: { session({ session, user }) { session.user.role = user.role; return session; }, },

Slide 11

Slide 11 text

© teamLab Inc. Auth.js: 拡張した型を宣言し、ランタイムのコードを型に合わせる 11 App Server DB Auth Runtime App Client 取得した session には session.user.role が含まれると推論される session.user.role: "user" | "admin" // Server Side const session = await auth(); // Client Side const { data: session } = useSession();

Slide 12

Slide 12 text

© teamLab Inc. session.user.role: "user" | "admin" Auth.js: 型とランタイムの値のズレが生じうる 12 App Server DB Auth Runtime App Client session は拡張済みの Session 型 → session に role を実際に含めずとも型エラーにならない // Server Side const session = await auth(); // Client Side const { data: session } = useSession(); callbacks: { session({ session, user }) { // roleを詰め忘れる // session.user.role = user.role; return session; }, },

Slide 13

Slide 13 text

© teamLab Inc. Auth.js: 型とランタイムの値のズレが生じうる 13 App Server DB Auth Runtime App Client callbacks: { session({ session, user }) { // roleを詰め忘れる // session.user.role = user.role; return session; }, }, declare module "next-auth" { interface Session { user: { role: "user" | "admin"; } & DefaultSession["user"]; } } ランタイムの値を扱う箇所と型定義を行う箇所が独立しているため 両方を同期・更新する必要がある

Slide 14

Slide 14 text

© teamLab Inc. Better Auth: config から型を推論 14 App Server DB Auth Runtime App Client export const auth = betterAuth({ user: { additionalFields: { role: { type: ["user", "admin"], input: false, }, }, }, })

Slide 15

Slide 15 text

© teamLab Inc. Better Auth: config から型を推論 15 App Server DB Auth Runtime App Client export const auth = betterAuth({ user: { additionalFields: { role: { type: ["user", "admin"], input: false, }, }, }, }) const session = await auth.api.getSession({ headers: await headers(), }); session.user.role: "user" | "admin"

Slide 16

Slide 16 text

© teamLab Inc. Better Auth: config から型を推論 16 App Server DB Auth Runtime App Client export const auth = betterAuth({ user: { additionalFields: { role: { type: ["user", "admin"], input: false, }, }, }, }) const session = await auth.api.getSession({ headers: await headers(), }); session.user.role: "user" | "admin" type Session = typeof auth.$Infer.Session;

Slide 17

Slide 17 text

© teamLab Inc. Better Auth: config から型を推論 17 App Server DB Auth Runtime App Client auth インスタンスがランタイムと型推論の交点になる export const auth = betterAuth({ user: { // roleを詰め忘れる }, }) type Session = typeof auth.$Infer.Session;

Slide 18

Slide 18 text

© teamLab Inc. 設計思想の変化 18

Slide 19

Slide 19 text

© teamLab Inc. 19 Auth.js 型を宣言する ↓ 型に合わせたレスポンスを作る Better Auth ランタイムを決める config を書く ↓ config を元に型を推論する 型とランタイムの SSOT(Single Source of Truth)を決める設計 設計思想の変化

Slide 20

Slide 20 text

© teamLab Inc. 近年の TypeScript エコシステムにおける、フロントエンドバックエ ンド一体型のアプリケーション構成の台頭が理由にありそう 👀 設計思想の変化 20 ただ、データが自分のアプリケーションとより密接に統合されていればいいのにと思 うこともありました。おそらく、それがあなたが目指しているところなのでしょう。 @bekacru はい、まさにそれが私たちの目指すところです。認証をアプリに密接に連携させるべ き理由はたくさんあると思います。 (省略) フロントエンドとバックエンドが一体となって強力な型システムを共有するフルス タックTypeScriptアプリの台頭に伴い、すべてのコンテキストを1か所にまとめてお くことがますます理にかなっていると言えるでしょう。 - Hacker News

Slide 21

Slide 21 text

© teamLab Inc. Better Auth は複雑なアプリ固有の認証モデルを自分たちのアプリ ケーションに統合して扱うことが開発の目的の一つ ランタイムでの認証モデルを config に定義し、型まで推論する設計 はフルスタック TypeScript アプリと認証ランタイムを統合する上で 相性がいい → サーバー/クライアントに認証モデルの変更が型レベルで伝播するため 設計思想の変化 21 (特に) フルスタック TypeScript アプリケーションに 認証ランタイムを統合する設計思想に変化した(...ように見える)

Slide 22

Slide 22 text

© teamLab Inc. - Auth.js は追加フィールドの型を宣言し、型に合わせてランタイム の処理を追加する - Better Auth ではランタイムの処理を定義する config を書き、そこ から型を推論する - 近年台頭するフルスタック TypeScript アプリに強く統合できるよ うな設計に変化した(...のだと思う) まとめ 22

Slide 23

Slide 23 text

© teamlab Inc. 宇根 昇汰 チームラボ パッケージチーム 所属 Auth.js から Better Auth への 移行に見る「型とランタイム」の 設計思想の変化 ありがとうございました

Slide 24

Slide 24 text

© teamLab Inc We’re Hiring! サマーインターンシップ応募受付中!