8
8
"fmt"
9
9
"io/ioutil"
10
10
"net/http"
11
+ "time"
11
12
13
+ "github.com/golang-jwt/jwt/v4"
12
14
"github.com/markbates/goth"
13
15
"golang.org/x/oauth2"
14
16
)
@@ -17,8 +19,14 @@ const (
17
19
authURL string = "https://access.line.me/oauth2/v2.1/authorize"
18
20
tokenURL string = "https://api.line.me/oauth2/v2.1/token"
19
21
endpointUser string = "https://api.line.me/v2/profile"
22
+ issuerURL string = "https://access.line.me"
20
23
)
21
24
25
+ type IDTokenClaims struct {
26
+ jwt.StandardClaims
27
+ Email string `json:"email"`
28
+ }
29
+
22
30
// Provider is the implementation of `goth.Provider` for accessing Line.me.
23
31
type Provider struct {
24
32
ClientKey string
@@ -95,11 +103,9 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
95
103
96
104
response , err := c .Do (req )
97
105
if err != nil {
98
- if response != nil {
99
- response .Body .Close ()
100
- }
101
106
return user , err
102
107
}
108
+ defer response .Body .Close ()
103
109
104
110
if response .StatusCode != http .StatusOK {
105
111
return user , fmt .Errorf ("%s responded with a %d trying to fetch user information" , p .providerName , response .StatusCode )
@@ -125,6 +131,13 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
125
131
user .NickName = u .DisplayName
126
132
user .AvatarURL = u .PictureURL
127
133
user .UserID = u .UserID
134
+
135
+ if sess .IDToken != "" {
136
+ if err = p .addDataFromIdToken (sess .IDToken , & user ); err != nil {
137
+ return user , err
138
+ }
139
+ }
140
+
128
141
return user , err
129
142
}
130
143
@@ -141,9 +154,7 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
141
154
}
142
155
143
156
if len (scopes ) > 0 {
144
- for _ , scope := range scopes {
145
- c .Scopes = append (c .Scopes , scope )
146
- }
157
+ c .Scopes = append (c .Scopes , scopes ... )
147
158
}
148
159
return c
149
160
}
@@ -167,3 +178,36 @@ func (p *Provider) SetBotPrompt(botPrompt string) {
167
178
}
168
179
p .authCodeOptions = append (p .authCodeOptions , oauth2 .SetAuthURLParam ("bot_prompt" , botPrompt ))
169
180
}
181
+
182
+ func (p * Provider ) addDataFromIdToken (idToken string , user * goth.User ) error {
183
+ token , err := jwt .ParseWithClaims (idToken , & IDTokenClaims {}, func (t * jwt.Token ) (interface {}, error ) {
184
+ claims := t .Claims .(* IDTokenClaims )
185
+ vErr := new (jwt.ValidationError )
186
+
187
+ if ! claims .VerifyAudience (p .ClientKey , true ) {
188
+ vErr .Inner = fmt .Errorf ("audience is incorrect" )
189
+ vErr .Errors |= jwt .ValidationErrorAudience
190
+ }
191
+ if ! claims .VerifyIssuer (issuerURL , true ) {
192
+ vErr .Inner = fmt .Errorf ("issuer is incorrect" )
193
+ vErr .Errors |= jwt .ValidationErrorIssuer
194
+ }
195
+ if ! claims .VerifyExpiresAt (time .Now ().Unix (), true ) {
196
+ vErr .Inner = fmt .Errorf ("token is expired" )
197
+ vErr .Errors |= jwt .ValidationErrorExpired
198
+ }
199
+ if vErr .Errors > 0 {
200
+ return nil , vErr
201
+ }
202
+
203
+ return []byte (p .Secret ), nil
204
+ })
205
+
206
+ if err != nil {
207
+ return err
208
+ }
209
+
210
+ user .Email = token .Claims .(* IDTokenClaims ).Email
211
+
212
+ return nil
213
+ }
0 commit comments