사용하는것
next14
nest
passport
github oauth
환경
frontend: localhost:3001
backend: localhost:3000
구현해야 하는것
우린 github oauth을 서비스에 이용한다. github profile을 이용해 서비스를 이용할 수 있게한다.
0. github에 어플리케이션을 등록한다.
https://github.com/settings/applications
GitHub: Let’s build from here
GitHub is where over 100 million developers shape the future of software, together. Contribute to the open source community, manage your Git repositories, review code like a pro, track bugs and fea...
github.com
clientid clientsecret을 발급받는다.
Homepage Url은 http://localhost:3001 로 설정한다.
Authorization callback URL은 http://localhost:3000/auth/github/callback 로 설정한다.
1. next frontend에선 github oauth server로 client_id를 담아 이동한다.
Next/Link컴포넌트를 이용한다. (pure react는 a태그)
import Image from "next/image";
import Link from "next/link";
const GithubSection = () => {
const clientId = process.env.NEXT_PUBLIC_GITHUB_CLIENT_ID;
const url = `https://github.com/login/oauth/authorize?client_id=${clientId}`;
return (
<Link
href={url}
>
<Image
src="/image/github-logo.png"
alt="github"
width={30}
height={30}
/>
깃허브로 로그인
</Link>
);
};
export default GithubSection;
중요한 것은 민감한 정보(cliendId)를 환경변수로 사용하는 것이다.
//.env.local
NEXT_PUBLIC_GITHUB_CLIENT_ID=발급받은 클라이언드 id
프로젝트 루트의 .env.local에 다음과 같이 작성한다.
NEXT_PUBLIC_ 을 앞애 붙여줘야한다. 아니면 undefined가 나온다.
이제 프론트는 백엔드가 열심히 일하는 것을 기다린다.
2. 그럼, 깃허브는 등록한 http://localhost:3000/auth/github/callback에 쿼리 파라미터로 code를 담아줄 것이다.
이 코드는 엑세스토큰을 github에 요청하고 발급받는데 사용된다.
3. nest backend에 환경 변수를 설정해준다.
// .env
GITHUB_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CALLBACK_URL=
JWT_SECRET=
필요한 값을 채워준다.
app.module에 환경변수 설정을해준다.
//app.module.ts
@Module({
imports: [
ConfigModule.forRoot(envConfig),
],
})
export class AppModule {
constructor(private dataSource: DataSource) {}
}
//envConfig
import { ConfigModuleOptions } from '@nestjs/config';
export const envConfig: ConfigModuleOptions = {
내 맘대로 설정
나같은 경우는 isGlobal: true,옵션을 사용한다.
또한 .env가 아닌 별도 환경변수 파일을 사용할시 envFilePath도 설정해준다.
};
4. github passport strategy를 작성해준다.
import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
import { Profile, Strategy } from 'passport-github';
@Injectable()
export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
constructor(configService: ConfigService) {
super({
clientID: configService.get<string>('GITHUB_CLIENT_ID'),
clientSecret: configService.get<string>('GITHUB_CLIENT_SECRET'),
callbackURL: configService.get<string>('GITHUB_CALLBACK_URL'),
scope: ['public_profile'],
});
}
async validate(
accessToken: string,
_refreshToken: string,
profile: Profile,
// done: any,
) {
// 필요한 validate로직
return profile;
}
}
설정한 환경변수로 strategy를 초기화해준다.
validate로직을 작성해준다.
(passport에 대해서 설명하는 것도 한 포스트가 될 것같아 자세한건 다른 포스트에 쓸 예정이다.)
참고: https://www.passportjs.org/packages/passport-github/
5. controller를 작성해준다.
import { Controller, Get, Redirect, Req, Res, UseGuards } from '@nestjs/common';
import { Request, Response } from 'express';
import { JwtAuthService } from '../jwt/jwt-auth.service';
import { AuthGuard } from '@nestjs/passport';
@Controller('auth/github')
export class GithubController {
constructor(private jwtAuthService: JwtAuthService) {}
// @Get()
// @UseGuards(AuthGuard('github'))
// async login() {}
@Get('callback')
@UseGuards(AuthGuard('github'))
async githubAuthCallback(
@Req() req: Request,
@Res({ passthrough: true }) res: Response,
) {
const user = req.user;
return user
}
}
6. module을 만들어준다.
@Module({
imports: [],
controllers: [GithubController],
providers: [GithubStrategy],
})
export class GithubModule {}
이제 http://localhost:3000/auth/github/callback에서 어떤일이 벌어질까.
UseGuards(AuthGuard('github'))이 해당 컨트롤러에서 github module에 주입된 github strategy 로직을 사용하게 해준다.
passport는 oauth2 프로세스에 따라 다음을 수행한다.
1. auth/gihub/callback?code=에서 코드를 추출한다.
2. 엑세스 토큰을 github에 요청 후 받는다.
3. 이제 엑세스토큰과 github profile정보를 사용할 수 있다.
이것을 우리가 일일히 작성할 필요없이 passport한테 맡기면 된다.
github strategy는 validate로 검증 후 req에 유저정보를 반환할 것이다.(위 코드에선 작성한 로직이 별거없으므로 단순 githubprofile정보가 반환된다.)
다음
다음 포스트에선 passport나 이를 jwt와 연동하는걸 해볼 생각이다.
우선, githubAuthCallback에서 로그인이 성공했거나 실패했다면 res.redirect로 localhost:3001의 특정 라우트로 라다이렉트 시키는 코드가 필요하다. 프론트와 협의해서 어디로 리다이렉트 시킬지 결정해본다.
이는 생략한다.
'개발' 카테고리의 다른 글
github oauth2 클라이언트와 서버 구현해보기 -2 (1) | 2024.02.15 |
---|---|
bcrypt invalid ELF header 에러 해결하기 (0) | 2024.02.14 |
react-hook-form을 이용한 복잡한 폼 구현 feat checkbox (1) | 2024.01.11 |
프로젝트에서 예상치 못한 곳에서 발생하는 리다이렉트를 막은 방법 (0) | 2024.01.08 |
react-hook-form 폼 유효성 검사기능을 추가한 뒤 submit이 안될 때 (0) | 2024.01.07 |