1. OAuth Applciation 등록
GCP Console (https://console.cloud.google.com/)

외부를 클릭하여 만들고 앱 이름 , 사용자 지원 이메일, 개발자 연락처 정보만 기입하고 나머지는 빈칸으로 둔다.

그 후 사용자 인증정보를 OAuth 클라이언트 ID 를 선택해서 생성한다.
테스트 해볼 용도이니 출처는 localhost:5000 으로 리디렉션도 마찬가지로 localhost:5000/auth/google/callback 으로 했다.


이때 생성되는 클라이언트 ID 와 비밀번호를 잘 보관하자 이 키를 기반으로 구글인증을 진행할수 있다.
2. golang handler 작성
이제는 위의 저장된 정보를 기반으로 고 핸들러를 작성해보자.
현재 쓰고있는 프레임워크는 긴이라 긴으로 작성했지만 이건 쓰고있는 프레임워크를 선택하면 된다.
구글인증을 사용하기 위해선 아래와같은 패키지 설치가 필요하다.
go get -u github.com/gin-gonic/gin
go get golang.org/x/oauth2
go get cloud.google.com/go
main.go
func main() {
r := gin.Default()
r.GET("/", googleForm)
r.GET("/auth/google/login", googleLoginHandler)
r.GET("/auth/google/callback", googleAuthCallback)
r.Run(":5000")
}
구현한 핸들러는 3개 테스트로 작성해서 모든 함수가 main package 에 있지만 알맞게 라우팅을 해서 고도화 하면 된다.
func googleForm(c *gin.Context) {
c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(
"<html>" +
"\n<head>\n " +
"<title>Go Oauth2.0 Test</title>\n" +
"</head>\n" +
"<body>\n<p>" +
"<a href='./auth/google/login'>Google Login</a>" +
"</p>\n" +
"</body>\n" +
"</html>"))
}
localhost:5000/ 으로 들어가면

간단한 Google Login 버튼을 구현한 html 사이트가 나오고 이 버튼을 누르면
localhost:5000/auth/google/login -> 즉 googleLoginHandler 를 실행하게 된다.
var googleOauthConfig = oauth2.Config{
RedirectURL: "http://localhost:3000/auth/google/callback",
ClientID: "위에서 발급받은 ID",
ClientSecret: "위에서 발급받은 Secret",
Scopes: []string{"https://www.googleapis.com/auth/userinfo.email"},
Endpoint: google.Endpoint,
}
func generateStateOauthCookie(w http.ResponseWriter) string {
expiration := time.Now().Add(1 * 24 * time.Hour)
b := make([]byte, 16)
rand.Read(b)
state := base64.URLEncoding.EncodeToString(b)
cookie := &http.Cookie{Name: "oauthstate", Value: state, Expires: expiration}
http.SetCookie(w, cookie)
return state
}
func googleLoginHandler(c *gin.Context) {
state := generateStateOauthCookie(c.Writer)
url := googleOauthConfig.AuthCodeURL(state)
c.Redirect(http.StatusTemporaryRedirect,url)
}
이제 googleLoginHandler 를 작성할 차례다.
state : 패키지로 받은 AuthCodeURL 함수에 인자로 들어가는 값이다. 베이스64인코딩을 해주고 쿠기로 저장한다.
url : 패키지를 받은 googleOauthConfig.AuthCodeURL(state) 를 실행하게 되면
구글에서 Config 에 들어있는 값 기반으로 인증을 한후 우리에게 url 을 전달해주고 우리는 다시 거기로 redirect 를 보낸다.
url 은 https://accounts.google.com/o/oauth2/auth 뒤에 client_id 와 scope , state 등 필수정보를 포함한
query 값을 보내주는데 이걸 들어가면 아래와 같은 구글 로그인 화면이 나온다.

이제 저기서 구글 로그인을 하면 위에서 설정한 승인된 Redirection URI 로 정보를 받아와야 한다.
googleAuthCallback 핸들러가 그역할을 하는데 아래 코드다.
(localhost:5000/auth/google/callback)
func googleAuthCallback(c *gin.Context) {
oauthstate, _ := c.Request.Cookie("oauthstate") // 12
if c.Request.FormValue("state") != oauthstate.Value { // 13
log.Printf("invalid google oauth state cookie:%s state:%s\n", oauthstate.Value, c.Request.FormValue("state"))
c.Redirect(http.StatusTemporaryRedirect,"/")
return
}
data, err := getGoogleUserInfo(c.Request.FormValue("code")) // 14
if err != nil { // 15
log.Println(err.Error())
c.Redirect( http.StatusTemporaryRedirect,"/")
return
}
fmt.Fprint(c.Writer, string(data)) // 16
}
func getGoogleUserInfo(code string) ([]byte, error) { // 17
token, err := googleOauthConfig.Exchange(context.Background(), code) // 18
if err != nil { // 19
return nil, fmt.Errorf("Failed to Exchange %s\n", err.Error())
}
resp, err := http.Get(oauthGoogleUrlAPI + token.AccessToken) // 20
if err != nil { // 21
return nil, fmt.Errorf("Failed to Get UserInfo %s\n", err.Error())
}
return ioutil.ReadAll(resp.Body) // 23
}
우리가 state 를 위에서 생성한 후 쿠키에 저장을 해놨다가 Callback 함수가 실행됐을때 그정보를 response 값이랑 비교를 해서 같은지 확인한후 받아온 code 값으로 .Exchange 함수를 사용해서 Token 값을 가져올 수 있다.
우리는 이 토큰 값으로 JWT 을 생성해서 관리하거나 필요에 따라 맞게 활용 할 수 있다.
위의 코드는 구글이 토큰값으로 인증한 후에 오는 데이터값을 반환하는데 JSON 형식으로
id , email , verified_email , picture 를 반환한다.
POST
https://www.googleapis.com/oauth2/v3/userinfo?access_token=[토큰값] 을 주면 아래와 같은 데이터를 가져오게 된다.
{
"id": "186",
"email": "myungsworld@gmail.com",
"verified_email": true,
"picture": "https://lh3.googleusercontent.com/a-/AOhGbjsc=s96-c"
}
이상 구글 Oauth2 인증을 구현해봤다.
'Go' 카테고리의 다른 글
golang 싱글톤 패턴(Singleton Pattern with golang) (0) | 2022.07.14 |
---|---|
고에서 에러 제어 (0) | 2022.06.08 |
golang Gracefully Shutdown (위대한 쇼다운맨 ) (0) | 2022.05.03 |
Golang TDD (테스트 주도 개발) 유닛테스트 루트 절대경로 설정 (0) | 2022.04.08 |
고(golang) 메일보내기(google) (0) | 2022.01.13 |