OAuth 2.0 이란?
Open Authorization 의 약자이며, 인증을 위한 개방형 표준 프로토콜이다.
이 프로토콜에서는 Third-Party 프로그램에 리소스 소유자를 대신해서 리소스 서버에서 제공하는 자원에 대한 접근 권한을 위임하는 방식으로 작동된다.
구글, 페이스북 등 외부 소셜 계정을 기반으로 간편하게 인증하는 기능이다.
용어 정리
- Authentication(인증) : 인증, 접근 자격이 있는지 검증하는 단계
- Authorization(인가) : 자원에 접근할 권할을 부여하고 리소스 접근 권한이 있는 Acess Token을 제공
- client 우리가 개발한 서비스(service), 어플리케이션(Application)을 뜻함.
- Resource Owner 리소스 소유자이며, 자격을 승인하는 주체이다. 예시로 구글 로그인을 할 사용자를 뜻함
- Resource Server 구글, 페이스북, 카카오, 네이버 등 회사를 의미함. client와 Reource Owner가 사용하는 자원들을 관리하며 유효성을 체크함.
- Authorization Server 권한을 부여해주는 서버. 사용자 → ID, PW를 넘겨 Authorization Code를 발급 받을 수 있다.
- Client → Authorization Code를 넘겨 Token을 발급 받을 수 있다.
권한 부여 방식
1. Authorization Code Grant 권한 부여 승인 코드
- 권한 부여 승인을 위해 자체 생성한 Authorization Code를 전달하는 방식으로 많이 쓰인다.
2. Implicit Grant 암묵적 승인 방식
- 권한부여승인 코드없이 AccessToken 발급
3. Resource Owner Password Credentials Grant 자원 소유자 자격증명 승인 방식
- 간단하게 username, password로 access token을 받는 방식
4. Client Credentials Grant 클라이언트 자격증명 승인 방식
1. OAuth 2.0 클라이언트 ID 발급
Google OAuth 2.0을 이용하려면 클라이언트 ID를 발급받아야 한다.
발급은 Google APIs Console에서 받을 수 있다.
1. 프로젝트 생성
2. 사용자 인증 정보 만들기
OAuth 2.0 클라이언트 ID항목이 비어있다면 사용자 인증 정보 만들기-OAuth 2.0 클라이언트 ID를선택하여 생성한다.
3. 도메인 입력
별도의 사용 도메인이 있을 경우 생성한 클라이언트 ID 설정화면에서 URI에 도메인을 입력하고, 리디렉션 URI를 세팅한다.
2. Passport.js
serializeUser는 사용자가 인증을 성공할 경우, deserializeUser는 이후 사용자의 요청 시마다 호출된다.
passport는 google 이외에도 facebook, twitter, github, local login 등 다양한 로그인 처리를 손쉽게 이룰 수 있다.
const express = require('express');
const session = require('express-session');
const fileStore = require('session-file-store')(session);
const passport = require('passport')
,GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
const app = express();
//기본 회원정보 (웹 실무시 데이터 베이스로 대체 하면됨)
let db = [{
id : '1',
email : 'goodmemory@tistory.com',
password : 'goodmemory',
name : 'goodmemory',
provider : '',
token : '',
providerId : ''
}];
//구글 api ID, Secret 정보 저장 (구글 개발자 웹 내 앱ID, 시크릿 입력)
const googleCredentials = {
"web": {
"client_id": "200473103414-vgo0ni4nqnha6f9qsblh84eo1cdkbhkh.apps.googleusercontent.com",
"client_secret": "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",
"redirect_uris": [
"http://localhost:3000/auth/google/callback"
]
}
}
//MIDDLEWARE
app.use(express.urlencoded({extended : false}));
app.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: false,
store : new fileStore()
}));
//PASSPORT - 전용 middleware 추가
app.use(passport.initialize());
app.use(passport.session());
//PASSPORT - 직렬화
//serializeUser : 로그인 / 회원가입 후 1회 실행
//deserializeUser : 페이지 전환시 마다 실행
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
//PASSPORT (Google) - 구글 로그인시 정보 GET
passport.use(new GoogleStrategy({
clientID: googleCredentials.web.client_id,
clientSecret: googleCredentials.web.client_secret,
callbackURL: googleCredentials.web.redirect_uris[0]
},
function(accessToken, refreshToken, profile, done) {
console.log(profile);
let user = db.find(userInfo => userInfo.email === profile.emails[0].value);
if(user) {
user.provider = profile.provider;
user.providerId = profile.id;
user.token = accessToken;
user.name = profile.displayName;
}else {
user = {
id : 2, //랜덤값 필요시, npm shortid 설치 후 shortid.generate() 활용
provider : profile.provider,
providerId : profile.id,
token : accessToken,
name : profile.displayName,
email : profile.emails[0].value
}
db.push(user);
}
return done(null, user);
}
));
3. 예시 페이지 get api
//구글 로그인 버튼 클릭시 구글 페이지로 이동하는 역할
app.get('/auth/google',
passport.authenticate('google', { scope: ['email','profile'] }));
//구글 로그인 후 자신의 웹사이트로 돌아오게될 주소 (콜백 url)
app.get('/auth/google/callback',
passport.authenticate('google', { failureRedirect: '/auth/login' }),
function(req, res) {
res.redirect('/');
});
//홈페이지 생성 (req.user는 passport의 serialize를 통해 user 정보 저장되어있음)
app.get('/',(req,res)=>{
const temp = getPage('Welcome', 'Welcome to visit...',getBtn(req.user));
res.send(temp);
});
//로그아웃 페이지 : 로그 아웃 처리 + 세션 삭제 + 쿠키 삭제 후 홈으로 리다이렉션
//passport 패키지로 인해 req.logout()으로 로그아웃 기능 구현 가능
app.get('/auth/logout',(req,res,next)=>{
req.session.destroy((err)=>{
if(err) next(err);
req.logOut();
res.cookie(`connect.sid`,``,{maxAge:0});
res.redirect('/');
});
});
//로그인 or 로그아웃 상태에 따른 버튼 생성
const getBtn = (user) =>{
return user !== undefined ? `${user.name} | <a href="/auth/logout">logout</a>` : `<a href="/auth/google">Google Login</a>`;
}
//페이지 생성 함수
const getPage = (title, description,auth)=>{
return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${title}</title>
</head>
<body>
${auth}
<h1>${title}</h1>
<p>${description}</p>
</body>
</html>
`;
}
//SERVER
app.listen(3000,()=>console.log('http://localhost:3000'));
'Backend > JavaScript,NodeJS, Express' 카테고리의 다른 글
[JavaScript] ES6의 유용한 문법 익히기 (0) | 2022.11.02 |
---|---|
[JavaScript] TestCode 작성하기 (0) | 2022.10.05 |
[NodeJS] Multer 와 S3로 이미지 처리하기 (0) | 2022.04.28 |
[NodeJS] sharp 패키지 써보기. (0) | 2022.04.26 |
[AWS] 이미지 저장을 할 때 필요한 이미지 해상도 개수만큼 저장하는 이유는 무엇일까? ( S3, Lambda) (0) | 2022.03.28 |