114 lines
3.1 KiB
Go
114 lines
3.1 KiB
Go
package usecase
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type googleUserInfoResp struct {
|
|
ID string `json:"id"`
|
|
Email string `json:"email"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
func googleUserInfo(ctx context.Context, accessOrIDToken string) (*googleUserInfoResp, error) {
|
|
// Prefer userinfo with Bearer access token (stand-alone style)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+accessOrIDToken)
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("google userinfo status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
var info googleUserInfoResp
|
|
if err := json.Unmarshal(body, &info); err != nil {
|
|
return nil, err
|
|
}
|
|
if info.ID == "" {
|
|
return nil, fmt.Errorf("google userinfo missing id")
|
|
}
|
|
return &info, nil
|
|
}
|
|
|
|
type lineTokenResp struct {
|
|
AccessToken string `json:"access_token"`
|
|
}
|
|
|
|
type lineProfileResp struct {
|
|
UserID string `json:"userId"`
|
|
DisplayName string `json:"displayName"`
|
|
}
|
|
|
|
func lineExchangeCode(ctx context.Context, code, redirectURI, clientID, clientSecret string) (string, error) {
|
|
data := url.Values{}
|
|
data.Set("grant_type", "authorization_code")
|
|
data.Set("code", code)
|
|
data.Set("redirect_uri", redirectURI)
|
|
data.Set("client_id", clientID)
|
|
data.Set("client_secret", clientSecret)
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.line.me/oauth2/v2.1/token",
|
|
strings.NewReader(data.Encode()))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != http.StatusOK {
|
|
return "", fmt.Errorf("line token status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
var tok lineTokenResp
|
|
if err := json.Unmarshal(body, &tok); err != nil {
|
|
return "", err
|
|
}
|
|
if tok.AccessToken == "" {
|
|
return "", fmt.Errorf("line empty access_token")
|
|
}
|
|
return tok.AccessToken, nil
|
|
}
|
|
|
|
func lineProfile(ctx context.Context, accessToken string) (*lineProfileResp, error) {
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.line.me/v2/profile", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
req.Header.Set("Authorization", "Bearer "+accessToken)
|
|
client := &http.Client{Timeout: 10 * time.Second}
|
|
resp, err := client.Do(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
body, _ := io.ReadAll(resp.Body)
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, fmt.Errorf("line profile status %d: %s", resp.StatusCode, string(body))
|
|
}
|
|
var p lineProfileResp
|
|
if err := json.Unmarshal(body, &p); err != nil {
|
|
return nil, err
|
|
}
|
|
if p.UserID == "" {
|
|
return nil, fmt.Errorf("line profile missing userId")
|
|
}
|
|
return &p, nil
|
|
}
|