Skip to content
nuguya edited this page Nov 30, 2019 · 1 revision

Google Oauth for JWT

using Oauth with passport

ꡬ글 ν΄λΌμš°λ“œ ν”Œλž«νΌμ— λ“€μ–΄κ°€μ„œ ν”„λ‘œμ νŠΈ 등둝 ν›„ OAuth key와 idλ₯Ό λ°œκΈ‰λ°›λŠ”λ‹€. - 이 λ•Œ 개발용으둜 μ‚¬μš©ν–ˆκΈ°μ— urlκ³Ό λ¦¬λ‹€μ΄λ ‰μ…˜ url은 둜컬호슀트λ₯Ό μ μš©ν•¨.

등둝받은 key와 idλ₯Ό ν™˜κ²½λ³€μˆ˜λ‚˜ λ³„λ„μ˜ 파일둜 λΆ„λ¦¬ν•˜μ—¬ μ €μž₯.

passport-google-oauth20 λͺ¨λ“ˆ μ„€μΉ˜ ν›„ initializeλ₯Ό μœ„ν•œ strategy μ„€μ •.

import passport from "passport";
import { Strategy } from "passport-google-oauth20";
import loadConfig from "../config/configLoader";
import { createHost, findHostById } from "../../DB/queries/host";

const GoogleStrategy = Strategy;

function extractProfile(profile) {
	let imageUrl = "";
	if (profile.photos && profile.photos.length) {
		imageUrl = profile.photos[0].value;
	}
	return {
		id: profile.id,
		displayName: profile.displayName,
		image: imageUrl,
		email: profile.emails[0].value,
	};
}

export default (function() {
	const { oAuthArgs } = loadConfig();
	passport.use(
		new GoogleStrategy(
			{ ...oAuthArgs },
			async (accessToken, refreshToken, profile, cb) => {
				try {
					const { id, displayName, image, email } = extractProfile(
						profile
					);
					let host = await findHostById(id);
					if (!host) host = await createHost(id, displayName, email);
					return cb(null, host);
				} catch (error) {
					console.error(error);
				}
			}
		)
	);
})();
  • λ”°λ‘œ Token을 μƒμ„±ν•˜κΈ° λ•Œλ¬Έμ— accessTokenκ³Ό refreshToken은 μ‚¬μš©ν•˜μ§€ μ•ŠμŒ, profile은 ꡬ글 인증 성곡 ν›„ κ°€μ Έμ˜¬ 수 μžˆλŠ” μ‚¬μš©μž ν”„λ‘œν•„μ— ν•΄λ‹Ή.
  • profileμ—μ„œ λ°˜ν™˜ν•˜λŠ” id κ°’μœΌλ‘œ DB에 이미 λ“±λ‘λ˜μ–΄μžˆλŠ”μ§€ 체크 ν›„ λ“±λ‘λ˜μ–΄μžˆμ§€ μ•ŠμœΌλ©΄, μœ μ € 생성.
  • session을 μ‚¬μš©ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— serialize와 deserializeλŠ” μ„ μ–Έν•˜μ§€ μ•ŠμŒ.

ꡬ글 λ‘œκ·ΈμΈμ— 접속 ν•  수 μžˆλŠ” endpointλ₯Ό μ •μ˜ν•˜κ³  routing

import express from "express";
import passport from "passport";
import { generateAccessToken } from "../authentication/token";

const router = express.Router();

router.get(
	"/login",
	passport.authenticate("google", {
		session: false,
		scope: ["email", "profile"],
		prompt: "select_account",
	})
);

router.get("/logout", function(req, res, next) {
	req.logOut();
	res.redirect("/");
});

router.get(
	"/google/callback",
	passport.authenticate("google", {
		session: false,
	}),
	(req, res) => {
		const accessToken = generateAccessToken(req.user.oauthId);
		res.cookie("vaagle", accessToken);
		res.redirect("http://localhost:3002/");
	}
);
module.exports = router;
  • req.user둜 μ•žμ„œ μ •μ˜ν•œ google strategyμ—μ„œμ˜ cb return 값을 μ½μ–΄μ˜¬ 수 있음.
  • passport.authenticate의 session μ˜΅μ…˜μ„ λͺ…μ‹œμ μœΌλ‘œ false둜 쀌.
  • promt option은 둜그인 λ¦¬λ‹€μ΄λ ‰μ…˜ μ‹œ μžλ™λ‘œκ·ΈμΈμ΄ μ•„λ‹Œ 둜그인 계정을 선택할 수 μžˆλŠ” 창을 λΆˆλŸ¬μ˜€λ„λ‘ μ„€μ •.

ꡬ글 λ‘œκ·ΈμΈμ„ 톡해 μ‚¬μš©μžκ°€ 인증되면 JWTλ₯Ό 생성 ν›„ ν΄λΌμ΄μ–ΈνŠΈ 쿠킀에 λ“±λ‘μ‹œν‚¨λ‹€. 이 ν›„ 토큰을 λ°œκΈ‰λ°›μ€ ν΄λΌμ΄μ–ΈνŠΈλ“€μ„ passport jwt μ „λž΅μ„ 톡해 토큰 값을 λ³΅ν˜Έμ‹œν‚€κ³  μœ μ €κ°€ μžˆλŠ”μ§€ ν™•μΈν•˜λŠ” λ°©μ‹μœΌλ‘œ 인증을 μˆ˜ν–‰ν•œλ‹€.

import passport from "passport";
import passportJwt from "passport-jwt";
import loadConfig from "../config/configLoader";
import { findHostById } from "../../DB/queries/host";

export default (function() {
	const { tokenArgs } = loadConfig();
	const jwtOptions = {
		jwtFromRequest: passportJwt.ExtractJwt.fromAuthHeaderAsBearerToken(),
		secretOrKey: tokenArgs.secret,
		issuer: tokenArgs.issuer,
		audience: tokenArgs.audience,
	};
	passport.use(
		new passportJwt.Strategy(jwtOptions, async (payload, cb) => {
			try {
				const host = findHostById(payload.sub);
				if (host) {
					return cb(null, host, payload);
				}
				return cb();
			} catch (error) {}
		})
	);
})();
  • jwtFromRequest은 ν΄λΌμ΄μ–ΈνŠΈ 헀더에 λ‹΄κΈ΄ jwt 토큰을 의미.
  • payloadλŠ” 토큰을 JWT payload 뢀뢄에 ν•΄λ‹Ή.

RefreshToken ??

λ§Œμ•½ RefreshToken을 μ‚¬μš©ν•œλ‹€λ©΄ accessToken은 ν΄λΌμ΄μ–ΈνŠΈ inMemory에 μ €μž₯ν•˜κ³  refreshToken은 cookie에 μ €μž₯ν•˜μ—¬ μ‚¬μš©ν•˜λ„λ‘ ν•  μ˜ˆμ •.

ν”„λ‘œμ νŠΈ 적용

diagram

Clone this wiki locally