package auth import ( "encoding/json" "io/ioutil" "strconv" "golang.org/x/oauth2" ) var _ Provider = (*Github)(nil) // NameGithub is the unique name of the Github provider. const NameGithub string = "github" // Github allows authentication via Github OAuth2. type Github struct { *baseProvider } // NewGithubProvider creates new Github provider instance with some defaults. func NewGithubProvider() *Github { return &Github{&baseProvider{ scopes: []string{"user"}, authUrl: "https://github.com/login/oauth/authorize", tokenUrl: "https://github.com/login/oauth/access_token", userApiUrl: "https://api.github.com/user", }} } // FetchAuthUser returns an AuthUser instance based the Github's user api. func (p *Github) FetchAuthUser(token *oauth2.Token) (*AuthUser, error) { // https://docs.github.com/en/rest/reference/users#get-the-authenticated-user rawData := struct { Id int `json:"id"` Name string `json:"name"` Email string `json:"email"` AvatarUrl string `json:"avatar_url"` }{} if err := p.FetchRawUserData(token, &rawData); err != nil { return nil, err } user := &AuthUser{ Id: strconv.Itoa(rawData.Id), Name: rawData.Name, Email: rawData.Email, AvatarUrl: rawData.AvatarUrl, } // in case user set "Keep my email address private", // email should be retrieved via extra API request if user.Email == "" { client := p.Client(token) response, err := client.Get(p.userApiUrl + "/emails") if err != nil { return user, err } defer response.Body.Close() content, err := ioutil.ReadAll(response.Body) if err != nil { return user, err } emails := []struct { Email string Verified bool Primary bool }{} if err := json.Unmarshal(content, &emails); err != nil { return user, err } // extract the verified primary email for _, email := range emails { if email.Verified && email.Primary { user.Email = email.Email break } } } return user, nil }