「NestJS meetup Tokyo #1 @ Eureka ( https://nest-jp.connpass.com/event/152260/ )」で話した、「NestJS + Passport と Firebase Auth で宣言的な JWT 認証を実装する」のスライドです。
NestJS + Passport ͱ Firebase Auth Ͱએݴతͳ JWT ೝূΛ࣮͢Δ2019.11.29 #nestjs_meetup ͠ʔͳΔ͢(@ci7lus)
View Slide
͠ʔͳΔ͢(@ci7lus)• Organizations• An Engineer at ElevenBack LLC.• I like• TypeScript, Python, Front/Back-End, Server-less, Database• Works• Candy by ElevenBack LLC. - ϑϦʔϥϯεɾύϥϨϧϫʔΧʔͷͨΊͷؾʹ͠ͳ͍ձܭཧΫϥυ(https://candy.ac) ։ൃ
Topics1. NestJS ʹ͓͚ΔೝূɾೝՄʹ͍ͭͯ2. Firebase Auth ΛΈ͜ΜͰΈΔ3. خ͍͠4. ·ͱΊ
ΞϓϦέʔγϣϯʹ͓͚Δೝূ• ϢʔβʔใΛ࣋ͭ Web ΞϓϦέʔγϣϯʹ͓͍ͯɺೝূใͷཧආ͚ΒΕͳ͍• ʮೝূใ͕͖͍ͬͯͯΔ͔ʯʮਖ਼͍͠Ϣʔβʔใ͔ʯҰՕॴʹूதͯ͠ཧ͍ͨ͠
NestJS ʹ͓͚ΔೝূɾೝՄ• Guards ͱ͍͏ػೳΛ࣮ͬͯ͢Δ• https://docs.nestjs.com/guards• σίϨʔλΛ༻͍ͯએݴతʹΤϯυϙΠϯτΛम০• 2 छྨͷ Guards ͕ଘࡏ͢Δ• ಛఆͷΤϯυϙΠϯτʹରͯ͠ෳͷ Guards ΛࢦఆͰ͖Δ• ༏ઌॱҐΛઃఆͰ͖Δ
2छྨͷ Guards: CanActivate• https://docs.nestjs.com/guards Ͱղઆ͞Ε͍ͯΔ Guards• ࣮ߦ࣌ͷঢ়ଶ͔ΒೝՄΛஅ• ೝՄΛ Boolean Ͱฦ٫• ΤϯυϙΠϯτʹର͠ೝՄʹඞཁͳϝλσʔλΛՃ͠ɺͦΕΒΛಡΈऔΔ͜ͱͰ͖Δ• ೝՄରͷϩʔϧͳͲ
2छྨͷ Guards: AuthGuard• https://docs.nestjs.com/techniques/authenticationͰղઆ͞Ε͍ͯΔ Guards• Passport ͷ Strategy Λ༻͍ͯೝՄΛஅ• ೝՄΛ Boolean / Object Ͱฦ٫• طଘͷ Strategy Λར༻͢Δ͜ͱ͕Ͱ͖Δ• ར༻͍ͯ͠Δೝূػߏ͚ʹ Strategy ͕ఏڙ͞Ε͍ͯΔ߹ɺಋೖ͕༰қ
2छྨͷ Guards ͷൺֱ• CanActivate• ݁Ռͷฦ٫ Boolean• ใͷ࠶ར༻Ͱ͖ͳ͍• AuthGuard• ݁Ռͷฦ٫ Object / Boolean• ϦΫΤετͰฦ٫ͨ͠ΦϒδΣΫτͷ࠶ར༻͕Մೳ• Ϣʔβʔೝূ͜ΕΛ༻͍Δͷ͕ϕλʔ
Firebase Auth Λ༻͍ͨೝূ• NestJS Passport ͷ Strategy Λ༻͍ͯೝূ͕Ͱ͖Δʢ@nestjs/passportʣ• Firebase Auth ͷ Passport Strategy Λ༻͍Δ͜ͱ͕Ͱ͖Δʁ• ͋Γͦ͏ɺ୳ͯ͠ΈΑ͏• https://www.npmjs.com/package/passport-firebase-auth• “0.0.0-beta.1 / Published 3 years ago”• ͩΊͦ͏…
Firebase Auth Λ༻͍ͨೝূ• https://docs.nestjs.com/techniques/authentication ʹ͋ΔΑ͏ʹɺpassport-local Λ༻͍ͯ validate ͰೝূΛ࣮ߦͰ͖ͳ͍͔ʁ• Ҿͱͯ͠ड͚औΕΔใ͕ඇৗʹ੍ݶ͞Ε͍ͯΔ• Username / Password ͷΈ• ೝূʹඞཁͳใΛऔಘͰ͖ͳ͍
Firebase Auth Λ༻͍ͨೝূ• ͡Ό͋Ͳ͏͢Δ͔• passport-custom ͱ͍͏ϑϧΧελϜՄೳͳ Strategy ͕ଘࡏ͢Δ• Ҿͱͯ͠ϦΫΤετͷใΛͯ͢ड͚औΔ͜ͱ͕Ͱ͖Δ• Authorization ϔομʔͷ JWT ݟΒΕΔʂ• ͜ΕΛ֦ுͯ͠ @nestjs/passport ͱ࿈ܞͤ͞Δ
࣮ͷצॴ• https://docs.nestjs.com/techniques/authentication ͷ“auth/local.strategy.ts“ Λࢀߟʹ Strategy Λ֦ு͍ͯ͘͠• constructor ͷ super() ʹ͕ͨ͠ຊདྷͷ Strategy ͷୈ1Ҿʹ͞ΕΔ• Strategy ʹରͯ͠ΦϓγϣϯΛ͢߹͜͜Ͱͤྑ͍• ͜ΕΛলུ͢Δͱୈ1Ҿ͔Βͦͷ·· validate ʹΔ• Strategy ͷίʔϧόοΫ֦ுଆͰΑ͠ͳʹͯ͘͠ΕΔ
࣮ͷצॴ• ·ͨɺStrategy ໊ࣗ༝ʹมߋ͕Մೳ• σϑΥϧτͩͱ “custom”• PassportStrategy ͷୈ2Ҿʹ string ΛͤΔ• Θ͔Γ͘͢ “firebase” ͳͲʹ͓ͯ͘͠ͱྑ͍• PassportStrategy(Strategy, ‘firebase’)• ݺͼग़͢ͱ͖ @UseGuards(AuthGuard(‘strategyName’))
࣮@Injectable()export class FirebaseStrategy extends PassportStrategy(Strategy, 'firebase') {constructor() {super();}async validate(req: Request) {const token = req.headers.authorization;if (!token) return false;const parsedToken = bearerRegex.exec(token);if (!parsedToken) return false;try {return await auth.verifyIdToken(parsedToken[1]);} catch (error) {return false;}}}
خ͍͠• ೝূɾೝՄΛൺֱత؆୯ʹಋೖ͢Δ͜ͱ͕Ͱ͖Δ• AuthGuard ͕ Passport ͷ֦ுϞδϡʔϧͱͯ͠༻ҙ͞Ε͍ͯΔ• ϝϯςφϯε͞Ε͍ͯΔ Strategy ͕طଘ͍ͯ͠ΕΑΓ؆୯• ϩʔϧͳͲඞཁʹͳΓ͍͢ػೳ৫ΓࠐΈࡁΈ• σίϨʔλϕʔεͰอޢ͕࣮ࢪͰ͖ΔͷͰָ
خ͍͠• ΤϯυϙΠϯτͰೝূใΛ༻͍Δ͜ͱ͕Ͱ͖Δ• req.user ʹ UserRecord ͕ೖ͞ΕΔ• UserRecord ܕΛ͚ͯݺͼग़͢ͱศར• Strategy ͰखʹೖͬͨϢʔβใΛ༻͍ͯԿΛฦ͍͍ͯ͠• Firestore ͷϢʔβʔใͱ݁߹ͯ͠ฦ͢ͱͦͷ··͑Δ• ϢʔβʔใΛѻ͏ Util Λผ్ఆٛͯ͠ฦͯ͠Α͍
·ͱΊ• Express ͳͲͰϛυϧΣΞͱͯ͠Ұॹͨ͘ʹѻΘΕΔอޢػߏΛଞϛυϧΣΞͱ໌ࣔతʹผͷͷͱͯ͠ѻ͏͜ͱͰɺ࣮ͷ༰қ͞Λ࣮ݱ͍ͯ͠Δ• ಋೖʹ͓͍ͯ2ͭͷ Guards ֓೦ͷཧղʹ࣌ؒΛཁͨͨ͠Ίɺͦͷʹؔͯ͠೦ೖΓʹղઆͨ͠ʢͭΓʣ• ৭ʑศརʂ
͓ΘΓ