github.com/ngocphuongnb/tetua@v0.0.7-alpha/packages/auth/github.go (about) 1 package auth 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io/ioutil" 8 "net/http" 9 "strconv" 10 11 "github.com/ngocphuongnb/tetua/app/auth" 12 "github.com/ngocphuongnb/tetua/app/config" 13 "github.com/ngocphuongnb/tetua/app/entities" 14 "github.com/ngocphuongnb/tetua/app/server" 15 "github.com/ngocphuongnb/tetua/app/utils" 16 "golang.org/x/oauth2" 17 "golang.org/x/oauth2/github" 18 ) 19 20 const GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" 21 const GITHUB_USER_URL = "https://api.github.com/user" 22 23 type GithubAccessTokenResponse struct { 24 Scope string `json:"scope"` 25 TokenType string `json:"token_type"` 26 AccessToken string `json:"access_token"` 27 } 28 29 type GithubUserResponse struct { 30 Login string `json:"login"` 31 ID int `json:"id"` 32 AvatarURL string `json:"avatar_url"` 33 Name string `json:"name"` 34 Blog string `json:"blog"` 35 Email string `json:"email"` 36 Bio string `json:"bio"` 37 } 38 39 type GithubAuthProvider struct { 40 config *oauth2.Config 41 } 42 43 func NewGithub(cfg map[string]string) server.AuthProvider { 44 if cfg["client_id"] == "" || cfg["client_secret"] == "" { 45 panic("Github client id or secret is not set") 46 } 47 return &GithubAuthProvider{ 48 config: &oauth2.Config{ 49 ClientID: cfg["client_id"], 50 ClientSecret: cfg["client_secret"], 51 RedirectURL: utils.Url("/auth/github/callback"), 52 Endpoint: github.Endpoint, 53 }, 54 } 55 } 56 57 func (g *GithubAuthProvider) Name() string { 58 return "github" 59 } 60 61 func (g *GithubAuthProvider) GetGithubAccessToken(code string) (string, error) { 62 requestBody := map[string]string{ 63 "code": code, 64 "client_id": g.config.ClientID, 65 "client_secret": g.config.ClientSecret, 66 } 67 requestJSON, _ := json.Marshal(requestBody) 68 req, err := http.NewRequest( 69 "POST", 70 GITHUB_ACCESS_TOKEN_URL, 71 bytes.NewBuffer(requestJSON), 72 ) 73 74 if err != nil { 75 return "", err 76 } 77 78 req.Header.Set("Content-Type", "application/json") 79 req.Header.Set("Accept", "application/json") 80 81 resp, err := http.DefaultClient.Do(req) 82 if err != nil { 83 return "", err 84 } 85 86 body, err := ioutil.ReadAll(resp.Body) 87 88 if err != nil { 89 return "", err 90 } 91 92 var accessTokenResponse GithubAccessTokenResponse 93 if err := json.Unmarshal(body, &accessTokenResponse); err != nil { 94 return "", err 95 } 96 97 return accessTokenResponse.AccessToken, nil 98 } 99 100 func (g *GithubAuthProvider) GetGithubUser(accessToken string) (*GithubUserResponse, error) { 101 req, err := http.NewRequest( 102 "GET", 103 GITHUB_USER_URL, 104 nil, 105 ) 106 107 if err != nil { 108 return nil, err 109 } 110 111 req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken)) 112 resp, err := http.DefaultClient.Do(req) 113 114 if err != nil { 115 return nil, err 116 } 117 118 body, err := ioutil.ReadAll(resp.Body) 119 120 if err != nil { 121 return nil, err 122 } 123 124 userResponse := &GithubUserResponse{} 125 if err := json.Unmarshal(body, userResponse); err != nil { 126 return nil, err 127 } 128 129 return userResponse, nil 130 } 131 132 func (g *GithubAuthProvider) GetGithubUserFromAccessCode(code string) (*GithubUserResponse, error) { 133 accessToken, err := g.GetGithubAccessToken(code) 134 if err != nil { 135 return nil, err 136 } 137 138 return g.GetGithubUser(accessToken) 139 } 140 141 func (g *GithubAuthProvider) Login(c server.Context) error { 142 url := g.config.AuthCodeURL( 143 c.Cookies(config.COOKIE_UUID), 144 oauth2.AccessTypeOffline, 145 oauth2.SetAuthURLParam("scope", "user:email"), 146 ) 147 148 return c.Redirect(url) 149 } 150 151 func (g *GithubAuthProvider) Callback(c server.Context) (u *entities.User, err error) { 152 if c.Query("code") == "" { 153 return nil, fmt.Errorf("code is empty") 154 } 155 156 githubUser, err := g.GetGithubUserFromAccessCode(c.Query("code")) 157 158 if err != nil { 159 return nil, err 160 } 161 162 return &entities.User{ 163 Provider: "github", 164 ProviderID: utils.SanitizePlainText(strconv.Itoa(githubUser.ID)), 165 Username: utils.SanitizePlainText(githubUser.Login), 166 Email: utils.SanitizePlainText(githubUser.Email), 167 ProviderAvatar: utils.SanitizePlainText(githubUser.AvatarURL), 168 DisplayName: utils.SanitizePlainText(githubUser.Name), 169 URL: utils.SanitizePlainText(githubUser.Blog), 170 ProviderUsername: utils.SanitizePlainText(githubUser.Login), 171 RoleIDs: []int{auth.ROLE_USER.ID}, 172 Active: config.Setting("auto_approve_user") == "yes", 173 }, nil 174 }