3

How to implement social login with GitHub so that I am able to get private emails of user for authentication if users email is set to private? Currently I am able to process user login when user email is public.

I know it has to be done somehow through https://api.github.com/user/emails but haven't been able to figure it out yet.

Current code for doing the social login through user public email:

package configs

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
)

type GithubUser struct {
    Email  string `json:"email"`
    Name   string `json:"name"`
    Avatar string `json:"avatar_url"`
}

func GetGithubAccessToken(code string) string {
    clientID := GithubKey()
    clientSecret := GithubSecret()

    requestBodyMap := map[string]string{
        "client_id":     clientID,
        "client_secret": clientSecret,
        "code":          code,
    }
    requestJSON, _ := json.Marshal(requestBodyMap)

    req, reqerr := http.NewRequest("POST", "https://github.com/login/oauth/access_token", bytes.NewBuffer(requestJSON))
    if reqerr != nil {
        log.Panic("Request creation failed")
    }
    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Accept", "application/json")

    resp, resperr := http.DefaultClient.Do(req)
    if resperr != nil {
        log.Panic("Request failed")
    }

    respbody, _ := ioutil.ReadAll(resp.Body)

    type githubAccessTokenResponse struct {
        AccessToken string `json:"access_token"`
        TokenType   string `json:"token_type"`
        Scope       string `json:"scope"`
    }

    var ghresp githubAccessTokenResponse
    json.Unmarshal(respbody, &ghresp)

    return ghresp.AccessToken
}

func GetGithubData(accessToken string) []byte {
    req, reqerr := http.NewRequest("GET", "https://api.github.com/user", nil)
    if reqerr != nil {
        log.Panic("API Request creation failed")
    }

    authorizationHeaderValue := fmt.Sprintf("token %s", accessToken)
    req.Header.Set("Authorization", authorizationHeaderValue)

    resp, resperr := http.DefaultClient.Do(req)
    if resperr != nil {
        log.Panic("Request failed")
    }

    respbody, _ := ioutil.ReadAll(resp.Body)

    return respbody
}

Egon
  • 410
  • 3
  • 8
  • 18

1 Answers1

2

I figured it out, leaving answer here maybe it saves someones time. Had to make a change to GetGithubData and add some more structs.

To GetGithubData function added the part to make a new request to retrieve the user's emails using the same access token that before to retrieve the user's email information.

type GithubData struct {
    UserInfo  UserInfo    `json:"user_info"`
    EmailInfo []EmailInfo `json:"email_info,omitempty"`
}

type UserInfo struct {
    AvatarURL string `json:"avatar_url"`
    Name      string `json:"name"`
    Email     string `json:"email"`
}

type EmailInfo struct {
    Email      string `json:"email"`
    Primary    bool   `json:"primary"`
    Verified   bool   `json:"verified"`
    Visibility string `json:"visibility"`
}

func GetGithubData(accessToken string) ([]byte, error) {
    req, reqerr := http.NewRequest("GET", "https://api.github.com/user", nil)
    if reqerr != nil {
        log.Panic("API Request creation failed")
    }

    authorizationHeaderValue := fmt.Sprintf("token %s", accessToken)
    req.Header.Set("Authorization", authorizationHeaderValue)

    resp, resperr := http.DefaultClient.Do(req)
    if resperr != nil {
        log.Panic("Request failed")
    }

    respbody, _ := ioutil.ReadAll(resp.Body)

    req, reqerr = http.NewRequest("GET", "https://api.github.com/user/emails", nil)
    if reqerr != nil {
        log.Panic("API Request creation failed")
    }

    req.Header.Set("Authorization", authorizationHeaderValue)

    resp, resperr = http.DefaultClient.Do(req)
    if resperr != nil {
        log.Panic("Request failed")
    }

    emailResp, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return nil, err
    }

    combinedData := fmt.Sprintf(`{"basic_info": %s, "email_info": %s}`, respbody, emailResp)
    return []byte(combinedData), nil
}

Egon
  • 410
  • 3
  • 8
  • 18