examples/server/go/oauth2.go
package main
import (
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"github.com/google/go-querystring/query"
"github.com/parnurzeal/gorequest"
"gopkg.in/mgo.v2"
"gopkg.in/mgo.v2/bson"
)
type OAuth2Params struct {
Code string `json:"code" url:"code"`
ClientId string `json:"client_id" url:"client_id"`
ClientSecret string `json:"client_secret" url:"client_secret"`
RedirectUri string `json:"redirect_uri" url:"redirect_uri"`
GrantType string `json:"grant_type,omitempty" url:"grant_type,omitempty"`
}
type accessTokenData struct {
AccessToken string `json:"access_token" url:"access_token"`
TokenType string `json:"token_type" url:"token_type"`
ExpiresIn int `json:"expires_in" url:"expires_in"`
}
func (f *OAuth2Params) LoadFromHTTPRequest(r *http.Request) {
type requestData struct {
Code string `json:"code"`
ClientId string `json:"clientId"`
RedirectUri string `json:"redirectUri"`
}
decoder := json.NewDecoder(r.Body)
var data requestData
err := decoder.Decode(&data)
if err != nil {
panic(err)
}
f.Code = data.Code
f.ClientId = data.ClientId
f.RedirectUri = data.RedirectUri
}
func newFBParams() *OAuth2Params {
return &OAuth2Params{
ClientSecret: config.FACEBOOK_SECRET,
}
}
func newGoogleParams() *OAuth2Params {
return &OAuth2Params{
ClientSecret: config.GOOGLE_SECRET,
GrantType: "authorization_code",
}
}
func LoginWithFacebook(w http.ResponseWriter, r *http.Request) {
apiUrl := "https://graph.facebook.com"
accessTokenPath := "/v2.3/oauth/access_token"
graphApiPath := "/v2.3/me"
// Step 1. Exchange authorization code for access token.
fbparams := newFBParams()
fbparams.LoadFromHTTPRequest(r)
v, _ := query.Values(fbparams)
u, _ := url.ParseRequestURI(apiUrl)
u.Path = accessTokenPath
u.RawQuery = v.Encode()
urlStr := fmt.Sprintf("%v", u)
res, body, _ := gorequest.New().Get(urlStr).End()
if res.StatusCode != 200 {
var errorData map[string]interface{}
json.Unmarshal([]byte(body), &errorData)
ServeJSON(w, r, &Response{
"message": errorData["error"].(map[string]interface{})["message"],
}, 500)
return
}
// Step 2. Retrieve profile information about the current user.
var atData accessTokenData
err := json.Unmarshal([]byte(body), &atData)
v, _ = query.Values(atData)
u, _ = url.ParseRequestURI(apiUrl)
u.Path = graphApiPath
u.RawQuery = v.Encode()
urlStr = fmt.Sprintf("%v", u)
resProfile, body, _ := gorequest.New().Get(urlStr).End()
var profileData map[string]interface{}
err = json.Unmarshal([]byte(body), &profileData)
if resProfile.StatusCode != 200 {
ServeJSON(w, r, &Response{
"message": profileData["error"].(map[string]interface{})["message"],
}, 500)
return
}
db := GetDB(w, r)
if IsTokenSet(r) {
// Step 3a. Link user accounts.
existingUser, errM := FindUserByProvider(db, "facebook", profileData["id"].(string))
if existingUser != nil {
ServeJSON(w, r, &Response{
"message": "There is already a Facebook account that belongs to you",
}, 409)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
tokenData := GetToken(w, r)
user, errM := FindUserById(db, bson.ObjectIdHex(tokenData.ID))
if user == nil {
ServeJSON(w, r, &Response{
"message": "User not found",
}, 400)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
user.Facebook = profileData["id"].(string)
if user.Picture == "" {
user.Picture = "https://graph.facebook.com/v2.3/" + profileData["id"].(string) + "/picture?type=large"
}
if user.DisplayName == "" {
user.DisplayName = profileData["name"].(string)
}
err = user.Save(db)
if err != nil {
ISR(w, r, errors.New("Couldn't save user profile informations"))
}
SetToken(w, r, user)
} else {
// Step 3b. Create a new user account or return an existing one.
existingUser, errM := FindUserByProvider(db, "facebook", profileData["id"].(string))
if existingUser != nil {
SetToken(w, r, existingUser)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
// Create user with his facebook id
user := NewUser()
user.Facebook = profileData["id"].(string)
user.Email = profileData["email"].(string)
user.Picture = "https://graph.facebook.com/v2.3/" + profileData["id"].(string) + "/picture?type=large"
user.DisplayName = profileData["name"].(string)
err = user.Save(db)
if err != nil {
ISR(w, r, err)
return
}
SetToken(w, r, user)
}
}
func LoginWithGoogle(w http.ResponseWriter, r *http.Request) {
accessTokenUrl := "https://accounts.google.com/o/oauth2/token"
peopleApiUrl := "https://www.googleapis.com"
peopleApiPath := "/plus/v1/people/me/openIdConnect"
// Step 1. Exchange authorization code for access token.
googleParams := newGoogleParams()
googleParams.LoadFromHTTPRequest(r)
v, _ := query.Values(googleParams)
res, body, _ := gorequest.New().Post(accessTokenUrl).
Send(v.Encode()).
Type("form").
End()
if res.StatusCode != 200 {
var errorData map[string]interface{}
json.Unmarshal([]byte(body), &errorData)
ServeJSON(w, r, &Response{
"message": errorData["error"].(string),
}, 500)
return
}
// Step 2. Retrieve profile information about the current user.
var atData accessTokenData
err := json.Unmarshal([]byte(body), &atData)
qs, _ := query.Values(atData)
u, _ := url.ParseRequestURI(peopleApiUrl)
u.Path = peopleApiPath
u.RawQuery = qs.Encode()
urlStr := fmt.Sprintf("%v", u)
resProfile, body, _ := gorequest.New().Get(urlStr).End()
var profileData map[string]interface{}
err = json.Unmarshal([]byte(body), &profileData)
if resProfile.StatusCode != 200 {
ServeJSON(w, r, &Response{
"message": profileData["error"].(map[string]interface{})["message"],
}, 500)
return
}
db := GetDB(w, r)
if IsTokenSet(r) {
// Step 3a. Link user accounts.
existingUser, errM := FindUserByProvider(db, "google", profileData["sub"].(string))
if existingUser != nil {
ServeJSON(w, r, &Response{
"message": "There is already a Facebook account that belongs to you",
}, 409)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
tokenData := GetToken(w, r)
user, errM := FindUserById(db, bson.ObjectIdHex(tokenData.ID))
if user == nil {
ServeJSON(w, r, &Response{
"message": "User not found",
}, 400)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
user.Google = profileData["sub"].(string)
if user.Picture == "" {
user.Picture = strings.Replace(profileData["picture"].(string), "sz=50", "sz=200", -1)
}
if user.DisplayName == "" {
user.DisplayName = profileData["name"].(string)
}
err = user.Save(db)
if err != nil {
ISR(w, r, errors.New("Couldn't save user profile informations"))
}
SetToken(w, r, user)
} else {
// Step 3b. Create a new user account or return an existing one.
existingUser, errM := FindUserByProvider(db, "google", profileData["sub"].(string))
if existingUser != nil {
SetToken(w, r, existingUser)
return
}
if errM != nil && errM.Reason != mgo.ErrNotFound {
HandleModelError(w, r, errM)
return
}
// Create user with his google id
user := NewUser()
user.Google = profileData["sub"].(string)
user.Email = profileData["email"].(string)
user.Picture = strings.Replace(profileData["picture"].(string), "sz=50", "sz=200", -1)
user.DisplayName = profileData["name"].(string)
err = user.Save(db)
if err != nil {
ISR(w, r, err)
return
}
SetToken(w, r, user)
}
}
func NewClient() *http.Client {
return &http.Client{}
}