Kakao Social Login 기능 추가
GitHub 연동까지 해 보세요.
Storage ⇒ HTML5에 추가된 기능
https://developer.mozilla.org/ko/docs/Web/API/Web_Storage_API/Using_the_Web_Storage_API
브라우저에서 쿠키를 사용하는 것보다 훨씬 직관적으로 key/value 데이터를 안전하게 저장할 수 있는 메커니즘을 제공합니다.
- sessionStorage는 페이지의 세션이 유지되는 동안 사용할 수 있는 각 origin별로 별도의 스토리지를 관리합니다. (페이지 리로딩 및 복원을 포함한, 브라우저가 열려있는 한 최대한 긴 시간 동안)
- localStorage도 같은 일을 하지만, 브라우저가 닫히거나 다시 열리더라도 유지합니다.
JWS(JSON Web Token) 토큰 검증 후 사용자 프로필 조회(P126)
#1 auth0.com에 등록된 사용자 프로필 정보를 반환하는 람다 함수를 생성
#1-1 api-gateway-lambd-exec-role 역할을 생성
#1-2 람다 함수 생성
JSON Web Token 검증
auth0 엔드포인트를 호출해서 사용자 정보를 조회
웹 사이트에 응답을 전송
#1-3 작업 디렉터리 생성 및 필요 모듈 추가
PS C:\Users\i> cd C:\serverless\
PS C:\serverless> mkdir user-profile
PS C:\serverless> cd user-profile
PS C:\serverless\user-profile> npm init -y
PS C:\serverless\user-profile> npm install jsonwebtoken
PS C:\serverless\user-profile> npm install request
#1-4 package.json 파일에 deploy, predeploy 스크립트를 추가
c:\serverless\user-profile\package.json
{ "name": "user-profile", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "predeploy": "del Lambda-Deployment.zip & zip -r Lambda-Deployment.zip * -x *.zip *.log node_modules/aws-sdk/*", "deploy": "aws lambda update-function-code --function-name user-profile-람다함수의ARN --zip-file fileb://Lambda-Deployment.zip" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "jsonwebtoken": "^8.5.1", "request": "^2.88.2" } } |
#1-5 index.js 파일 생성 및 소스 코드 추가
참고 : auth0.com에서 사용자 정보를 받아와서 반환
https://auth0.com/docs/api/authentication#user-profile
c:\serverless\user-profile\index.js
// P130
// AWS 람다 서비스를 통해서 실행되는 코드
'use strict';
// https://www.npmjs.com/package/jsonwebtoken
// https://www.npmjs.com/package/request
var jwt = require('jsonwebtoken');
var request = require('request');
/*
event = {
"accessToken": "w7agqIkUzCofd9nUelOxgd1zogqPpw9V",
"authToken": "Bearer eyJhbGciOiJ~~~cCI6IkpXVCJ9.eyJnaXZlbl~~~pKNDQuZSJ9.mioxKcb1~~~W1LTk5_anGo"
};
*/
exports.handler = function (event, context, callback) {
console.log(JSON.stringify(event));
// event 객체에 authToken, accessToken 존재 여부를 확인
// 만약, 존재하지 않으면 리턴
if (!event.authToken) {
callback('Could not find authToken');
return;
}
if (!event.accessToken) {
callback('Could not find access_token');
return;
}
// authToken의 값을 공백문자를 기준으로 분리한 후 두번째 값을 id_token 변수에 할당
// authToken은 Bearer 값.값.값 형식을 가짐
var id_token = event.authToken.split(' ')[1];
var access_token = event.accessToken;
var body = {
'id_token': id_token,
'access_token': access_token
};
// 환경변수 DOMAIN의 값은 auth0.com에 설정되어 있는 어플리케이션의 도메인
// auth0.com에 사용자 프로필 정보 조회에 필요한 값을 설정
// ==> 로그인 시 전달받은 access token을 요청 파라미터로 전달
// https://auth0.com/docs/api/authentication#user-profile
var options = {
url: 'https://' + process.env.DOMAIN + '/userinfo',
method: 'GET',
json: true,
body: body
};
// auth0.com으로 사용자 프로필 정보를 조회
request(options, function (error, response, body) {
if (!error && response.statusCode === 200) {
// 정상적으로 조회한 경우 호출한 곳으로 프로필 정보를 반환
callback(null, body);
} else {
callback(error);
}
});
};
#1-6 람다 함수를 배포
PS C:\serverless\user-profile> npm run deploy
#1-7 환경변수 등록
#1-8 람다 함수 테스트
소셜 로그인 후 브라우저 개발자 도구를 이용해서 localStorage에 저장된 accessToken과 idToken 값을 추출
user-profile 람다 함수의 event로 전달 (authToken의 값은 localStorage에 있는 idToken값을 할당)
{
"accessToken": "ifzF3bnON0RH9E2VBHem15ygluDSuftA",
"authToken": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Im9VWmF6dzRINnNkWFhqYzRtem13ciJ9.eyJuaWNrbmFtZSI6Im15YW5qaW5pIiwibmFtZSI6Il4uLl52IiwicGljdHVyZSI6Imh0dHA6Ly9rLmtha2FvY2RuLm5ldC9kbi9QMWpYYi9idHFHa1djN2JuRC9ZVGd4VWtNMEM0dm5sRXNjZDNxd2gwL2ltZ18xMTB4MTEwLmpwZyIsInVwZGF0ZWRfYXQiOiIyMDIxLTAzLTE2VDA0OjUwOjEwLjAyOVoiLCJlbWFpbCI6Im15YW5qaW5pQGtha2FvLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJpc3MiOiJodHRwczovL25hYW5qaW5pLnVzLmF1dGgwLmNvbS8iLCJzdWIiOiJvYXV0aDJ8a2FrYW98MTUwOTIzOTQ4OCIsImF1ZCI6IkJBZEVlWlRrRmxBZHlDMWE3bldFTkIzc1J5ZHBCeVhSIiwiaWF0IjoxNjE1ODcwNDQxLCJleHAiOjE2MTU5MDY0NDEsImF0X2hhc2giOiJjZEsxenJnZU9sMlFaLVY2YnNsb2JBIiwibm9uY2UiOiJ5Y25rcFFsUC1iT3ZWeExhQl9FZGN4aTdzemM2eWJqfiJ9.sTTMxgRgivW1AaylaJ0PomkBAa0AXuTYaR5_cce1WQgY3pQNxhdJgDcYPNButLTo0pc0v1FKOb4ypmcqW79Ypq3vDkQyYQNDgooio67q_XGSgbVaKAk3MYlpHOrIwPgZpks7SlzQ6ALILm2fFbtPM3bvQvwcZcp4rcvCx1bm5TV69HsPfWfzBH7H-_XFZA61TlANmWuNOoZRqopCVQpSA8KHTo0Qxyn5gvBthRu0sQNoh67L-oLfxZHZfCN3GFeIREPFlBs9WJgn6LZ4nSUQL5XVW-4IBLN_lO3HgTlKsexcKSgeJMI8R_h0mqBkl4cgsVRyCV4xfWIAM_CdFRT8bg"
}
참고: 테스트 케이스를 만드는 스크립트
var accessToken = localStorage.getItem('accessToken');
var idToken = localStorage.getItem('idToken');
var event = { accessToken: accessToken, authToken: idToken };
console.log(event);
테스트 수행 시 응답(response)에 사용자 프로필이 출력되는 것을 확인
#2 API Gateway 설정
웹 사이트의 요청을 받아서 user-profile 람다 함수를 호출
#2-1 REST API 구축
#2-2 리소스 생성
#2-3 메서드 생성
#2-4 CORS 활성화
#2-5 매핑 생성
웹 사이트의 Authorization 헤더를 통해 전달된 JWT 토큰을 람다 함수에서 사용할 수 있도록 event 객체의 값으로 설정
웹 브라우저 -----------------------> API Gateway ----------------------> user-profile 람다 함수
(웹 사이트)
Authorization: JWT_TOKEN event = { authToken: JWT_TOKEN, … }
~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
+---------------- 맵핑(mapping) --------------+
{
"authToken": "$input.params('Authorization')",
"accessToken": "$input.params('AccessToken')"
}
참고 : API Gateway 매핑 템플릿과 액세스 로깅 변수 참조
#2-6 API 배포
API Gateway 및 리소스 URL로 접근하면 오류를 반환 ⇒ 내부 수행에 필요한 값이 전달되지 않기 때문
#3 API Gateway를 통해서 람다 함수를 호출(실행)하도록 웹 페이스를 수정 (P138)
프로필 보기 기능을 추가 구현
#3-1 config.js 파일에 API Gateway 호출 URL을 등록
c:\serverless\24-hour-video\js\config.js
var configConstants = {
auth0: {
domain: 'naanjini.us.auth0.com',
clientId: 'BAdEeZTkFlAdyC1a7nWENB3sRydpByXR'
},
// API Gateway 호출 URL
apiBaseUrl: 'https://vfhp67njsk.execute-api.us-east-1.amazonaws.com/dev'
};
#3-2 user-controller.js 파일에 Show Profile 버튼을 클릭했을 때 동작을 추가 (이벤트 핸들러를 추가)
c:\serverless\24-hour-video\js\user-controller.js
// 특정 이벤트에 반응하는 함수를 정의
wireEvents: function() {
var that = this;
// P138
// Show profile 버튼 클릭 이벤트 핸들러
this.uiElements.profileButton.click(function(e) {
// user-profile 리소스 호출 URL
var url = that.data.config.apiBaseUrl + '/user-profile';
// 지정된 URL 주소로 GET 방식의 요청을 전달
$.get(url, function(data, status) {
console.log('data', data);
console.log('status', status);
}).fail(function(e) {
console.log(e);
});
});
// :
// (이하 생략)
// :
#4 사용자 프로필 조회 테스트
#4-1 콘솔창에 오류 메시지를 확인
본 요청(main request)에 AccessToken이라는 요청 헤더가 포함되어 있음 ⇒ 기원이 다른 서버(API 게이트웨이)에서 해당 헤더를 처리할 수 있는지 여부를 먼저 확인(preflighted request)
프리플라이트 요청 결과에 access-control-allow-headers에 AccessToken이 포함되어 있지 않음 ⇒ Request header field accesstoken is not allowed by Access-Control-Allow-Headers in preflight response. 오류가 발생
해결방안 ⇒ 기원이 다른 서버(API Gateway)에서 Access-Control-Allow-Headers 응답 헤더에 AccessToken을 추가
#4-2 Access-Control-Allow-Headers 응답 헤더에 AccessToken을 추가
#4-3 프로필 요청 테스트
프리플라이트 요청의 응답에는 access-control-allow-headers 헤더를 확인
본 요청의 응답에는 access-control-allow-origin 헤더를 확인
CORS(교차 기원 자원 공유)
https://developer.mozilla.org/ko/docs/Web/HTTP/CORS
웹(WEB) = 공유 ⇒ 교차 기원 요청이 가능
~~~~
origin = 기원 = 출처
HTTP 요청은 기본적으로 교차 기원 요청(Cross Origin Request)이 가능
기원이 다른 JS 파일, 이미지 파일을 가져와서 서비스(보여지고 실행) 가능
http://127.0.0.1:8000/cors_test.html
<html>
<head></head>
<body>
<img src="http://127.0.0.1:8000/tile.png" height="200">
<img src="https://s.pstatic.net/static/www/img/uit/2020/sp_shop_bffdc9.png" height="200">
<script src="http://127.0.0.1:8000/js/main.js"></script>
<script src="https://nid.naver.com/login/js/bvsd.1.3.4.min.js"></script>
</body>
</html>
http://127.0.0.1:8000/cors_test.html
http://127.0.0.1:8000/tile.png
http://127.0.0.1:8000/js/main.js
https://s.pstatic.net/static/www/img/uit/2020/sp_shop_bffdc9.png ⇐ 기원이 다름에도 불구하고 가져와서 사용이 가능
https://nid.naver.com/login/js/bvsd.1.3.4.min.js
스크립트 내에서 교차 기원 요청은 보안 상의 이유로 제한 ⇒ SOP(Same Origin Policy: 동일 기원/출처 정책)
~~~~~~~~~~~~ ⇒ ajax 통신 = XMLHttpRequest 객체를 이용한 비동기 통신
SOP ⇒ XMLHttpRequest 객체를 사용해서 가져오는 리소스는 해당 웹 애플리케이션과 동일한 기원으로 제한
<html>
<head></head>
<body>
<img src="tile.png" height="200">
<img src="https://s.pstatic.net/static/www/img/uit/2020/sp_shop_bffdc9.png" height="200">
<script src="js/main.js"></script>
<script src="https://nid.naver.com/login/js/bvsd.1.3.4.min.js"></script>
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(xhr.responseText);
}
};
// XMLHttpRequest를 이용해서 동일 기원의 자원을 요청
xhr.open("GET", "http://127.0.0.1:8000/js/main.js", true);
xhr.send();
</script>
</body>
</html>
<html>
<head></head>
<body>
<img src="tile.png" height="200">
<img src="https://s.pstatic.net/static/www/img/uit/2020/sp_shop_bffdc9.png" height="200">
<script src="js/main.js"></script>
<script src="https://nid.naver.com/login/js/bvsd.1.3.4.min.js"></script>
<script>
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log(xhr.responseText);
}
};
// XMLHttpRequest를 이용해서 교차 기원의 자원을 요청
xhr.open("GET", "https://nid.naver.com/login/js/bvsd.1.3.4.min.js", true);
xhr.send();
</script>
</body>
</html>
CORS(Cross Origin Resource Sharing: 교차 기원 자원 공유)
⇒ SOP를 완화하는 정책
⇒ Access-Control-Allow-Origin 응답 헤더를 이용해서 자원 사용 여부를 허가
단순 요청(simple request)
프리플라이트 요청(preflighted request)
'CLOUD > AWS' 카테고리의 다른 글
3/19 - AWS 15차시 (0) | 2021.03.19 |
---|---|
3/17 - AWS 14차시 (1) | 2021.03.17 |
3/15 - AWS 12차시 (0) | 2021.03.15 |
3/12 - AWS 11차시 (0) | 2021.03.12 |
3/11 - AWS 10차시 (0) | 2021.03.11 |