thread-master/apps/backend/internal/module/member/usecase/oauth_providers.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
}