/
google.go
139 lines (123 loc) · 3.83 KB
/
google.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Package google provides you access to Google's OAuth2
// infrastructure. The implementation is based on this blog post:
// http://skarlso.github.io/2016/06/12/google-signin-with-go/
package google
import (
"crypto/rand"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin"
"github.com/golang/glog"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)
// Credentials stores google client-ids.
type Credentials struct {
ClientID string `json:"clientid"`
ClientSecret string `json:"secret"`
}
// User is a retrieved and authenticated user.
type User struct {
Sub string `json:"sub"`
Name string `json:"name"`
GivenName string `json:"given_name"`
FamilyName string `json:"family_name"`
Profile string `json:"profile"`
Picture string `json:"picture"`
Email string `json:"email"`
EmailVerified bool `json:"email_verified"`
Gender string `json:"gender"`
Hd string `json:"hd"`
}
var cred Credentials
var conf *oauth2.Config
var state string
var store sessions.CookieStore
func randToken() string {
b := make([]byte, 32)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
}
// Setup the authorization path
func Setup(redirectURL, credFile string, scopes []string, secret []byte) {
store = sessions.NewCookieStore(secret)
var c Credentials
file, err := ioutil.ReadFile(credFile)
if err != nil {
glog.Fatalf("[Gin-OAuth] File error: %v\n", err)
}
json.Unmarshal(file, &c)
conf = &oauth2.Config{
ClientID: c.ClientID,
ClientSecret: c.ClientSecret,
RedirectURL: redirectURL,
Scopes: scopes,
Endpoint: google.Endpoint,
}
}
func Session(name string) gin.HandlerFunc {
return sessions.Sessions(name, store)
}
func LoginHandler(ctx *gin.Context) {
state = randToken()
session := sessions.Default(ctx)
session.Set("state", state)
session.Save()
ctx.Writer.Write([]byte("<html><title>Golang Google</title> <body> <a href='" + GetLoginURL(state) + "'><button>Login with Google!</button> </a> </body></html>"))
}
func GetLoginURL(state string) string {
return conf.AuthCodeURL(state)
}
// Auth is the google authorization middleware. You can use them to protect a routergroup.
// Example:
//
// private.Use(google.Auth())
// private.GET("/", UserInfoHandler)
// private.GET("/api", func(ctx *gin.Context) {
// ctx.JSON(200, gin.H{"message": "Hello from private for groups"})
// })
// func UserInfoHandler(ctx *gin.Context) {
// ctx.JSON(http.StatusOK, gin.H{"Hello": "from private", "user": ctx.MustGet("user").(google.User)})
// }
func Auth() gin.HandlerFunc {
return func(ctx *gin.Context) {
// Handle the exchange code to initiate a transport.
session := sessions.Default(ctx)
retrievedState := session.Get("state")
if retrievedState != ctx.Query("state") {
ctx.AbortWithError(http.StatusUnauthorized, fmt.Errorf("Invalid session state: %s", retrievedState))
return
}
tok, err := conf.Exchange(oauth2.NoContext, ctx.Query("code"))
if err != nil {
ctx.AbortWithError(http.StatusBadRequest, err)
return
}
client := conf.Client(oauth2.NoContext, tok)
email, err := client.Get("https://www.googleapis.com/oauth2/v3/userinfo")
if err != nil {
ctx.AbortWithError(http.StatusBadRequest, err)
return
}
defer email.Body.Close()
data, err := ioutil.ReadAll(email.Body)
if err != nil {
glog.Errorf("[Gin-OAuth] Could not read Body: %s", err)
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
var user User
err = json.Unmarshal(data, &user)
if err != nil {
glog.Errorf("[Gin-OAuth] Unmarshal userinfo failed: %s", err)
ctx.AbortWithError(http.StatusInternalServerError, err)
return
}
// save userinfo, which could be used in Handlers
ctx.Set("user", user)
}
}