Skip to content

Commit

Permalink
feat: Implement "Verify ID Token"
Browse files Browse the repository at this point in the history
  • Loading branch information
DaysJan committed Oct 18, 2024
1 parent e9ca982 commit 3be2995
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 0 deletions.
27 changes: 27 additions & 0 deletions response.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,21 @@ type TokenRefreshResponse struct {
RefreshToken string `json:"refresh_token"`
}

// VerifyIDTokenResponse type
type VerifyIDTokenResponse struct {
Iss string `json:"iss"`
Sub string `json:"sub"`
Aud string `json:"aud"`
Exp int `json:"exp"`
Iat int `json:"iat"`
AuthTime int `json:"auth_time"`
Nonce string `json:"nonce"`
Amr []string `json:"amr"`
Name string `json:"name"`
Picture string `json:"picture"`
Email string `json:"email"`
}

// GetUserProfileResponse type
type GetUserProfileResponse struct {
// UserID: Identifier of the user
Expand Down Expand Up @@ -239,6 +254,18 @@ func decodeToTokenRefreshResponse(res *http.Response) (*TokenRefreshResponse, er
return &result, nil
}

func decodeToVerifyIDTokenResponse(res *http.Response) (*VerifyIDTokenResponse, error) {
if err := checkResponse(res); err != nil {
return nil, err
}
decoder := json.NewDecoder(res.Body)
result := VerifyIDTokenResponse{}
if err := decoder.Decode(&result); err != nil {
return nil, err
}
return &result, nil
}

func decodeToGetUserProfileResponse(res *http.Response) (*GetUserProfileResponse, error) {
if err := checkResponse(res); err != nil {
return nil, err
Expand Down
59 changes: 59 additions & 0 deletions social.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,65 @@ func (call *RevokeTokenCall) Do() (*BasicResponse, error) {
return decodeToBasicResponse(res)
}

// VerifyIDToken ID tokens are JSON web tokens (JWT) with information about the
// user. It's possible for an attacker to spoof an ID token. Use this call to
// verify that a received ID token is authentic, meaning you can use it to obtain
// the user's profile information and email.
// https://developers.line.biz/en/reference/line-login/#verify-id-token
func (client *Client) VerifyIDToken(iDToken string, options VerifyIDTokenRequestOptions) *VerifyIDTokenCall {
return &VerifyIDTokenCall{
c: client,
iDToken: iDToken,
options: options,
}
}

type VerifyIDTokenRequestOptions struct {
nonce string
userID string
}

// VerifyIDTokenCall type
type VerifyIDTokenCall struct {
c *Client
ctx context.Context

iDToken string
options VerifyIDTokenRequestOptions
}

// WithContext method
func (call *VerifyIDTokenCall) WithContext(ctx context.Context) *VerifyIDTokenCall {
call.ctx = ctx
return call
}

// Do method
func (call *VerifyIDTokenCall) Do() (*VerifyIDTokenResponse, error) {
data := url.Values{}
data.Set("id_token", call.iDToken)
data.Set("client_id", call.c.channelID)

if call.options.nonce != "" {
data.Set("nonce", call.options.nonce)
}

if call.options.userID != "" {
data.Set("user_id", call.options.userID)
}

res, err := call.c.post(call.ctx, APIEndpointTokenVerify, strings.NewReader(data.Encode()))
if res != nil && res.Body != nil {
defer res.Body.Close()
}

if err != nil {
return nil, err
}

return decodeToVerifyIDTokenResponse(res)
}

// GetUserProfile: Gets a user's display name, profile image, and status message.
//Note: Requires an access token with the profile scope. For more information, see Making an authorization request and Scopes.
func (client *Client) GetUserProfile(accessToken string) *GetUserProfileCall {
Expand Down
21 changes: 21 additions & 0 deletions social_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,24 @@ import (
var (
accessToken string
refreshToken string
iDToken string
cID string
cSecret string
qURL string
code string
userID string
)

//Provide those data for testing.
func init() {
accessToken = os.Getenv("LINE_ACCESS_TOKEN")
refreshToken = os.Getenv("LINE_REFRESH_TOKEN")
iDToken = os.Getenv("LINE_ID_TOKEN")
cID = os.Getenv("LINE_CLIENT_ID")
cSecret = os.Getenv("LINE_CLIENT_SECRET")
qURL = os.Getenv("LINE_SERVER_URL")
code = os.Getenv("LINE_LOGIN_CODE")
userID = os.Getenv("LINE_USER_ID")
}

func checkEnvVariables(t *testing.T) {
Expand Down Expand Up @@ -123,6 +127,23 @@ func TestRevokeToken(t *testing.T) {
log.Println("ret:", ret)
}

func TestVerifyIDToken(t *testing.T) {
checkEnvVariables(t)

nonce := GenerateNonce()

client, _ := New(cID, cSecret)
ret, err := client.VerifyIDToken(iDToken, VerifyIDTokenRequestOptions{
nonce: nonce,
userID: userID,
}).Do()
if err != nil {
log.Println("err:", err)
}

log.Println("ret:", ret)
}

func TestGetAccessTokenPKCE(t *testing.T) {
checkEnvVariables(t)

Expand Down

0 comments on commit 3be2995

Please # to comment.