Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Firebase Authenticationのセッション管理術
Search
SatohJohn
July 07, 2023
Programming
2.7k
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Firebase Authenticationのセッション管理術
7/7 PORT Firebase meetup
https://connpass.com/event/285741/
発表させていただいた資料です
SatohJohn
July 07, 2023
More Decks by SatohJohn
See All by SatohJohn
A2UI という光を覗いてみる
satohjohn
1
140
_Architecture_Modernization_から学ぶ現状理解から設計への道のり.pdf
satohjohn
2
1.2k
アーキテクチャモダナイゼーションを実現する組織
satohjohn
1
1.5k
Vertex_AI_Searchを使いこなす実践テクニック
satohjohn
1
200
アーキテクチャモダナイゼーションの書籍紹介
satohjohn
0
55
NVIDIA NeMo Agent Tooklit を使ってみた
satohjohn
0
110
Gemini Enterprise を恐れない - Securityと監査-
satohjohn
0
200
進化の早すぎる生成 AI と向き合う
satohjohn
0
790
お前も Gemini CLI extensions を作らないか?
satohjohn
0
190
Other Decks in Programming
See All in Programming
C# and C++ Interoperability - cho-dotnetnew
harukasao
0
260
ふつうのFeature Flag実践入門
irof
8
4k
Strategic Design in the Frontend: Moduliths & Micro Frontends @DDDEurope
manfredsteyer
PRO
0
110
Java × distroless で 軽量なコンテナイメージを / Java on Distroless
contour_gara
0
550
AIだと陥りがちなJakarta EE最新技術への移行時の落とし穴と解決策
tnagao7
0
110
LLMによるContent Moderationの本番運用の裏側と品質担保への挑戦
suikabar
3
710
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
260
TAKTでAI駆動開発の品質を設計する
j5ik2o
7
1.4k
フロントエンドとバックエンドで「1文字」を揃えよう
youkidearitai
PRO
0
710
CSC307 Lecture 17
javiergs
PRO
0
320
The NotImplementedError Problem in Ruby
koic
1
840
Oxcを導入して開発体験が向上した話
yug1224
4
320
Featured
See All Featured
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
2
1.1k
Stewardship and Sustainability of Urban and Community Forests
pwiseman
0
230
The Spectacular Lies of Maps
axbom
PRO
1
820
The Mindset for Success: Future Career Progression
greggifford
PRO
0
360
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
It's Worth the Effort
3n
188
29k
Balancing Empowerment & Direction
lara
6
1.2k
The Limits of Empathy - UXLibs8
cassininazir
1
360
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
600
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
3
160
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
240
Transcript
Firebase Authenticationの セッション管理術 2023/07/07 PORT Firebase meetup 株式会社スリーシェイク 佐藤慧太
自己紹介 • 株式会社スリーシェイク Sreake事業部 • 佐藤慧太@SatohJohn • SREとしてお客様の労苦<Toil>を減らす仕事 • Google
Cloudの製品が好き • FirebaseでのWebアプリケーション経験4年ぐらい
株式会社スリーシェイクについて Copyright © 3-shake, Inc. All Rights Reserved. xOps
Plattform DesignOps IaaS DevOps / SRE RevOps (Revenue Ops) HR(Engineer Hiring) HROps Data Engineering DataOps Security DevSecOps SecOps 事業者が抱える セキュリティリスクを無くす 本格的な脆弱性診断を 無料で手軽に セキュリファイ Security 良いエンジニアに良い案件を フリーランスエンジニアに 「今よりいい条件」を リランス HR(Engineer Hiring) あらゆるサービスを連携する ハブになる クラウド型ETL/データパイプ ラインサービスの決定版 レコナー Data Engineering 日本のSREをリード SRE総合支援からセキュリティ 対策を全方位支援 スリーク SRE スリーシェイク = xOps領域のプラットフォーマーへ
Session管理って めんどくさいですよね?
本日伝えたいこと • Webアプリケーションでの Firebase Authenticationのセッション管理方法について ◦ Cookie ◦ idTokenとService Worker
本日伝えたいこと • Webアプリケーションでの Firebase Authenticationのセッション管理方法について ◦ Cookie ◦ idTokenとService Worker
実装できる(気がする)まで持っていく
話さないこと • Service workerの詳しい説明 ◦ fetchしかでてきません • Firebase Authenticationの詳しい説明 ◦
Login後のどう管理するかだけです
超概略的まとめ • idTokenはaccess tokenのようなもので、有効期限がとても短い • idTokenをcookieに変換して利用する ◦ サーバ側で検証、処理をするイメージ • service
workerを使って、idTokenのまま利用する ◦ フロント側で検証、処理をするイメージ
Cookieの利用
Cookieでの管理のメリット • ブラウザが保存、取り出しをしてくれるため フロントで処理を記載する必要がない • 初回アクセス時にサーバ側で処理できる • 古いブラウザでも対応している
Cookieでの管理のデメリット • Cookieの作成の口を用意しないといけない • 色々なアプリで展開している場合、Cookie用の検証用の口を サーバサイドで用意しないと行けない • 他Firebase製品をフロントで使う場合や、Password変更など 組み合わせる際に、セッション管理が別になり、複雑になる
Cookieシーケンス Login部分 • Firebaseでのログインをした後 idTokenを取得する • idTokenをもとにCookieを サーバで作成してもらう • ログイン状態の2重管理になるた
め以下を注意する ◦ Firebase Authentication の情報保存をoffにするこ と ◦ Cookieの保存後に Firebase Authentication でログアウトすること
Cookieシーケンス Login部分 • Firebaseでのログインをした後 idTokenを取得する • idTokenをもとにCookieを サーバで作成してもらう • ログイン状態の2重管理になるた
め以下を注意する ◦ Firebase Authentication の情報保存をoffにするこ と ◦ Cookieの保存後に Firebase Authentication でログアウトすること
実装(フロント) • ログイン方法は別になんでもよく idTokenが取得できれば良い • Cookie取得のリクエストが成功す ればCookieが自動的に ブラウザに保存される import {
signInWithEmailAndPassword, } from "firebase/auth"; export const loginWithEmailAndPassword = (auth, email, password) => { return signInWithEmailAndPassword(auth, email, password) .then(async (result) => { const idToken = await result.user.getIdToken(); return login(idToken); }) }; export const login = (idToken) => { return fetch("/auth/authenticate", { method: "POST", data: { id_token: idToken, }, }).then((data) => data.json()) };
実装(サーバ) • フロントから送られてきたidToken を使ってCookieを 組み立てる • CookieにHTTP OnlyとSecure属 性をつけることを忘れない def
authenticate(request): id_token = request.POST.get("id_token") if id_token is None or len(id_token) == 0: return HttpResponse("id token not found", status=400) session_cookie = create_session_cookie(id_token) if session_cookie is None: return HttpResponse("Failed to create a session cookie", status=401) response = HttpResponse() response.set_cookie( session_cookie["name"], value=session_cookie["value"], expires=session_cookie["expires"], secure=True, httponly=True, samesite="Lax", ) return response
実装(サーバ) • idTokenをデコードすると認証した 時間が取れる • 認証した時間から公式では 5分以内であることで不正ではな いことを担保している • Cookieで使える値は最大14日間
の有効期限なのでCookieもその 時点で消えるようにしておく from firebase_admin import auth def create_session_cookie(id_token): decoded_claims = auth.verify_id_token(id_token) if time.time() - decoded_claims["auth_time"] < 5 * 60: expires_in = datetime.timedelta(days=14) expires = datetime.datetime.now() + expires_in session_cookie = auth.create_session_cookie(id_token, expires_in=expires_in) return build_session_cookie(expires, session_cookie) return None def build_session_cookie(expires, session_cookie): return {"expires": expires, "value": session_cookie, "name": LOGIN_COOKIE_NAME}
cookieシーケンス リクエスト部分 • ブラウザがリクエスト時にCookie をつける • 検証結果に応じて処理を 実施する
実装(サーバ) • Cookieからclaim取得して それからUserを取得する • userが取得できない場合は 403エラーとしても良いし 未ログインでも使えるように してもよい •
Pythonの例ではあるが FrameworkのMiddleware上で 実装するのが良い class RequiredAuthorizedMiddleware: get_response = None REQUIRED = "required" KEY = "auth" REQUIRED_DICT = {KEY: REQUIRED} def __init__(self, get_response): self.get_response = get_response def process_view(self, request, view_func, view_args, view_kwargs): claim = auth.verify_session_cookie(request.COOKIES.get(LOGIN_COOKIE_NAME)) if self.authorized(claim, request): request.login_user = convert_login_user(claime) return None return HttpResponse("fobidden", status=403) def authorized(self, claim, request): if claim is None: return False return True
idTokenとservice worker
idTokenとservice workerのメリット • 作成の変換コストがcookieより少ない • client、Firebase間でidTokenの更新をするため、サーバ側での期限切れがパ ターンが少ない • idToken作成に使うrefresh tokenがcookieよりも生存する期間が長いため
再度Loginページ表示が少なくて済む • iOS、Androidと同じような処理で、サーバ側で認証ができる
idTokenとservice workerのデメリット • idTokenの期限が1時間なので、作り直しはcookieより多い • 古いブラウザなどでService Workerに対応していない可能性がある • 初回アクセス時にサーバ側で処理しないため、要件とは合わない可能性がある
service worker シーケンス • Cookieのときと、ログインまでは 変わらない • HTMLを受け取りservice worker を、インストールする部分が入って
いる
実装(フロント) • Firebase Authenticationで ログインをするだけ import { signInWithEmailAndPassword } from
"firebase/auth"; export const loginWithEmailAndPassword = (auth, email, password, afterLogin) => { return signInWithEmailAndPassword(auth, email, password) .then(async (result) => { return afterLogin(); }) };
service worker シーケンス • idTokenが取得できない場合は Loginページに遷移させる ◦ サーバで処理する場合も 検証は必要なので 2箇所見る必要がある
実装(フロント) • getIdTokenの際にtokenが取得で きるかをみて リダイレクトさせる • httpRequestの際にリクエストをプ ロキシすることができるので同じ ホストの場合 Headerに追加する
• 公式で公開されているコードがあ るのでそちらを見ながら 触ってみると良いかと self.addEventListener("fetch", (event: Event) => { const fetchEvent = event as FetchEvent; const requestProcessor = (idToken: string | null) => { let req = fetchEvent.request; let processRequestPromise = Promise.resolve(); if (self.location.origin == getOriginFromUrl(fetchEvent.request.url)) { const headers = new Headers(); for (let entry of req.headers.entries()) { headers.append(entry[0], entry[1]); } headers.append("Authorization", "Bearer " + idToken); processRequestPromise = getBodyContent(req).then((body) => { req = new Request(req.url, { method: req.method, headers: headers, mode: "same-origin", credentials: req.credentials, cache: req.cache, redirect: req.redirect, referrer: req.referrer, body: body as string, }); }); } return processRequestPromise.then(() => fetch(req)); }; fetchEvent.respondWith(getIdToken().then(requestProcessor)); });
実装(サーバ) • headerからidTokenを取得し フロントで行えないユーザ別の処 理を行う事ができる • この形はアプリでも同じ形に なるはず function getIdToken(req)
{ const authorizationHeader = req.headers.authorization || ''; const components = authorizationHeader.split(' '); return components.length > 1 ? components[1] : ''; } app.get("/something", (req, res) => { const idToken = getIdToken(req); admin .auth() .verifyIdToken(idToken) .then((decodedClaims) => { return res.json(something(decodedClaims)) }) .catch((error) => { console.log(error); res.status(403); res.json({ error: "You must be logged in to continue!" }); }); });
実装(フロント) • サーバとの通信とは関係なく ログイン中のユーザの情報を 取得できる • ログイン中のユーザ情報はauthイ ンスタンスから取得する ◦ idTokenから取得できるレ
ベルではある export const getLoginUser = (auth): Promise<User | null> => { return new Promise((resolve, reject) => { const subscribe = auth.onAuthStateChanged((user) => { subscribe(); resolve(user); }); }); };
まとめ • どっちが良いということはなく、サービスの特性に合わせて利用する ◦ idTokenをcookieに変換して利用する ▪ 処理をサーバで実施するようなパターン ▪ MPAのアプリケーションと相性が良い ◦
service workerを使って、idTokenのまま利用する ▪ 処理をフロントで実施するようなパターン ▪ SPAのアプリケーションと相性が良い
一緒にSREとして働きませんか? • 社会から労苦<Toil>をなくしましょう ◦ 気になりましたら会社ホームページやどんなメンバがいるのか を見ていただき 申し込みしていただければ! ◦ または、私の方にtwitterとかでメンションなどでご相談していただければ!
おわり