From 722f57c90f0c1b813a4eeefcbd5a97e819b0757d Mon Sep 17 00:00:00 2001 From: peterxcli Date: Mon, 15 Jan 2024 21:39:55 +0800 Subject: [PATCH] feat: auto add line bot after line oauth consent --- README.md | 1 + pkg/controller/oauth_controller.go | 36 +++++++++------------ pkg/line_utils/social.go | 52 ++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 21 deletions(-) create mode 100644 pkg/line_utils/social.go diff --git a/README.md b/README.md index f7e72f3..26ddba7 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ - [[Golang][LINE][教學] 將你的 chatbot 透過 account link 連接你的服務](https://www.evanlin.com/line-accountlink/) - [[Golang][LINE][教學] 導入 LINE Login 到你的商業網站之中,並且加入官方帳號為好友](https://www.evanlin.com/line-login/) - [github](https://github.com/kkdai/line-login-go) +- [Official Document for Linking a LINE Official Account when Login](https://developers.line.biz/en/docs/line-login/link-a-bot/#link-a-line-official-account) ## Branch/Commit Type diff --git a/pkg/controller/oauth_controller.go b/pkg/controller/oauth_controller.go index 764d91e..13f435a 100644 --- a/pkg/controller/oauth_controller.go +++ b/pkg/controller/oauth_controller.go @@ -2,6 +2,7 @@ package controller import ( "bikefest/pkg/bootstrap" + "bikefest/pkg/line_utils" "bikefest/pkg/model" "fmt" "github.com/gin-gonic/gin" @@ -54,7 +55,7 @@ func (ctrl *OAuthController) LineLogin(c *gin.Context) { } nonce := social.GenerateNonce() redirectURL := fmt.Sprintf("%s/line-login/callback", serverURL) - targetURL := ctrl.lineSocialClient.GetWebLoinURL(redirectURL, state, scope, social.AuthRequestOptions{Nonce: nonce, Prompt: "consent"}) + targetURL := ctrl.lineSocialClient.GetWebLoinURL(redirectURL, state, scope, social.AuthRequestOptions{Nonce: nonce, Prompt: "consent", BotPrompt: "aggressive"}) c.SetCookie("state", state, 3600, "/", "", false, true) c.Redirect(http.StatusFound, targetURL) } @@ -90,34 +91,27 @@ func (ctrl *OAuthController) LineLoginCallback(c *gin.Context) { } log.Println("access_token:", token.AccessToken, " refresh_token:", token.RefreshToken) + // check friendship with official account + friendFlag, err := line_utils.GetFriendshipStatus(token.AccessToken) + if err != nil { + c.AbortWithStatusJSON(http.StatusInternalServerError, model.Response{ + Msg: err.Error(), + }) + } + if friendFlag != true { + c.AbortWithStatusJSON(http.StatusForbidden, model.Response{ + Msg: "You are not a friend of the official account", + }) + return + } var payload *social.Payload - //if len(token.IDToken) == 0 { - // // User don't request openID, use access token to get user profile - // log.Println(" token:", token, " AccessToken:", token.AccessToken) - // res, err := ctrl.lineSocialClient.GetUserProfile(token.AccessToken).Do() - // if err != nil { - // log.Println("GetUserProfile err:", err) - // return - // } - // payload = &social.Payload{ - // Name: res.DisplayName, - // Picture: res.PictureURL, - // } - //} else { - //Decode token.IDToken to payload payload, err = token.DecodePayload(ctrl.env.Line.ChannelID) if err != nil { log.Println("DecodeIDToken err:", err) return } - //} log.Printf("payload: %#v", payload) - //c.JSON(http.StatusOK, gin.H{ - // "status": "Success", - // "data": payload, - //}) - user := &model.User{ ID: payload.Sub, Name: payload.Name, diff --git a/pkg/line_utils/social.go b/pkg/line_utils/social.go new file mode 100644 index 0000000..5f99bf1 --- /dev/null +++ b/pkg/line_utils/social.go @@ -0,0 +1,52 @@ +package line_utils + +import ( + "encoding/json" + "io" + "net/http" +) + +type FriendshipStatusResponse struct { + FriendFlag bool `json:"friendFlag"` +} + +func GetFriendshipStatus(accessToken string) (friendFlag bool, err error) { + // Define the API endpoint + url := "https://api.line.me/friendship/v1/status" + + // Create a new HTTP client + client := &http.Client{} + + // Create the request + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return false, err + } + + // Add the Authorization header + req.Header.Add("Authorization", "Bearer "+accessToken) + + // Perform the request + resp, err := client.Do(req) + if err != nil { + return false, err + } + defer func(Body io.ReadCloser) { + _ = Body.Close() + }(resp.Body) + + // Read the response body + body, err := io.ReadAll(resp.Body) + if err != nil { + return false, err + } + + // Unmarshal the JSON response + var response FriendshipStatusResponse + err = json.Unmarshal(body, &response) + if err != nil { + return false, err + } + + return response.FriendFlag, nil +}