Skip to content

Commit

Permalink
line: added email from id_token (#578)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexanderGrom authored Jan 14, 2025
1 parent 0c63ed9 commit 8ba0d36
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 7 deletions.
56 changes: 50 additions & 6 deletions providers/line/line.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import (
"fmt"
"io/ioutil"
"net/http"
"time"

"github.com/golang-jwt/jwt/v4"

Check failure on line 13 in providers/line/line.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, ubuntu-latest)

no required module provides package github.com/golang-jwt/jwt/v4; to add it:

Check failure on line 13 in providers/line/line.go

View workflow job for this annotation

GitHub Actions / test (1.21.x, macos-latest)

no required module provides package github.com/golang-jwt/jwt/v4; to add it:

Check failure on line 13 in providers/line/line.go

View workflow job for this annotation

GitHub Actions / test (1.22.x, ubuntu-latest)

no required module provides package github.com/golang-jwt/jwt/v4; to add it:

Check failure on line 13 in providers/line/line.go

View workflow job for this annotation

GitHub Actions / test (1.23.x, ubuntu-latest)

no required module provides package github.com/golang-jwt/jwt/v4; to add it:
"github.com/markbates/goth"
"golang.org/x/oauth2"
)
Expand All @@ -17,8 +19,14 @@ const (
authURL string = "https://access.line.me/oauth2/v2.1/authorize"
tokenURL string = "https://api.line.me/oauth2/v2.1/token"
endpointUser string = "https://api.line.me/v2/profile"
issuerURL string = "https://access.line.me"
)

type IDTokenClaims struct {
jwt.StandardClaims
Email string `json:"email"`
}

// Provider is the implementation of `goth.Provider` for accessing Line.me.
type Provider struct {
ClientKey string
Expand Down Expand Up @@ -95,11 +103,9 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {

response, err := c.Do(req)
if err != nil {
if response != nil {
response.Body.Close()
}
return user, err
}
defer response.Body.Close()

if response.StatusCode != http.StatusOK {
return user, fmt.Errorf("%s responded with a %d trying to fetch user information", p.providerName, response.StatusCode)
Expand All @@ -125,6 +131,13 @@ func (p *Provider) FetchUser(session goth.Session) (goth.User, error) {
user.NickName = u.DisplayName
user.AvatarURL = u.PictureURL
user.UserID = u.UserID

if sess.IDToken != "" {
if err = p.addDataFromIdToken(sess.IDToken, &user); err != nil {
return user, err
}
}

return user, err
}

Expand All @@ -141,9 +154,7 @@ func newConfig(provider *Provider, scopes []string) *oauth2.Config {
}

if len(scopes) > 0 {
for _, scope := range scopes {
c.Scopes = append(c.Scopes, scope)
}
c.Scopes = append(c.Scopes, scopes...)
}
return c
}
Expand All @@ -167,3 +178,36 @@ func (p *Provider) SetBotPrompt(botPrompt string) {
}
p.authCodeOptions = append(p.authCodeOptions, oauth2.SetAuthURLParam("bot_prompt", botPrompt))
}

func (p *Provider) addDataFromIdToken(idToken string, user *goth.User) error {
token, err := jwt.ParseWithClaims(idToken, &IDTokenClaims{}, func(t *jwt.Token) (interface{}, error) {
claims := t.Claims.(*IDTokenClaims)
vErr := new(jwt.ValidationError)

if !claims.VerifyAudience(p.ClientKey, true) {
vErr.Inner = fmt.Errorf("audience is incorrect")
vErr.Errors |= jwt.ValidationErrorAudience
}
if !claims.VerifyIssuer(issuerURL, true) {
vErr.Inner = fmt.Errorf("issuer is incorrect")
vErr.Errors |= jwt.ValidationErrorIssuer
}
if !claims.VerifyExpiresAt(time.Now().Unix(), true) {
vErr.Inner = fmt.Errorf("token is expired")
vErr.Errors |= jwt.ValidationErrorExpired
}
if vErr.Errors > 0 {
return nil, vErr
}

return []byte(p.Secret), nil
})

if err != nil {
return err
}

user.Email = token.Claims.(*IDTokenClaims).Email

return nil
}
6 changes: 6 additions & 0 deletions providers/line/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ type Session struct {
AccessToken string
RefreshToken string
ExpiresAt time.Time
IDToken string
}

var _ goth.Session = &Session{}
Expand Down Expand Up @@ -42,6 +43,11 @@ func (s *Session) Authorize(provider goth.Provider, params goth.Params) (string,
s.AccessToken = token.AccessToken
s.RefreshToken = token.RefreshToken
s.ExpiresAt = token.Expiry

if idToken := token.Extra("id_token"); idToken != nil {
s.IDToken = idToken.(string)
}

return token.AccessToken, err
}

Expand Down
2 changes: 1 addition & 1 deletion providers/line/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ func Test_ToJSON(t *testing.T) {
s := &line.Session{}

data := s.Marshal()
a.Equal(data, `{"AuthURL":"","AccessToken":"","RefreshToken":"","ExpiresAt":"0001-01-01T00:00:00Z"}`)
a.Equal(data, `{"AuthURL":"","AccessToken":"","RefreshToken":"","ExpiresAt":"0001-01-01T00:00:00Z","IDToken":""}`)
}

func Test_String(t *testing.T) {
Expand Down

0 comments on commit 8ba0d36

Please sign in to comment.