이제 스스로에게 물어본다(?)
"로그인 규칙을 어떻게 만들것인가?" 예를 들어, 데이터베이스에
user
가 하나 있는데 이 user
는 이런 email
을 가지고 있다.
{ "_id" : "yuoIkA5t7NB_axs1qVST5xflGbrR45jh", "expires" : ISODate("2022-05-06T07:39:42.013Z"), "session" : "{\"cookie\":{\"originalMaxAge\":null,\"expires\":null,\"httpOnly\":true,\"path\":\"/\"},\"loggedIn\":true,\"user\":{\"_id\":\"625942ace3564e09811a5f21\",\"email\":\"pkpanda@naver.com\",\"username\":\"Cyber Lover\",\"password\":\"$2b$05$WMO/VH/yctvvPJST0SyLq.QRQfSNeLJ5zAJPFfRMwLgg5ZFq1KtBm\",\"name\":\"Mercury\",\"location\":\"NYC\",\"__v\":0}}" }
이미 해당 email
로 계정도 있고 password
도 있다.
즉 이 user
는 username
과 passoword
로 login
을 할수 있다.
그런데 만일 Github
으로 로그인 버튼을 누르게 된다면 토큰 작업등 전부 거친 뒤
Github
으로 로그인 한user
는 데이터베이스상에 똑같은 email
과 password
를 가진 user
를 받는다.
그러니까 웹사이트로 와서 email
과 password
로 계정을 생성하고 한달 후에 돌아와서
Github
으로 로그인 하려한다.
보다시피 Github
은 email
을 주고 있다. 그런데 이 email
이 똑같다.
그러면 어떻게 하는게 좋을까? 두가지 옵션이 있다. 하나는 user
에게 "그건 안된다.
이미 password
가 있으니 그걸로 로그인하라" 라고 말 할수 있고
또는 "똑같은 email
이 있다는걸 증명했으니 Github
로 로그인해도 된다" 라고 할수 있다.
만일 user
가 다른 무언가로 로그인을 했다면 예를 들어 카카오톡으로 로그인 했다하면
카카오톡이 이런식으로 email
을 줄거다. 이렇게 user
를 로그인 시킬수도 있다.
이런 방식을 채택한 여러 사이트들이 있다. 그치만 몇몇 사이트들은 password
로 계정을 만들게 하는 곳도 있다.
"password
로 로그인 하세요"라고 하면서 말이다.
혹은 email
과 password
로만 로그인하려 하는데 사실 password
없이 Github
로그인으로
계정을 만들었다면 "user
는 있지만 password
는 없다. 그러니까 Github
로 로그인해라" 라고 할거다.
보다시피 옵션은 정말 많다. 그래서 Github
로그인 같은 소셜 로그인을 할때
만일 email
에 접근 권한이 있다는게 증명이 된다면 즉, password
가 있거나
Github
의 email
이 verified
된거라면 email
의 주인이라는 뜻이니까
로그인 시켜 줄수 있다. 꼭 이렇게 할 필요는 없다. 더 많은 조건들을 만들어도 된다.
예를 들어 user
가 password
를 갖고 있다는걸 알게 된다면 로그인을 시켜선 안되는 거다.
이 같은 경우엔 여기에 primary
랑 verified
가 있기 때문에 그렇게 하지 않는다.
그래서 primary
이면서 verified
된 email
을 찾는거다. 두개다 중요하기 때문이다.
그래서 user
가 두개의 조건을 만족한다면 "email
이 있으니 로그인 시켜준다"라고 할거다.
이 부분 또한 바뀐다. 예를 들어 Github login
으로 계정을 만든 user
있을때
즉 email
은 있지만 password
가 없는 경우를 말하는 거다.
이럴때는 로그인 화면에서 user
에게 이렇게 말해야 한다.
"email
은 있는데 password
가 없다"이건 그들이 Github
으로 로그인해야 한다는 뜻이다.
이렇게 고려해야 할 사항들이 굉장히 많다.
이제 무엇을 하냐면 만약 primary
인 email
을 받고 데이터베이스에서 같은 email
을 가진 user
를 발견하면
user
들을 로그인 시켜 준다. email
은 사실 string
이 아닌 객체 이다.
그래서 find
가 객체를 주고 있는 거다. 그런데 email
그 자체가 필요하다.
이 부분을 emailObj
라 바꿔 본다.
const emailObj = emailData.find(
(email) => email.primary === true && email.verified === true
);
if (!emailObj) {
return res.redirect("/login");
}
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
}
} else {
return res.redirect("/login");
}
};
그리고 기존 user
를 찾는거다. 그리고 emailObj.email
을 가지고 user
를 찾는다.
만일 해당 email
을 가지는 user
가 이미 있다면 그 유저가 전에 Github
로 로그인했든
password
로 계정을 생성했든 신경 쓰지 않는다.
핵심은 해당 email
을 가진 user
가 이미 있는지 찾는 것이고 이런 유저를 로그인 시켜 줄거다.
로그인은 어떻게 시키냐면 위에서 있는 코드가 있으니 그것을 그대로 활용한다.
req.session.loggedIn = true;
req.session.user = user;
return res.redirect()
을 써서 "/"/
으로 돌아가게 한다.
하지만 이번에는 로그인이 된 상태가 된다.
다시 한번 확인하면 깃헙이 주는 list
에서 primary
이면서 verified
된 email
객체를 찾아야한다.
그리고 같은 email
을 가진 user
가 이미 있다면 그 유저를 로그인시켜 줄거다.
나중에 이 부분에서는 계정을 생성하는걸 추가해줘야 한다.
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
} else {
}// create an account
} else {
return res.redirect("/login");
}
};
무슨 말이냐면 해당 email
로 user
가 없으니까 계정을 생성해야 한다는 거다.
다행히도 이미 아주 만은 데이터를 가지고 있다.
email
도 있고 location
도 있고 name
도 있고 Github ID
도 있고
username
도 있다. 필요한 모든 것이 갖춰져 있다.
여태까지의 변경 사항들을 저장한 다음 테스트 해본다.
실행시켜보면 이 코드가 실행될거다.
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
왜냐하면 해당 email
을 가지고 있는 user
가 이미 데이터 베이스에 있고
Github
의 API
에서 해당 email
을 주기 때문이다.
테스트 해본다. 새로고침하고 나면 에러가 뜬다.
ReferenceError: user is not defined
user
가 정의되지 않았다고 뜬다.
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = existingUser;
user
를 existingUser
으로 변경해준다. 그러고 나서 다시 테스트를 해본다.
이제 로그인이 잘 된다.
이제 이 user
를 지우고 "만일 계정이 없다면 어떻게 할 것인가"에 대해 해보도록 한다.
그래서 하나를 만들어야 한다. 그리고 일종의 password
같은걸 만들어야 한다.
mongodb
에서 db.sessions.remove({})
를 해주고 나면
이 코드는 실행 되지 않을거다.
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = existingUser;
return res.redirect("/");
} else {
// create an account
// create an account
뭐가 실행되냐면 이게 실행될거다.
이제 user
를 생성해야 한다.
await User.create({
name,
username,
email,
password,
location,
이부분을 이용해서 사용한다. 똑같이 해보도록 한다. await User.create()
로 user
를 만들어 주고
const existingUser = await User.findOne({ email: emailObj.email });
if (existingUser) {
req.session.loggedIn = true;
req.session.user = existingUser;
return res.redirect("/");
} else {
const user = await User.create({
name: userData.name,
username: userData.login,
email: emailObj.email,
password: "",
location: userData.location,
});
그리고 나서 이제 값을 채우면 된다. name
은 userData.name
이다.
이 데이터는 객체이다. 그래서 userData.name
이라고 해준다.
다음은 username
이건 userData.login
이 될거다.
email
은 userData.email
이 아닌 emailObj.email
이다.
password
는 없으니 " "
으로 해준다.
location
은 이미 있으니 userData.location
으로 해준다.
그리고 User.js
에서 하나 추가해준다.
const userSchema = new mongoose.Schema({
githubId: { type: Number },
바로 githubId
이다. type
은 Number
이고 required:flase
, unique:true
로 해준다.
그냥 number
만 써준다. 이렇게 하는 이유는 user
가 Github
로 로그인했는지 여부를 알기 위해서이다.
이건 로그인 페이지에서 유저가 email
로 로그인하려는데 password
가 없을때 유용할수 있다.
githubId
를 체크하면 된다. 사실 githubId
가 아니라 githubLoginOnly
라 해준다.
아니면 noPasswordAccount
아니면 socialOnly
type
은 Boolean
으로 해준다.
default
는 false
로 해준다.
socialOnly: { type: Boolean, default: false },
이렇게 계정을 만들어주면 이 계정은 Github
을 이용해 계정을 만들었다면 password
는 없게 된다.
그렇다면 username
과 password form
을 사용 할수 없다.
그래서 계정이 socialOnly=true
라는걸 알려줘야 한다.
const user = await User.create({
name: userData.name,
username: userData.login,
email: emailObj.email,
password: "",
socialOnly: true,
location: userData.location,
});
이렇게 user
를 생성하도록 만들었다. 여기서 User.create()
는 새로 만든 user
를 return
시켜준다.
이 user
를 로그인 시켜줘야 한다.
const user = await User.create({
name: userData.name,
username: userData.login,
email: emailObj.email,
password: "",
socialOnly: true,
location: userData.location,
});
req.session.loggedIn = true;
req.session.user = user;
return res.redirect("/");
그래서 밑에 이렇게 넣어준다. 이 부분을 다듬어 보는건 다음에 해보도록 한다.
그리고 이건 다른 옵션이다. 데이터베이스에 해당 email
을 가진 user
가 없을때
이렇게 password
없이 Github
의 데이터로 user
를 생성하고
그런 user
에게는 socialOnly:true
값을 주고 있다.
그리고 여기 postLogin
때 유용 할거다.
export const postLogin = async (req, res) => {
왜냐하면 user
를 찾을때 몇몇 password
들을 비교해볼텐데
그러기 위해 if else
를 써야 될거 같다.
user
가 Github
로 계정을 만들고 password
로 로그인을 시도한다면
user
에게 "password
가 없으니 Github
로 로그인"하라고 말해줘야 한다.
아니면 pasword
를 새로 생성하는 페이지를 만들든지 해야 한다.
이제 테스트 해보도록 한다. 데이터베이스엔 user
가 없고
이 계정은 Github
데이터로만 만들어졌을거다.
const user = await User.create({
name: userData.name,
username: userData.login,
email: emailObj.email,
password: "",
socialOnly: true,
location: userData.location,
});
그리고 Github
데이터는 avatarUrl
이라 하는 것이 있는데 그건 나중에 써보도록 한다.
테스트를 해본다. login
-> Continue with Github
하면 계정이 생성되고
(해당 강의에서는 에러가 발생하는데 그냥 로그인이 잘된다.)
에러는 User
인증에 실패했습니다. User validation failed
라고 뜬다.
password
가 필수 조건이라고 한다.
password: { type: String, required: false },
password required
값을 false
로 바꿔준다. 유일한 옵션이다.
(그냥 없애도 잘 작동 한다.)
이건 몇몇 user
들에게는 password
가 없을테니 그런거다.
다시 실행해 보면 잘된다. (수정을 안 한 상태에서도 잘 작동하였지만 혹시 모를 에러때문에 이렇게 수정을 하고 진행하도록 한다. )
Github
으로 부터 온 프로필 이름이 나온다.
mongodb
로 가서 user
를 보면 socialOnly:true
인 계정이 생성 되었다.
password
는 없다. 왜냐하면 비어있는 해시값이기 때문이다.
username
은 Github username
일 테고 email
은 Github email
이다.
location
이런 것들 전부 Github
에서 오고 있다.