Opt: oauth2 provider

pull/21/head
zijiren233 2 years ago
parent 83843d3c3c
commit d3ced2c376

@ -5,13 +5,20 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/hashicorp/go-hclog"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/synctv-org/synctv/cmd/flags"
"github.com/synctv-org/synctv/internal/conf" "github.com/synctv-org/synctv/internal/conf"
"github.com/synctv-org/synctv/internal/provider" "github.com/synctv-org/synctv/internal/provider"
"github.com/synctv-org/synctv/utils" "github.com/synctv-org/synctv/utils"
) )
func InitProvider(ctx context.Context) error { func InitProvider(ctx context.Context) error {
logOur := log.StandardLogger().Writer()
logLevle := hclog.Info
if flags.Dev {
logLevle = hclog.Debug
}
for _, op := range conf.Conf.OAuth2.Plugins { for _, op := range conf.Conf.OAuth2.Plugins {
utils.OptFilePath(&op.PluginFile) utils.OptFilePath(&op.PluginFile)
log.Infof("load oauth2 plugin: %s", op.PluginFile) log.Infof("load oauth2 plugin: %s", op.PluginFile)
@ -20,7 +27,12 @@ func InitProvider(ctx context.Context) error {
log.Errorf("create plugin dir: %s failed: %s", filepath.Dir(op.PluginFile), err) log.Errorf("create plugin dir: %s failed: %s", filepath.Dir(op.PluginFile), err)
return err return err
} }
err = provider.InitProviderPlugins(op.PluginFile, op.Arges...) err = provider.InitProviderPlugins(op.PluginFile, op.Arges, hclog.New(&hclog.LoggerOptions{
Name: op.PluginFile,
Level: logLevle,
Output: logOur,
Color: hclog.ForceColor,
}))
if err != nil { if err != nil {
log.Errorf("load oauth2 plugin: %s failed: %s", op.PluginFile, err) log.Errorf("load oauth2 plugin: %s failed: %s", op.PluginFile, err)
return err return err

@ -1,6 +1,9 @@
package conf package conf
import "github.com/synctv-org/synctv/internal/provider" import (
"github.com/synctv-org/synctv/internal/provider"
"github.com/synctv-org/synctv/internal/provider/providers"
)
type OAuth2Config struct { type OAuth2Config struct {
Providers map[provider.OAuth2Provider]OAuth2ProviderConfig `yaml:"providers"` Providers map[provider.OAuth2Provider]OAuth2ProviderConfig `yaml:"providers"`
@ -21,7 +24,7 @@ type OAuth2ProviderConfig struct {
func DefaultOAuth2Config() OAuth2Config { func DefaultOAuth2Config() OAuth2Config {
return OAuth2Config{ return OAuth2Config{
Providers: map[provider.OAuth2Provider]OAuth2ProviderConfig{ Providers: map[provider.OAuth2Provider]OAuth2ProviderConfig{
(&provider.GithubProvider{}).Provider(): { (&providers.GithubProvider{}).Provider(): {
ClientID: "", ClientID: "",
ClientSecret: "", ClientSecret: "",
RedirectURL: "", RedirectURL: "",

@ -0,0 +1,68 @@
package provider
import (
"context"
"time"
providerpb "github.com/synctv-org/synctv/proto/provider"
"golang.org/x/oauth2"
)
type GRPCClient struct{ client providerpb.Oauth2PluginClient }
var _ ProviderInterface = (*GRPCClient)(nil)
func (c *GRPCClient) Init(o Oauth2Option) {
c.client.Init(context.Background(), &providerpb.InitReq{
ClientId: o.ClientID,
ClientSecret: o.ClientSecret,
RedirectUrl: o.RedirectURL,
})
}
func (c *GRPCClient) Provider() OAuth2Provider {
resp, err := c.client.Provider(context.Background(), &providerpb.Enpty{})
if err != nil {
return ""
}
return OAuth2Provider(resp.Name)
}
func (c *GRPCClient) NewAuthURL(state string) string {
resp, err := c.client.NewAuthURL(context.Background(), &providerpb.NewAuthURLReq{State: state})
if err != nil {
return ""
}
return resp.Url
}
func (c *GRPCClient) GetToken(ctx context.Context, code string) (*oauth2.Token, error) {
resp, err := c.client.GetToken(ctx, &providerpb.GetTokenReq{Code: code})
if err != nil {
return nil, err
}
return &oauth2.Token{
AccessToken: resp.AccessToken,
TokenType: resp.TokenType,
RefreshToken: resp.RefreshToken,
Expiry: time.Unix(resp.Expiry, 0),
}, nil
}
func (c *GRPCClient) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) {
resp, err := c.client.GetUserInfo(ctx, &providerpb.GetUserInfoReq{
Token: &providerpb.Token{
AccessToken: tk.AccessToken,
TokenType: tk.TokenType,
RefreshToken: tk.RefreshToken,
Expiry: tk.Expiry.Unix(),
},
})
if err != nil {
return nil, err
}
return &UserInfo{
Username: resp.Username,
ProviderUserID: uint(resp.ProviderUserId),
}, nil
}

@ -4,148 +4,34 @@ import (
"context" "context"
"fmt" "fmt"
"os/exec" "os/exec"
"time"
"github.com/hashicorp/go-hclog" "github.com/hashicorp/go-hclog"
plugin "github.com/hashicorp/go-plugin" "github.com/hashicorp/go-plugin"
log "github.com/sirupsen/logrus"
sysnotify "github.com/synctv-org/synctv/internal/sysNotify" sysnotify "github.com/synctv-org/synctv/internal/sysNotify"
providerpb "github.com/synctv-org/synctv/proto/provider" providerpb "github.com/synctv-org/synctv/proto/provider"
"golang.org/x/oauth2"
"google.golang.org/grpc" "google.golang.org/grpc"
) )
type ProviderPlugin struct { func InitProviderPlugins(name string, arg []string, Logger hclog.Logger) error {
plugin.Plugin client := NewProviderPlugin(name, arg, Logger)
Impl ProviderInterface sysnotify.RegisterSysNotifyTask(0, sysnotify.NewSysNotifyTask("plugin", sysnotify.NotifyTypeEXIT, func() error {
} client.Kill()
return nil
func (p *ProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error { }))
providerpb.RegisterOauth2PluginServer(s, &GRPCServer{Impl: p.Impl}) c, err := client.Client()
return nil
}
func (p *ProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return &GRPCClient{client: providerpb.NewOauth2PluginClient(c)}, nil
}
type GRPCServer struct {
providerpb.UnimplementedOauth2PluginServer
Impl ProviderInterface
}
func (s *GRPCServer) Init(ctx context.Context, req *providerpb.InitReq) (*providerpb.Enpty, error) {
s.Impl.Init(Oauth2Option{
ClientID: req.ClientId,
ClientSecret: req.ClientSecret,
RedirectURL: req.RedirectUrl,
})
return &providerpb.Enpty{}, nil
}
func (s *GRPCServer) Provider(ctx context.Context, req *providerpb.Enpty) (*providerpb.ProviderResp, error) {
return &providerpb.ProviderResp{Name: string(s.Impl.Provider())}, nil
}
func (s *GRPCServer) NewAuthURL(ctx context.Context, req *providerpb.NewAuthURLReq) (*providerpb.NewAuthURLResp, error) {
return &providerpb.NewAuthURLResp{Url: s.Impl.NewAuthURL(req.State)}, nil
}
func (s *GRPCServer) GetToken(ctx context.Context, req *providerpb.GetTokenReq) (*providerpb.Token, error) {
token, err := s.Impl.GetToken(ctx, req.Code)
if err != nil {
return nil, err
}
return &providerpb.Token{
AccessToken: token.AccessToken,
TokenType: token.TokenType,
RefreshToken: token.RefreshToken,
Expiry: token.Expiry.Unix(),
}, nil
}
func (s *GRPCServer) GetUserInfo(ctx context.Context, req *providerpb.GetUserInfoReq) (*providerpb.GetUserInfoResp, error) {
userInfo, err := s.Impl.GetUserInfo(ctx, &oauth2.Token{
AccessToken: req.Token.AccessToken,
TokenType: req.Token.TokenType,
Expiry: time.Unix(req.Token.Expiry, 0),
RefreshToken: req.Token.RefreshToken,
})
if err != nil {
return nil, err
}
resp := &providerpb.GetUserInfoResp{
Username: userInfo.Username,
ProviderUserId: uint64(userInfo.ProviderUserID),
}
if userInfo.TokenRefreshed != nil {
resp.TokenRefreshed = &providerpb.Token{
AccessToken: userInfo.TokenRefreshed.Token.AccessToken,
TokenType: userInfo.TokenRefreshed.Token.TokenType,
RefreshToken: userInfo.TokenRefreshed.Token.RefreshToken,
Expiry: userInfo.TokenRefreshed.Token.Expiry.Unix(),
}
}
return resp, nil
}
type GRPCClient struct{ client providerpb.Oauth2PluginClient }
var _ ProviderInterface = (*GRPCClient)(nil)
func (c *GRPCClient) Init(o Oauth2Option) {
c.client.Init(context.Background(), &providerpb.InitReq{
ClientId: o.ClientID,
ClientSecret: o.ClientSecret,
RedirectUrl: o.RedirectURL,
})
}
func (c *GRPCClient) Provider() OAuth2Provider {
resp, err := c.client.Provider(context.Background(), &providerpb.Enpty{})
if err != nil {
return ""
}
return OAuth2Provider(resp.Name)
}
func (c *GRPCClient) NewAuthURL(state string) string {
resp, err := c.client.NewAuthURL(context.Background(), &providerpb.NewAuthURLReq{State: state})
if err != nil { if err != nil {
return "" return err
} }
return resp.Url i, err := c.Dispense("Provider")
}
func (c *GRPCClient) GetToken(ctx context.Context, code string) (*oauth2.Token, error) {
resp, err := c.client.GetToken(ctx, &providerpb.GetTokenReq{Code: code})
if err != nil { if err != nil {
return nil, err return err
} }
return &oauth2.Token{ provider, ok := i.(ProviderInterface)
AccessToken: resp.AccessToken, if !ok {
TokenType: resp.TokenType, return fmt.Errorf("%s not implement ProviderInterface", name)
RefreshToken: resp.RefreshToken,
Expiry: time.Unix(resp.Expiry, 0),
}, nil
}
func (c *GRPCClient) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) {
resp, err := c.client.GetUserInfo(ctx, &providerpb.GetUserInfoReq{
Token: &providerpb.Token{
AccessToken: tk.AccessToken,
TokenType: tk.TokenType,
RefreshToken: tk.RefreshToken,
Expiry: tk.Expiry.Unix(),
},
})
if err != nil {
return nil, err
} }
return &UserInfo{ RegisterProvider(provider)
Username: resp.Username, return nil
ProviderUserID: uint(resp.ProviderUserId),
}, nil
} }
var HandshakeConfig = plugin.HandshakeConfig{ var HandshakeConfig = plugin.HandshakeConfig{
@ -158,35 +44,27 @@ var pluginMap = map[string]plugin.Plugin{
"Provider": &ProviderPlugin{}, "Provider": &ProviderPlugin{},
} }
func InitProviderPlugins(name string, arg ...string) error { type ProviderPlugin struct {
client := plugin.NewClient(&plugin.ClientConfig{ plugin.Plugin
Impl ProviderInterface
}
func (p *ProviderPlugin) GRPCServer(broker *plugin.GRPCBroker, s *grpc.Server) error {
providerpb.RegisterOauth2PluginServer(s, &GRPCServer{Impl: p.Impl})
return nil
}
func (p *ProviderPlugin) GRPCClient(ctx context.Context, broker *plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) {
return &GRPCClient{client: providerpb.NewOauth2PluginClient(c)}, nil
}
func NewProviderPlugin(name string, arg []string, Logger hclog.Logger) *plugin.Client {
return plugin.NewClient(&plugin.ClientConfig{
HandshakeConfig: HandshakeConfig, HandshakeConfig: HandshakeConfig,
Plugins: pluginMap, Plugins: pluginMap,
Cmd: exec.Command(name, arg...), Cmd: exec.Command(name, arg...),
AllowedProtocols: []plugin.Protocol{ AllowedProtocols: []plugin.Protocol{
plugin.ProtocolGRPC}, plugin.ProtocolGRPC},
Logger: hclog.New(&hclog.LoggerOptions{ Logger: Logger,
Name: "plugin",
Output: log.StandardLogger().Writer(),
Level: hclog.Debug,
}),
}) })
sysnotify.RegisterSysNotifyTask(0, sysnotify.NewSysNotifyTask("plugin", sysnotify.NotifyTypeEXIT, func() error {
client.Kill()
return nil
}))
c, err := client.Client()
if err != nil {
return err
}
i, err := c.Dispense("Provider")
if err != nil {
return err
}
provider, ok := i.(ProviderInterface)
if !ok {
return fmt.Errorf("%s not implement ProviderInterface", name)
}
registerProvider(provider)
return nil
} }

@ -10,6 +10,21 @@ import (
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
// go build -o gitee ./internal/provider/plugins/gitee.go
//
// mv gitee {data-dir}/plugins/oauth2/gitee
//
// config.yaml:
//
// oauth2:
// providers:
// gitee:
// client_id: xxx
// client_secret: xxx
// redirect_url: xxx
// plugins:
// - plugin_file: plugins/oauth2/gitee
// arges: []
type GiteeProvider struct { type GiteeProvider struct {
config oauth2.Config config oauth2.Config
} }

@ -9,11 +9,6 @@ import (
type OAuth2Provider string type OAuth2Provider string
var (
enabledProviders map[OAuth2Provider]ProviderInterface
allowedProviders = make(map[OAuth2Provider]ProviderInterface)
)
type TokenRefreshed struct { type TokenRefreshed struct {
Refreshed bool Refreshed bool
Token *oauth2.Token Token *oauth2.Token
@ -39,6 +34,11 @@ type ProviderInterface interface {
GetUserInfo(context.Context, *oauth2.Token) (*UserInfo, error) GetUserInfo(context.Context, *oauth2.Token) (*UserInfo, error)
} }
var (
enabledProviders map[OAuth2Provider]ProviderInterface
allowedProviders = make(map[OAuth2Provider]ProviderInterface)
)
func InitProvider(p OAuth2Provider, c Oauth2Option) error { func InitProvider(p OAuth2Provider, c Oauth2Option) error {
pi, ok := allowedProviders[p] pi, ok := allowedProviders[p]
if !ok { if !ok {
@ -52,7 +52,7 @@ func InitProvider(p OAuth2Provider, c Oauth2Option) error {
return nil return nil
} }
func registerProvider(ps ...ProviderInterface) { func RegisterProvider(ps ...ProviderInterface) {
for _, p := range ps { for _, p := range ps {
allowedProviders[p.Provider()] = p allowedProviders[p.Provider()] = p
} }

@ -1,4 +1,4 @@
package provider package providers
import ( import (
"context" "context"
@ -6,6 +6,7 @@ import (
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -14,7 +15,7 @@ type BaiduNetDiskProvider struct {
config oauth2.Config config oauth2.Config
} }
func (p *BaiduNetDiskProvider) Init(c Oauth2Option) { func (p *BaiduNetDiskProvider) Init(c provider.Oauth2Option) {
p.config.Scopes = []string{"basic", "netdisk"} p.config.Scopes = []string{"basic", "netdisk"}
p.config.Endpoint = oauth2.Endpoint{ p.config.Endpoint = oauth2.Endpoint{
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize", AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
@ -25,7 +26,7 @@ func (p *BaiduNetDiskProvider) Init(c Oauth2Option) {
p.config.RedirectURL = c.RedirectURL p.config.RedirectURL = c.RedirectURL
} }
func (p *BaiduNetDiskProvider) Provider() OAuth2Provider { func (p *BaiduNetDiskProvider) Provider() provider.OAuth2Provider {
return "baidu-netdisk" return "baidu-netdisk"
} }
@ -36,7 +37,7 @@ func (p *BaiduNetDiskProvider) NewAuthURL(state string) string {
func (p *BaiduNetDiskProvider) GetToken(ctx context.Context, code string) (*oauth2.Token, error) { func (p *BaiduNetDiskProvider) GetToken(ctx context.Context, code string) (*oauth2.Token, error) {
return p.config.Exchange(ctx, code) return p.config.Exchange(ctx, code)
} }
func (p *BaiduNetDiskProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (p *BaiduNetDiskProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := p.config.Client(ctx, tk) client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo&access_token=%s", tk.AccessToken), nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://pan.baidu.com/rest/2.0/xpan/nas?method=uinfo&access_token=%s", tk.AccessToken), nil)
if err != nil { if err != nil {
@ -55,14 +56,14 @@ func (p *BaiduNetDiskProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token
if ui.Errno != 0 { if ui.Errno != 0 {
return nil, fmt.Errorf("baidu oauth2 get user info error: %s", ui.Errmsg) return nil, fmt.Errorf("baidu oauth2 get user info error: %s", ui.Errmsg)
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.BaiduName, Username: ui.BaiduName,
ProviderUserID: ui.Uk, ProviderUserID: ui.Uk,
}, nil }, nil
} }
func init() { func init() {
registerProvider(new(BaiduNetDiskProvider)) provider.RegisterProvider(new(BaiduNetDiskProvider))
} }
type baiduNetDiskProviderUserInfo struct { type baiduNetDiskProviderUserInfo struct {

@ -1,4 +1,4 @@
package provider package providers
import ( import (
"context" "context"
@ -7,6 +7,7 @@ import (
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"github.com/zijiren233/stream" "github.com/zijiren233/stream"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -16,7 +17,7 @@ type BaiduProvider struct {
config oauth2.Config config oauth2.Config
} }
func (p *BaiduProvider) Init(c Oauth2Option) { func (p *BaiduProvider) Init(c provider.Oauth2Option) {
p.config.Scopes = []string{"basic"} p.config.Scopes = []string{"basic"}
p.config.Endpoint = oauth2.Endpoint{ p.config.Endpoint = oauth2.Endpoint{
AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize", AuthURL: "https://openapi.baidu.com/oauth/2.0/authorize",
@ -27,7 +28,7 @@ func (p *BaiduProvider) Init(c Oauth2Option) {
p.config.RedirectURL = c.RedirectURL p.config.RedirectURL = c.RedirectURL
} }
func (p *BaiduProvider) Provider() OAuth2Provider { func (p *BaiduProvider) Provider() provider.OAuth2Provider {
return "baidu" return "baidu"
} }
@ -39,7 +40,7 @@ func (p *BaiduProvider) GetToken(ctx context.Context, code string) (*oauth2.Toke
return p.config.Exchange(ctx, code) return p.config.Exchange(ctx, code)
} }
func (p *BaiduProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (p *BaiduProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := p.config.Client(ctx, tk) client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token=%s", tk.AccessToken), nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://openapi.baidu.com/rest/2.0/passport/users/getLoggedInUser?access_token=%s", tk.AccessToken), nil)
if err != nil { if err != nil {
@ -55,14 +56,14 @@ func (p *BaiduProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*Use
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.Uname, Username: ui.Uname,
ProviderUserID: uint(crc32.ChecksumIEEE(stream.StringToBytes(ui.Openid))), ProviderUserID: uint(crc32.ChecksumIEEE(stream.StringToBytes(ui.Openid))),
}, nil }, nil
} }
func init() { func init() {
registerProvider(new(BaiduProvider)) provider.RegisterProvider(new(BaiduProvider))
} }
type baiduProviderUserInfo struct { type baiduProviderUserInfo struct {

@ -1,10 +1,11 @@
package provider package providers
import ( import (
"context" "context"
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"golang.org/x/oauth2" "golang.org/x/oauth2"
) )
@ -12,7 +13,7 @@ type GiteeProvider struct {
config oauth2.Config config oauth2.Config
} }
func (p *GiteeProvider) Init(c Oauth2Option) { func (p *GiteeProvider) Init(c provider.Oauth2Option) {
p.config.Scopes = []string{"user_info"} p.config.Scopes = []string{"user_info"}
p.config.Endpoint = oauth2.Endpoint{ p.config.Endpoint = oauth2.Endpoint{
AuthURL: "https://gitee.com/oauth/authorize", AuthURL: "https://gitee.com/oauth/authorize",
@ -23,7 +24,7 @@ func (p *GiteeProvider) Init(c Oauth2Option) {
p.config.RedirectURL = c.RedirectURL p.config.RedirectURL = c.RedirectURL
} }
func (p *GiteeProvider) Provider() OAuth2Provider { func (p *GiteeProvider) Provider() provider.OAuth2Provider {
return "gitee" return "gitee"
} }
@ -35,7 +36,7 @@ func (p *GiteeProvider) GetToken(ctx context.Context, code string) (*oauth2.Toke
return p.config.Exchange(ctx, code) return p.config.Exchange(ctx, code)
} }
func (p *GiteeProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (p *GiteeProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := p.config.Client(ctx, tk) client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitee.com/api/v5/user", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitee.com/api/v5/user", nil)
if err != nil { if err != nil {
@ -51,7 +52,7 @@ func (p *GiteeProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*Use
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.Login, Username: ui.Login,
ProviderUserID: ui.ID, ProviderUserID: ui.ID,
}, nil }, nil
@ -63,5 +64,5 @@ type giteeUserInfo struct {
} }
func init() { func init() {
registerProvider(new(GiteeProvider)) provider.RegisterProvider(new(GiteeProvider))
} }

@ -1,10 +1,11 @@
package provider package providers
import ( import (
"context" "context"
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/github" "golang.org/x/oauth2/github"
) )
@ -13,7 +14,7 @@ type GithubProvider struct {
config oauth2.Config config oauth2.Config
} }
func (p *GithubProvider) Init(c Oauth2Option) { func (p *GithubProvider) Init(c provider.Oauth2Option) {
p.config.Scopes = []string{"user"} p.config.Scopes = []string{"user"}
p.config.Endpoint = github.Endpoint p.config.Endpoint = github.Endpoint
p.config.ClientID = c.ClientID p.config.ClientID = c.ClientID
@ -21,7 +22,7 @@ func (p *GithubProvider) Init(c Oauth2Option) {
p.config.RedirectURL = c.RedirectURL p.config.RedirectURL = c.RedirectURL
} }
func (p *GithubProvider) Provider() OAuth2Provider { func (p *GithubProvider) Provider() provider.OAuth2Provider {
return "github" return "github"
} }
@ -33,7 +34,7 @@ func (p *GithubProvider) GetToken(ctx context.Context, code string) (*oauth2.Tok
return p.config.Exchange(ctx, code) return p.config.Exchange(ctx, code)
} }
func (p *GithubProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (p *GithubProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := p.config.Client(ctx, tk) client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.github.com/user", nil)
if err != nil { if err != nil {
@ -49,7 +50,7 @@ func (p *GithubProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*Us
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.Login, Username: ui.Login,
ProviderUserID: ui.ID, ProviderUserID: ui.ID,
}, nil }, nil
@ -61,5 +62,5 @@ type githubUserInfo struct {
} }
func init() { func init() {
registerProvider(new(GithubProvider)) provider.RegisterProvider(new(GithubProvider))
} }

@ -1,9 +1,10 @@
package provider package providers
import ( import (
"context" "context"
"net/http" "net/http"
"github.com/synctv-org/synctv/internal/provider"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/gitlab" "golang.org/x/oauth2/gitlab"
) )
@ -12,7 +13,7 @@ type GitlabProvider struct {
config oauth2.Config config oauth2.Config
} }
func (g *GitlabProvider) Init(c Oauth2Option) { func (g *GitlabProvider) Init(c provider.Oauth2Option) {
g.config.Scopes = []string{"read_user"} g.config.Scopes = []string{"read_user"}
g.config.Endpoint = gitlab.Endpoint g.config.Endpoint = gitlab.Endpoint
g.config.ClientID = c.ClientID g.config.ClientID = c.ClientID
@ -20,7 +21,7 @@ func (g *GitlabProvider) Init(c Oauth2Option) {
g.config.RedirectURL = c.RedirectURL g.config.RedirectURL = c.RedirectURL
} }
func (g *GitlabProvider) Provider() OAuth2Provider { func (g *GitlabProvider) Provider() provider.OAuth2Provider {
return "gitlab" return "gitlab"
} }
@ -32,7 +33,7 @@ func (g *GitlabProvider) GetToken(ctx context.Context, code string) (*oauth2.Tok
return g.config.Exchange(ctx, code) return g.config.Exchange(ctx, code)
} }
func (g *GitlabProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (g *GitlabProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := g.config.Client(ctx, tk) client := g.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitlab.com/api/v4/user", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://gitlab.com/api/v4/user", nil)
if err != nil { if err != nil {
@ -43,9 +44,9 @@ func (g *GitlabProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*Us
return nil, err return nil, err
} }
defer resp.Body.Close() defer resp.Body.Close()
return nil, FormatErrNotImplemented("gitlab") return nil, provider.FormatErrNotImplemented("gitlab")
} }
func init() { func init() {
registerProvider(new(GitlabProvider)) provider.RegisterProvider(new(GitlabProvider))
} }

@ -1,10 +1,11 @@
package provider package providers
import ( import (
"context" "context"
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/google" "golang.org/x/oauth2/google"
) )
@ -13,7 +14,7 @@ type GoogleProvider struct {
config oauth2.Config config oauth2.Config
} }
func (g *GoogleProvider) Init(c Oauth2Option) { func (g *GoogleProvider) Init(c provider.Oauth2Option) {
g.config.Scopes = []string{"profile"} g.config.Scopes = []string{"profile"}
g.config.Endpoint = google.Endpoint g.config.Endpoint = google.Endpoint
g.config.ClientID = c.ClientID g.config.ClientID = c.ClientID
@ -21,7 +22,7 @@ func (g *GoogleProvider) Init(c Oauth2Option) {
g.config.RedirectURL = c.RedirectURL g.config.RedirectURL = c.RedirectURL
} }
func (g *GoogleProvider) Provider() OAuth2Provider { func (g *GoogleProvider) Provider() provider.OAuth2Provider {
return "google" return "google"
} }
@ -33,7 +34,7 @@ func (g *GoogleProvider) GetToken(ctx context.Context, code string) (*oauth2.Tok
return g.config.Exchange(ctx, code) return g.config.Exchange(ctx, code)
} }
func (g *GoogleProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (g *GoogleProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := g.config.Client(ctx, tk) client := g.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://www.googleapis.com/oauth2/v2/userinfo", nil)
if err != nil { if err != nil {
@ -49,14 +50,14 @@ func (g *GoogleProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*Us
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.Name, Username: ui.Name,
ProviderUserID: ui.ID, ProviderUserID: ui.ID,
}, nil }, nil
} }
func init() { func init() {
registerProvider(new(GoogleProvider)) provider.RegisterProvider(new(GoogleProvider))
} }
type googleUserInfo struct { type googleUserInfo struct {

@ -1,4 +1,4 @@
package provider package providers
import ( import (
"context" "context"
@ -6,6 +6,7 @@ import (
"net/http" "net/http"
json "github.com/json-iterator/go" json "github.com/json-iterator/go"
"github.com/synctv-org/synctv/internal/provider"
"github.com/zijiren233/stream" "github.com/zijiren233/stream"
"golang.org/x/oauth2" "golang.org/x/oauth2"
"golang.org/x/oauth2/microsoft" "golang.org/x/oauth2/microsoft"
@ -15,7 +16,7 @@ type MicrosoftProvider struct {
config oauth2.Config config oauth2.Config
} }
func (p *MicrosoftProvider) Init(c Oauth2Option) { func (p *MicrosoftProvider) Init(c provider.Oauth2Option) {
p.config.Scopes = []string{"user.read"} p.config.Scopes = []string{"user.read"}
p.config.Endpoint = microsoft.LiveConnectEndpoint p.config.Endpoint = microsoft.LiveConnectEndpoint
p.config.ClientID = c.ClientID p.config.ClientID = c.ClientID
@ -23,7 +24,7 @@ func (p *MicrosoftProvider) Init(c Oauth2Option) {
p.config.RedirectURL = c.RedirectURL p.config.RedirectURL = c.RedirectURL
} }
func (p *MicrosoftProvider) Provider() OAuth2Provider { func (p *MicrosoftProvider) Provider() provider.OAuth2Provider {
return "microsoft" return "microsoft"
} }
@ -35,7 +36,7 @@ func (p *MicrosoftProvider) GetToken(ctx context.Context, code string) (*oauth2.
return p.config.Exchange(ctx, code) return p.config.Exchange(ctx, code)
} }
func (p *MicrosoftProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*UserInfo, error) { func (p *MicrosoftProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (*provider.UserInfo, error) {
client := p.config.Client(ctx, tk) client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://graph.microsoft.com/v1.0/me", nil) req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://graph.microsoft.com/v1.0/me", nil)
if err != nil { if err != nil {
@ -51,7 +52,7 @@ func (p *MicrosoftProvider) GetUserInfo(ctx context.Context, tk *oauth2.Token) (
if err != nil { if err != nil {
return nil, err return nil, err
} }
return &UserInfo{ return &provider.UserInfo{
Username: ui.DisplayName, Username: ui.DisplayName,
ProviderUserID: uint(crc32.ChecksumIEEE(stream.StringToBytes(ui.ID))), ProviderUserID: uint(crc32.ChecksumIEEE(stream.StringToBytes(ui.ID))),
}, nil }, nil
@ -63,5 +64,5 @@ type microsoftUserInfo struct {
} }
func init() { func init() {
registerProvider(new(MicrosoftProvider)) provider.RegisterProvider(new(MicrosoftProvider))
} }

@ -0,0 +1,69 @@
package provider
import (
"context"
"time"
providerpb "github.com/synctv-org/synctv/proto/provider"
"golang.org/x/oauth2"
)
type GRPCServer struct {
providerpb.UnimplementedOauth2PluginServer
Impl ProviderInterface
}
func (s *GRPCServer) Init(ctx context.Context, req *providerpb.InitReq) (*providerpb.Enpty, error) {
s.Impl.Init(Oauth2Option{
ClientID: req.ClientId,
ClientSecret: req.ClientSecret,
RedirectURL: req.RedirectUrl,
})
return &providerpb.Enpty{}, nil
}
func (s *GRPCServer) Provider(ctx context.Context, req *providerpb.Enpty) (*providerpb.ProviderResp, error) {
return &providerpb.ProviderResp{Name: string(s.Impl.Provider())}, nil
}
func (s *GRPCServer) NewAuthURL(ctx context.Context, req *providerpb.NewAuthURLReq) (*providerpb.NewAuthURLResp, error) {
return &providerpb.NewAuthURLResp{Url: s.Impl.NewAuthURL(req.State)}, nil
}
func (s *GRPCServer) GetToken(ctx context.Context, req *providerpb.GetTokenReq) (*providerpb.Token, error) {
token, err := s.Impl.GetToken(ctx, req.Code)
if err != nil {
return nil, err
}
return &providerpb.Token{
AccessToken: token.AccessToken,
TokenType: token.TokenType,
RefreshToken: token.RefreshToken,
Expiry: token.Expiry.Unix(),
}, nil
}
func (s *GRPCServer) GetUserInfo(ctx context.Context, req *providerpb.GetUserInfoReq) (*providerpb.GetUserInfoResp, error) {
userInfo, err := s.Impl.GetUserInfo(ctx, &oauth2.Token{
AccessToken: req.Token.AccessToken,
TokenType: req.Token.TokenType,
Expiry: time.Unix(req.Token.Expiry, 0),
RefreshToken: req.Token.RefreshToken,
})
if err != nil {
return nil, err
}
resp := &providerpb.GetUserInfoResp{
Username: userInfo.Username,
ProviderUserId: uint64(userInfo.ProviderUserID),
}
if userInfo.TokenRefreshed != nil {
resp.TokenRefreshed = &providerpb.Token{
AccessToken: userInfo.TokenRefreshed.Token.AccessToken,
TokenType: userInfo.TokenRefreshed.Token.TokenType,
RefreshToken: userInfo.TokenRefreshed.Token.RefreshToken,
Expiry: userInfo.TokenRefreshed.Token.Expiry.Unix(),
}
}
return resp, nil
}
Loading…
Cancel
Save