92 lines
2.3 KiB
Go
92 lines
2.3 KiB
Go
package github
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"io/ioutil"
|
|
"net/http"
|
|
"net/url"
|
|
|
|
"github.com/juju/errors"
|
|
|
|
"github.com/localhots/cmdui/backend/config"
|
|
)
|
|
|
|
type User struct {
|
|
ID uint `json:"id"`
|
|
Login string `json:"login"`
|
|
Name string `json:"name"`
|
|
Picture string `json:"avatar_url"`
|
|
}
|
|
|
|
const (
|
|
authorizeURL = "https://github.com/login/oauth/authorize"
|
|
accessTokenURL = "https://github.com/login/oauth/access_token"
|
|
userDetailsURL = "https://api.github.com/user"
|
|
)
|
|
|
|
func RedirectToLogin(w http.ResponseWriter, r *http.Request) {
|
|
urlStr := authorizeURL + "?" + url.Values{
|
|
"client_id": {config.Get().Github.ClientID},
|
|
"scope": {"user"},
|
|
}.Encode()
|
|
http.Redirect(w, r, urlStr, http.StatusTemporaryRedirect)
|
|
}
|
|
|
|
func ExchangeCode(ctx context.Context, code string) (accessToken string, err error) {
|
|
cfg := config.Get()
|
|
reqBody := bytes.NewBufferString(url.Values{
|
|
"client_id": {cfg.Github.ClientID},
|
|
"client_secret": {cfg.Github.ClientSecret},
|
|
"code": {code},
|
|
}.Encode())
|
|
req, err := http.NewRequest(http.MethodPost, accessTokenURL, reqBody)
|
|
if err != nil {
|
|
return "", errors.Annotate(err, "Failed to create a code exchange request")
|
|
}
|
|
|
|
// Passing client request context
|
|
req = req.WithContext(ctx)
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return "", errors.Annotate(err, "Failed to perform code exchange request")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
respBody, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return "", errors.Annotate(err, "Failed to read access token response")
|
|
}
|
|
|
|
uri, err := url.ParseQuery(string(respBody))
|
|
if err != nil {
|
|
return "", errors.Annotate(err, "Failed to parse access token response")
|
|
}
|
|
|
|
return uri.Get("access_token"), nil
|
|
}
|
|
|
|
func AuthDetails(accessToken string) (User, error) {
|
|
var u User
|
|
reqURL := userDetailsURL + "?" + url.Values{
|
|
"access_token": {accessToken},
|
|
}.Encode()
|
|
resp, err := http.Get(reqURL)
|
|
if err != nil {
|
|
return u, errors.Annotate(err, "Failed to fetch authenticated user details")
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return u, errors.Annotate(err, "Failed to read authenticated user details")
|
|
}
|
|
if err := json.Unmarshal(body, &u); err != nil {
|
|
return u, errors.Annotate(err, "Failed to parse authenticated user details")
|
|
}
|
|
|
|
return u, nil
|
|
}
|