You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
synctv/internal/provider/providers/logto.go

120 lines
2.9 KiB
Go

package providers
import (
"context"
"encoding/json"
"fmt"
"net/http"
"net/url"
"github.com/synctv-org/synctv/internal/provider"
"github.com/synctv-org/synctv/internal/settings"
"golang.org/x/oauth2"
)
// https://openapi.logto.io/authentication
type logtoProvider struct {
config oauth2.Config
endpoint string
}
func newLogtoProvider() provider.Interface {
return &logtoProvider{
config: oauth2.Config{
Scopes: []string{"profile", "email", "phone", "name", "openid"},
},
}
}
func (p *logtoProvider) Init(opt provider.Oauth2Option) {
p.config.ClientID = opt.ClientID
p.config.ClientSecret = opt.ClientSecret
p.config.RedirectURL = opt.RedirectURL
}
func (p *logtoProvider) NewAuthURL(_ context.Context, state string) (string, error) {
return p.config.AuthCodeURL(state, oauth2.AccessTypeOnline), nil
}
func (p *logtoProvider) GetToken(ctx context.Context, code string) (*oauth2.Token, error) {
return p.config.Exchange(ctx, code)
}
func (p *logtoProvider) RefreshToken(ctx context.Context, token string) (*oauth2.Token, error) {
return p.config.TokenSource(ctx, &oauth2.Token{RefreshToken: token}).Token()
}
func (p *logtoProvider) GetUserInfo(ctx context.Context, code string) (*provider.UserInfo, error) {
tk, err := p.GetToken(ctx, code)
if err != nil {
return nil, err
}
client := p.config.Client(ctx, tk)
req, err := http.NewRequestWithContext(ctx, http.MethodGet, p.endpoint+"/oidc/me", nil)
if err != nil {
return nil, err
}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var ui logtoUserInfo
err = json.NewDecoder(resp.Body).Decode(&ui)
if err != nil {
return nil, err
}
un := ui.Username
if un == "" {
un = ui.Name
}
return &provider.UserInfo{
ProviderUserID: ui.Sub,
Username: un,
}, nil
}
type logtoUserInfo struct {
Sub string `json:"sub"`
Username string `json:"username"`
PrimaryEmail string `json:"primaryEmail"`
PrimaryPhone string `json:"primaryPhone"`
Name string `json:"name"`
Email string `json:"email"`
}
func (p *logtoProvider) RegistSetting(group string) {
settings.NewStringSetting(
group+"_endpoint", "", group,
settings.WithAfterInitString(func(_ settings.StringSetting, s string) {
p.endpoint = s
p.config.Endpoint = oauth2.Endpoint{
AuthURL: s + "/oidc/auth",
TokenURL: s + "/oidc/token",
}
}),
settings.WithBeforeSetString(func(_ settings.StringSetting, s string) (string, error) {
u, err := url.Parse(s)
if err != nil {
return "", err
}
return fmt.Sprintf("%s://%s", u.Scheme, u.Host), nil
}),
settings.WithAfterSetString(func(_ settings.StringSetting, s string) {
p.endpoint = s
p.config.Endpoint = oauth2.Endpoint{
AuthURL: s + "/oidc/auth",
TokenURL: s + "/oidc/token",
}
}),
)
}
func (p *logtoProvider) Provider() provider.OAuth2Provider {
return "logto"
}
func init() {
RegisterProvider(newLogtoProvider())
}