diff --git a/backend/domain/oauth.go b/backend/domain/oauth.go index 76c0357..3735ec9 100644 --- a/backend/domain/oauth.go +++ b/backend/domain/oauth.go @@ -13,7 +13,7 @@ type OAuthConfig struct { ClientID string ClientSecret string RedirectURI string - Scope string + Scopes []string AuthorizeURL string TokenURL string UserInfoURL string diff --git a/backend/go.mod b/backend/go.mod index 85acd2d..ec3b670 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -15,6 +15,7 @@ require ( github.com/rokku-c/go-openai v1.35.7-fix2 github.com/spf13/viper v1.20.1 golang.org/x/crypto v0.39.0 + golang.org/x/oauth2 v0.18.0 golang.org/x/text v0.26.0 ) @@ -33,6 +34,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.27.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -66,5 +68,7 @@ require ( golang.org/x/sys v0.33.0 // indirect golang.org/x/time v0.11.0 // indirect golang.org/x/tools v0.33.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/backend/go.sum b/backend/go.sum index b26474e..d95c4b7 100644 --- a/backend/go.sum +++ b/backend/go.sum @@ -68,7 +68,12 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs= github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= @@ -193,6 +198,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= +golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI= +golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -221,6 +228,7 @@ golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= @@ -238,6 +246,13 @@ golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc= golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/backend/internal/user/usecase/user.go b/backend/internal/user/usecase/user.go index a39c8f8..9ef80c6 100644 --- a/backend/internal/user/usecase/user.go +++ b/backend/internal/user/usecase/user.go @@ -6,7 +6,6 @@ import ( "fmt" "log/slog" "net/url" - "strings" "time" "github.com/google/uuid" @@ -390,7 +389,7 @@ func (u *UserUsecase) OAuthSignUpOrIn(ctx context.Context, req *domain.OAuthSign cfg.ClientID = setting.CustomOauth.ClientID cfg.ClientSecret = setting.CustomOauth.ClientSecret cfg.AuthorizeURL = setting.CustomOauth.AuthorizeURL - cfg.Scope = strings.Join(setting.CustomOauth.Scopes, " ") + cfg.Scopes = setting.CustomOauth.Scopes cfg.TokenURL = setting.CustomOauth.AccessTokenURL cfg.UserInfoURL = setting.CustomOauth.UserInfoURL cfg.IDField = setting.CustomOauth.IDField @@ -457,8 +456,9 @@ func (u *UserUsecase) FetchUserInfo(ctx context.Context, req *domain.OAuthCallba return nil, err } cfg := domain.OAuthConfig{ - Debug: u.cfg.Debug, - Platform: session.Platform, + Debug: u.cfg.Debug, + Platform: session.Platform, + RedirectURI: fmt.Sprintf("%s/api/v1/user/oauth/callback", u.cfg.BaseUrl), } switch session.Platform { @@ -475,7 +475,7 @@ func (u *UserUsecase) FetchUserInfo(ctx context.Context, req *domain.OAuthCallba cfg.ClientID = setting.CustomOauth.ClientID cfg.ClientSecret = setting.CustomOauth.ClientSecret cfg.AuthorizeURL = setting.CustomOauth.AuthorizeURL - cfg.Scope = strings.Join(setting.CustomOauth.Scopes, " ") + cfg.Scopes = setting.CustomOauth.Scopes cfg.TokenURL = setting.CustomOauth.AccessTokenURL cfg.UserInfoURL = setting.CustomOauth.UserInfoURL cfg.IDField = setting.CustomOauth.IDField diff --git a/backend/pkg/oauth/custom.go b/backend/pkg/oauth/custom.go index 229e404..37d5aef 100644 --- a/backend/pkg/oauth/custom.go +++ b/backend/pkg/oauth/custom.go @@ -1,24 +1,35 @@ package oauth import ( + "context" + "encoding/json" "fmt" - "net/url" - "strings" - "time" + "io" "github.com/google/uuid" + "golang.org/x/oauth2" "github.com/chaitin/MonkeyCode/backend/domain" - "github.com/chaitin/MonkeyCode/backend/pkg/request" ) type CustomOAuth struct { - cfg domain.OAuthConfig + cfg domain.OAuthConfig + oauth *oauth2.Config } func NewCustomOAuth(config domain.OAuthConfig) domain.OAuther { c := &CustomOAuth{ cfg: config, + oauth: &oauth2.Config{ + ClientID: config.ClientID, + ClientSecret: config.ClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: config.AuthorizeURL, + TokenURL: config.TokenURL, + }, + RedirectURL: config.RedirectURI, + Scopes: config.Scopes, + }, } return c @@ -27,17 +38,13 @@ func NewCustomOAuth(config domain.OAuthConfig) domain.OAuther { // GetAuthorizeURL implements domain.OAuther. func (c *CustomOAuth) GetAuthorizeURL() (string, string) { state := uuid.NewString() - url := fmt.Sprintf("%s?response_type=code&client_id=%s&state=%s&redirect_uri=%s", c.cfg.AuthorizeURL, c.cfg.ClientID, state, c.cfg.RedirectURI) + url := c.oauth.AuthCodeURL(state) return state, url } // GetUserInfo implements domain.OAuther. func (c *CustomOAuth) GetUserInfo(code string) (*domain.OAuthUserInfo, error) { - accessToken, err := c.getAccessToken(code) - if err != nil { - return nil, err - } - info, err := c.getUserInfo(accessToken) + info, err := c.getUserInfo(code) if err != nil { return nil, err } @@ -48,49 +55,29 @@ func (c *CustomOAuth) GetUserInfo(code string) (*domain.OAuthUserInfo, error) { }, nil } -func (c *CustomOAuth) getAccessToken(code string) (string, error) { - u, err := url.Parse(c.cfg.TokenURL) +type UserInfo map[string]any + +func (c *CustomOAuth) getUserInfo(code string) (UserInfo, error) { + token, err := c.oauth.Exchange(context.Background(), code) if err != nil { - return "", fmt.Errorf("[CustomOAuth] 无效的Token URL: %w", err) - } - client := request.NewClient(u.Scheme, u.Host, 30*time.Second) - client.SetDebug(c.cfg.Debug) - req := domain.GetAccessTokenReq{ - GrantType: "authorization_code", - Code: code, - RedirectURL: c.cfg.RedirectURI, - ClientID: c.cfg.ClientID, - ClientSecret: c.cfg.ClientSecret, + return nil, err } - resp, err := request.Post[domain.OAuthAccessToken](client, u.Path, req, request.WithHeader(request.Header{ - "Accept": "application/json", - })) + client := c.oauth.Client(context.Background(), token) + res, err := client.Get(c.cfg.UserInfoURL) if err != nil { - return "", fmt.Errorf("[CustomOAuth] 获取access token失败: %w", err) + return nil, err } - return resp.AccessToken, nil -} - -type UserInfo map[string]any + defer res.Body.Close() -func (c *CustomOAuth) getUserInfo(accessToken string) (UserInfo, error) { - u, err := url.Parse(c.cfg.UserInfoURL) + buf, err := io.ReadAll(res.Body) if err != nil { - return nil, fmt.Errorf("[CustomOAuth] 无效的UseInfo URL: %w", err) - } - client := request.NewClient(u.Scheme, u.Host, 30*time.Second) - client.SetDebug(c.cfg.Debug) - h := request.Header{ - "Authorization": fmt.Sprintf("Bearer %s", accessToken), - } - if strings.Contains(c.cfg.UserInfoURL, "github") { - h["Accept"] = "application/vnd.github.v3+json" + return nil, err } - resp, err := request.Get[UserInfo](client, u.Path, request.WithHeader(h)) + var userInfo UserInfo + err = json.Unmarshal(buf, &userInfo) if err != nil { - return nil, fmt.Errorf("[CustomOAuth] 获取用户信息失败: %w", err) + return nil, err } - - return *resp, nil + return userInfo, nil }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: