client: 일반 사용자가 아닌 소셜 로그인을 사용하는 사람
= 서비스를 만드는 "나"
resource owner: 소셜 로그인 기능을 제공하는 서비스를 사용하는 유저
= client가 받고자 하는 정보의 주인
resource server: 소셜 로그인 기능을 제공하는 곳
= client가 필요한 진짜 유저의 데이터를 가지고 있는 곳
1) 사용자가 소셜로그인 버튼을 클릭 시, 로그인하고자 하는 소셜의(카카오나 구글) 로그인페이지로 이동
2) 이 때 이 로그인 페이지로 가게 하기 위해, 서비스제공자와 소셜 사이에서의 이 상호작용 발생, 이 상호작용을 위해 서비스 제공자는 미리 OAuth라는 서비스를 사용
3) 로그인을 성공 시, 소셜은 사용자의 페이지가 기존에 사용하던 서비스 페이지로 redirect
중개자의 역할을 가능하도록 해주는 서비스 **OAuth **
=> 사용자가 소셜로그인에 로그인 시, OAuth를 거쳐서 소셜에서는 "나"에게 Access Token을 제공하고, "나"는 이 토큰을 통해서 소셜에 접근할 수 있게 되고, 사용자에게 로그인 페이지를 제공!!!!
1) 등록
Client는 Resource Server 를 사용하기 위해서는 등록필요! =
Google Developer, naver Developer와 같은 사이트에서 진행합니다.
등록 완료 후 , Client 와 Resource Server 는 3가지를 공유
절대 코드에 노출되어지면 안되는 정보!!! = env 파일로 따로 관리해야함!)2) 인증
로그인 하고자 하는 resource owner 즉, 데이터의 주인인 서비스 유저에게 승인필요.
버튼을 누르면, resource owner가 resource server에 로그인을 시도하는 창으로 이동
로그인 되어 있는 경우 : 소셜서비스(resource server)에서 로그인을 시도한 링크의 client ID를 점검
로그인 되어 있지 않은 경우 : 로그인을 진행
→ 로그인 완료 후에, 로그인을 시도한 링크의 redirect URL을 비교
- 소셜서비스(resource server)가 해당 URL을 가지고 있지 않다면 종료
→ 허용할 경우, 그 응답이 client에게 전달됩니다.
전달된 내용을 이용해 데이터 요청
서비스제공자인 "나"가 소셜서비스로 접속할때 가지고 간 주소링크에서 Authorizaion code와 clientID, client secret, redirect URL이 모두 일치하는지를 확인
일치하면 드디어 Access Token을 발급
→ 발급된 access token을 가지고 소셜서비스(resource server)에서 resource owner에 대한 데이터를 받아옴
// goole-oauth
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-google-oauth20';
@Injectable()
export class JwtGoogleStrategy extends PassportStrategy(Strategy, 'google') {
constructor() {
// 1. 검증부
super({
clientID: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
callbackURL: process.env.GOOGLE_CALLBACKURL,
scope: ['email', 'profile'],
});
}
// 2. 검증이 완료되면 실행
validate(accessToken: string, resfreshToken: string, profile: any) {
console.log(accessToken);
console.log(resfreshToken);
console.log(profile);
return {
// return 값은 context안의 request안 user로 들어감
email: profile.emails[0].value,
password: '1111',
name: profile.displayName,
phone: '01022222222',
address: '주소없음',
};
}
}
// naver-oauth
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-naver-v2';
@Injectable()
export class JwtNaverStrategy extends PassportStrategy(Strategy, 'naver') {
constructor() {
// 1. 검증부
super({
clientID: process.env.NAVER_CLIENT_ID,
clientSecret: process.env.NAVER_CLIENT_SECRET,
callbackURL: process.env.NAVER_CALLBACKURL,
});
}
// 2. 검증이 완료되면 실행
validate(accessToken: string, resfreshToken: string, profile: any) {
console.log(accessToken);
console.log(resfreshToken);
console.log(profile);
return {
// return 값은 context안의 request안 user로 들어감
email: profile.email,
password: '1111',
name: profile.name,
phone: profile.mobile,
address: '주소없음',
};
}
}
//kakao-oauth
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-kakao';
@Injectable()
export class JwtKakaoStrategy extends PassportStrategy(Strategy, 'kakao') {
constructor() {
// 1. 검증부
super({
clientID: process.env.KAKAO_CLIENT_ID,
clientSecret: process.env.KAKAO_CLIENT_SECRET,
callbackURL: process.env.KAKAO_CALLBACKURL,
scope: ['account_email', 'profile_nickname'],
});
}
// 2. 검증이 완료되면 실행
validate(accessToken: string, resfreshToken: string, profile: any) {
console.log(accessToken);
console.log(resfreshToken);
console.log(profile);
return {
// return 값은 context안의 request안 user로 들어감
email: profile._json.kakao_account.email,
password: '1111',
name: profile.username,
phone: '01022222222',
address: '주소없음',
};
}
}
=> 각 소셜마다 전체적인 사용 형태는 비슷하지만 profile내용이 전달되는 형태가 다르므로 확인 후 알맞게 사용해야함!