From f16a9cd0dc4af6c6c171d7615328ccbef620200a Mon Sep 17 00:00:00 2001 From: abelanger5 Date: Thu, 11 Apr 2024 12:32:12 -0700 Subject: [PATCH] feat: add github sso to dashboard (#373) --- api-contracts/openapi/openapi.yaml | 4 + api-contracts/openapi/paths/user/user.yaml | 38 +- .../handlers/github-app/oauth_callback.go | 6 +- .../server/handlers/github-app/oauth_start.go | 6 +- api/v1/server/handlers/metadata/get.go | 4 + api/v1/server/handlers/users/create.go | 11 +- .../handlers/users/github_oauth_callback.go | 186 +++++++ .../handlers/users/github_oauth_start.go | 25 + api/v1/server/handlers/users/service.go | 18 + api/v1/server/oas/gen/openapi.gen.go | 376 +++++++++----- frontend/app/src/lib/api/generated/Api.ts | 34 +- frontend/app/src/pages/auth/login/index.tsx | 63 ++- .../app/src/pages/auth/register/index.tsx | 38 +- .../pages/contributing/github-app-setup.mdx | 2 +- .../pages/self-hosting/github-app-setup.mdx | 2 +- internal/auth/oauth/configs.go | 15 + internal/config/loader/loader.go | 17 + internal/config/server/server.go | 16 + .../vcs/github/github_app_oauth.go | 2 +- internal/repository/user.go | 2 +- pkg/client/rest/gen.go | 182 +++++++ .../hatchet_sdk/clients/rest/api/user_api.py | 476 +++++++++++++++++- 22 files changed, 1328 insertions(+), 195 deletions(-) create mode 100644 api/v1/server/handlers/users/github_oauth_callback.go create mode 100644 api/v1/server/handlers/users/github_oauth_start.go diff --git a/api-contracts/openapi/openapi.yaml b/api-contracts/openapi/openapi.yaml index 01a4c3aa9..dcf30323c 100644 --- a/api-contracts/openapi/openapi.yaml +++ b/api-contracts/openapi/openapi.yaml @@ -38,6 +38,10 @@ paths: $ref: "./paths/user/user.yaml#/oauth-start-github" /api/v1/users/github/callback: $ref: "./paths/user/user.yaml#/oauth-callback-github" + /api/v1/users/github-app/start: + $ref: "./paths/user/user.yaml#/oauth-start-github-app" + /api/v1/users/github-app/callback: + $ref: "./paths/user/user.yaml#/oauth-callback-github-app" /api/v1/github/webhook: $ref: "./paths/github-app/github-app.yaml#/globalWebhook" /api/v1/github/webhook/{webhook}: diff --git a/api-contracts/openapi/paths/user/user.yaml b/api-contracts/openapi/paths/user/user.yaml index 73536b6e4..83980f1a9 100644 --- a/api-contracts/openapi/paths/user/user.yaml +++ b/api-contracts/openapi/paths/user/user.yaml @@ -188,10 +188,7 @@ oauth-start-github: location: schema: type: string - # Note that the security scheme requires cookies, because this endpoint is for linking - # a GitHub account to an existing user account. - security: - - cookieAuth: [] + security: [] summary: Start OAuth flow tags: - User @@ -199,6 +196,39 @@ oauth-callback-github: get: description: Completes the OAuth flow operationId: user:update:github-oauth-callback + responses: + "302": + description: Successfully completed the OAuth flow + headers: + location: + schema: + type: string + security: [] + summary: Complete OAuth flow + tags: + - User +oauth-start-github-app: + get: + description: Starts the OAuth flow + operationId: user:update:github-app-oauth-start + responses: + "302": + description: Successfully started the OAuth flow + headers: + location: + schema: + type: string + # Note that the security scheme requires cookies, because this endpoint is for linking + # a GitHub account to an existing user account. + security: + - cookieAuth: [] + summary: Start OAuth flow + tags: + - User +oauth-callback-github-app: + get: + description: Completes the OAuth flow + operationId: user:update:github-app-oauth-callback responses: "302": description: Successfully completed the OAuth flow diff --git a/api/v1/server/handlers/github-app/oauth_callback.go b/api/v1/server/handlers/github-app/oauth_callback.go index 85539d1e5..dde35b9d7 100644 --- a/api/v1/server/handlers/github-app/oauth_callback.go +++ b/api/v1/server/handlers/github-app/oauth_callback.go @@ -14,7 +14,7 @@ import ( ) // Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow. -func (g *GithubAppService) UserUpdateGithubOauthCallback(ctx echo.Context, _ gen.UserUpdateGithubOauthCallbackRequestObject) (gen.UserUpdateGithubOauthCallbackResponseObject, error) { +func (g *GithubAppService) UserUpdateGithubAppOauthCallback(ctx echo.Context, _ gen.UserUpdateGithubAppOauthCallbackRequestObject) (gen.UserUpdateGithubAppOauthCallbackResponseObject, error) { user := ctx.Get("user").(*db.UserModel) ghApp, err := GetGithubAppConfig(g.config) @@ -86,8 +86,8 @@ func (g *GithubAppService) UserUpdateGithubOauthCallback(ctx echo.Context, _ gen url = "/" } - return gen.UserUpdateGithubOauthCallback302Response{ - Headers: gen.UserUpdateGithubOauthCallback302ResponseHeaders{ + return gen.UserUpdateGithubAppOauthCallback302Response{ + Headers: gen.UserUpdateGithubAppOauthCallback302ResponseHeaders{ Location: url, }, }, nil diff --git a/api/v1/server/handlers/github-app/oauth_start.go b/api/v1/server/handlers/github-app/oauth_start.go index 6c5401079..660a67874 100644 --- a/api/v1/server/handlers/github-app/oauth_start.go +++ b/api/v1/server/handlers/github-app/oauth_start.go @@ -9,7 +9,7 @@ import ( ) // Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow. -func (g *GithubAppService) UserUpdateGithubOauthStart(ctx echo.Context, _ gen.UserUpdateGithubOauthStartRequestObject) (gen.UserUpdateGithubOauthStartResponseObject, error) { +func (g *GithubAppService) UserUpdateGithubAppOauthStart(ctx echo.Context, _ gen.UserUpdateGithubAppOauthStartRequestObject) (gen.UserUpdateGithubAppOauthStartResponseObject, error) { ghApp, err := GetGithubAppConfig(g.config) if err != nil { @@ -24,8 +24,8 @@ func (g *GithubAppService) UserUpdateGithubOauthStart(ctx echo.Context, _ gen.Us url := ghApp.AuthCodeURL(state, oauth2.AccessTypeOffline) - return gen.UserUpdateGithubOauthStart302Response{ - Headers: gen.UserUpdateGithubOauthStart302ResponseHeaders{ + return gen.UserUpdateGithubAppOauthStart302Response{ + Headers: gen.UserUpdateGithubAppOauthStart302ResponseHeaders{ Location: url, }, }, nil diff --git a/api/v1/server/handlers/metadata/get.go b/api/v1/server/handlers/metadata/get.go index 75f282fdc..90fd89fa5 100644 --- a/api/v1/server/handlers/metadata/get.go +++ b/api/v1/server/handlers/metadata/get.go @@ -17,6 +17,10 @@ func (u *MetadataService) MetadataGet(ctx echo.Context, request gen.MetadataGetR authTypes = append(authTypes, "google") } + if u.config.Auth.ConfigFile.Github.Enabled { + authTypes = append(authTypes, "github") + } + meta := gen.APIMeta{ Auth: &gen.APIMetaAuth{ Schemes: &authTypes, diff --git a/api/v1/server/handlers/users/create.go b/api/v1/server/handlers/users/create.go index 1d4ad8911..316be375f 100644 --- a/api/v1/server/handlers/users/create.go +++ b/api/v1/server/handlers/users/create.go @@ -2,7 +2,6 @@ package users import ( "errors" - "strings" "github.com/labstack/echo/v4" @@ -24,15 +23,7 @@ func (u *UserService) UserCreate(ctx echo.Context, request gen.UserCreateRequest } // check restricted email group - // parse domain from email - // make sure there's only one @ in the email - if strings.Count(string(request.Body.Email), "@") != 1 { - return nil, errors.New("invalid email") - } - - domain := strings.Split(string(request.Body.Email), "@")[1] - - if err := u.checkUserRestrictions(u.config, domain); err != nil { + if err := u.checkUserRestrictionsForEmail(u.config, string(request.Body.Email)); err != nil { return nil, err } diff --git a/api/v1/server/handlers/users/github_oauth_callback.go b/api/v1/server/handlers/users/github_oauth_callback.go new file mode 100644 index 000000000..54ecc8689 --- /dev/null +++ b/api/v1/server/handlers/users/github_oauth_callback.go @@ -0,0 +1,186 @@ +package users + +import ( + "context" + "errors" + "fmt" + + githubsdk "github.com/google/go-github/v57/github" + "github.com/labstack/echo/v4" + "golang.org/x/oauth2" + + "github.com/hatchet-dev/hatchet/api/v1/server/authn" + "github.com/hatchet-dev/hatchet/api/v1/server/oas/gen" + "github.com/hatchet-dev/hatchet/internal/config/server" + "github.com/hatchet-dev/hatchet/internal/repository" + "github.com/hatchet-dev/hatchet/internal/repository/prisma/db" +) + +// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow. +func (u *UserService) UserUpdateGithubOauthCallback(ctx echo.Context, _ gen.UserUpdateGithubOauthCallbackRequestObject) (gen.UserUpdateGithubOauthCallbackResponseObject, error) { + isValid, _, err := authn.NewSessionHelpers(u.config).ValidateOAuthState(ctx, "github") + + if err != nil || !isValid { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Could not log in. Please try again and make sure cookies are enabled.") + } + + token, err := u.config.Auth.GithubOAuthConfig.Exchange(context.Background(), ctx.Request().URL.Query().Get("code")) + + if err != nil { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Forbidden") + } + + if !token.Valid() { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, fmt.Errorf("invalid token"), "Forbidden") + } + + user, err := u.upsertGithubUserFromToken(u.config, token) + + if err != nil { + if errors.Is(err, ErrGithubNotVerified) { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Please verify your email on Github.") + } + + if errors.Is(err, ErrGithubNoEmail) { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Github user must have an email.") + } + + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Internal error.") + } + + err = authn.NewSessionHelpers(u.config).SaveAuthenticated(ctx, user) + + if err != nil { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Internal error.") + } + + return gen.UserUpdateGithubOauthCallback302Response{ + Headers: gen.UserUpdateGithubOauthCallback302ResponseHeaders{ + Location: u.config.Runtime.ServerURL, + }, + }, nil +} + +func (u *UserService) upsertGithubUserFromToken(config *server.ServerConfig, tok *oauth2.Token) (*db.UserModel, error) { + gInfo, err := u.getGithubEmailFromToken(tok) + + if err != nil { + return nil, err + } + + if err := u.checkUserRestrictionsForEmail(config, gInfo.Email); err != nil { + return nil, err + } + + expiresAt := tok.Expiry + + // use the encryption service to encrypt the access and refresh token + accessTokenEncrypted, err := config.Encryption.Encrypt([]byte(tok.AccessToken), "github_access_token") + + if err != nil { + return nil, fmt.Errorf("failed to encrypt access token: %s", err.Error()) + } + + refreshTokenEncrypted, err := config.Encryption.Encrypt([]byte(tok.RefreshToken), "github_refresh_token") + + if err != nil { + return nil, fmt.Errorf("failed to encrypt refresh token: %s", err.Error()) + } + + oauthOpts := &repository.OAuthOpts{ + Provider: "github", + ProviderUserId: gInfo.ID, + AccessToken: accessTokenEncrypted, + RefreshToken: &refreshTokenEncrypted, + ExpiresAt: &expiresAt, + } + + user, err := u.config.APIRepository.User().GetUserByEmail(gInfo.Email) + + switch err { + case nil: + user, err = u.config.APIRepository.User().UpdateUser(user.ID, &repository.UpdateUserOpts{ + EmailVerified: repository.BoolPtr(gInfo.EmailVerified), + Name: repository.StringPtr(gInfo.Name), + OAuth: oauthOpts, + }) + + if err != nil { + return nil, fmt.Errorf("failed to update user: %s", err.Error()) + } + case db.ErrNotFound: + user, err = u.config.APIRepository.User().CreateUser(&repository.CreateUserOpts{ + Email: gInfo.Email, + EmailVerified: repository.BoolPtr(gInfo.EmailVerified), + Name: repository.StringPtr(gInfo.Name), + OAuth: oauthOpts, + }) + + if err != nil { + return nil, fmt.Errorf("failed to create user: %s", err.Error()) + } + default: + return nil, fmt.Errorf("failed to get user: %s", err.Error()) + } + + return user, nil +} + +var ErrGithubNotVerified = fmt.Errorf("Please verify your email on Github") +var ErrGithubNoEmail = fmt.Errorf("Github user must have an email") + +type githubInfo struct { + Email string + EmailVerified bool + Name string + ID string +} + +func (u *UserService) getGithubEmailFromToken(tok *oauth2.Token) (*githubInfo, error) { + client := githubsdk.NewClient(u.config.Auth.GithubOAuthConfig.Client(context.Background(), tok)) + + user, _, err := client.Users.Get(context.Background(), "") + + if err != nil { + return nil, err + } + + emails, _, err := client.Users.ListEmails(context.Background(), &githubsdk.ListOptions{}) + + if err != nil { + return nil, err + } + + primary := "" + verified := false + + // get the primary email + for _, email := range emails { + if email.GetPrimary() { + primary = email.GetEmail() + verified = email.GetVerified() + break + } + } + + if primary == "" { + return nil, ErrGithubNoEmail + } + + if !verified { + return nil, ErrGithubNotVerified + } + + id := user.GetID() + + if id == 0 { + return nil, fmt.Errorf("Github user has no ID") + } + + return &githubInfo{ + Email: primary, + EmailVerified: verified, + Name: user.GetName(), + ID: fmt.Sprintf("%d", id), + }, nil +} diff --git a/api/v1/server/handlers/users/github_oauth_start.go b/api/v1/server/handlers/users/github_oauth_start.go new file mode 100644 index 000000000..abcc034da --- /dev/null +++ b/api/v1/server/handlers/users/github_oauth_start.go @@ -0,0 +1,25 @@ +package users + +import ( + "github.com/labstack/echo/v4" + + "github.com/hatchet-dev/hatchet/api/v1/server/authn" + "github.com/hatchet-dev/hatchet/api/v1/server/oas/gen" +) + +// Note: we want all errors to redirect, otherwise the user will be greeted with raw JSON in the middle of the login flow. +func (u *UserService) UserUpdateGithubOauthStart(ctx echo.Context, _ gen.UserUpdateGithubOauthStartRequestObject) (gen.UserUpdateGithubOauthStartResponseObject, error) { + state, err := authn.NewSessionHelpers(u.config).SaveOAuthState(ctx, "github") + + if err != nil { + return nil, authn.GetRedirectWithError(ctx, u.config.Logger, err, "Could not get cookie. Please make sure cookies are enabled.") + } + + url := u.config.Auth.GithubOAuthConfig.AuthCodeURL(state) + + return gen.UserUpdateGithubOauthStart302Response{ + Headers: gen.UserUpdateGithubOauthStart302ResponseHeaders{ + Location: url, + }, + }, nil +} diff --git a/api/v1/server/handlers/users/service.go b/api/v1/server/handlers/users/service.go index 5c754ab7a..0f6b8148f 100644 --- a/api/v1/server/handlers/users/service.go +++ b/api/v1/server/handlers/users/service.go @@ -1,7 +1,9 @@ package users import ( + "errors" "fmt" + "strings" "github.com/hatchet-dev/hatchet/internal/config/server" ) @@ -16,6 +18,22 @@ func NewUserService(config *server.ServerConfig) *UserService { } } +func (u *UserService) checkUserRestrictionsForEmail(conf *server.ServerConfig, email string) error { + if len(conf.Auth.ConfigFile.RestrictedEmailDomains) == 0 { + return nil + } + + // parse domain from email + // make sure there's only one @ in the email + if strings.Count(email, "@") != 1 { + return errors.New("invalid email") + } + + domain := strings.Split(email, "@")[1] + + return u.checkUserRestrictions(conf, domain) +} + func (u *UserService) checkUserRestrictions(conf *server.ServerConfig, emailDomain string) error { if len(conf.Auth.ConfigFile.RestrictedEmailDomains) == 0 { return nil diff --git a/api/v1/server/oas/gen/openapi.gen.go b/api/v1/server/oas/gen/openapi.gen.go index a67919947..cfc1e73f3 100644 --- a/api/v1/server/oas/gen/openapi.gen.go +++ b/api/v1/server/oas/gen/openapi.gen.go @@ -1109,6 +1109,12 @@ type ServerInterface interface { // (GET /api/v1/users/current) UserGetCurrent(ctx echo.Context) error // Complete OAuth flow + // (GET /api/v1/users/github-app/callback) + UserUpdateGithubAppOauthCallback(ctx echo.Context) error + // Start OAuth flow + // (GET /api/v1/users/github-app/start) + UserUpdateGithubAppOauthStart(ctx echo.Context) error + // Complete OAuth flow // (GET /api/v1/users/github/callback) UserUpdateGithubOauthCallback(ctx echo.Context) error // Start OAuth flow @@ -2110,12 +2116,32 @@ func (w *ServerInterfaceWrapper) UserGetCurrent(ctx echo.Context) error { return err } -// UserUpdateGithubOauthCallback converts echo context to params. -func (w *ServerInterfaceWrapper) UserUpdateGithubOauthCallback(ctx echo.Context) error { +// UserUpdateGithubAppOauthCallback converts echo context to params. +func (w *ServerInterfaceWrapper) UserUpdateGithubAppOauthCallback(ctx echo.Context) error { var err error ctx.Set(CookieAuthScopes, []string{}) + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UserUpdateGithubAppOauthCallback(ctx) + return err +} + +// UserUpdateGithubAppOauthStart converts echo context to params. +func (w *ServerInterfaceWrapper) UserUpdateGithubAppOauthStart(ctx echo.Context) error { + var err error + + ctx.Set(CookieAuthScopes, []string{}) + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.UserUpdateGithubAppOauthStart(ctx) + return err +} + +// UserUpdateGithubOauthCallback converts echo context to params. +func (w *ServerInterfaceWrapper) UserUpdateGithubOauthCallback(ctx echo.Context) error { + var err error + // Invoke the callback with all the unmarshaled arguments err = w.Handler.UserUpdateGithubOauthCallback(ctx) return err @@ -2125,8 +2151,6 @@ func (w *ServerInterfaceWrapper) UserUpdateGithubOauthCallback(ctx echo.Context) func (w *ServerInterfaceWrapper) UserUpdateGithubOauthStart(ctx echo.Context) error { var err error - ctx.Set(CookieAuthScopes, []string{}) - // Invoke the callback with all the unmarshaled arguments err = w.Handler.UserUpdateGithubOauthStart(ctx) return err @@ -2508,6 +2532,8 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/api/v1/tenants/:tenant/workflows", wrapper.WorkflowList) router.GET(baseURL+"/api/v1/tenants/:tenant/workflows/runs", wrapper.WorkflowRunList) router.GET(baseURL+"/api/v1/users/current", wrapper.UserGetCurrent) + router.GET(baseURL+"/api/v1/users/github-app/callback", wrapper.UserUpdateGithubAppOauthCallback) + router.GET(baseURL+"/api/v1/users/github-app/start", wrapper.UserUpdateGithubAppOauthStart) router.GET(baseURL+"/api/v1/users/github/callback", wrapper.UserUpdateGithubOauthCallback) router.GET(baseURL+"/api/v1/users/github/start", wrapper.UserUpdateGithubOauthStart) router.GET(baseURL+"/api/v1/users/google/callback", wrapper.UserUpdateGoogleOauthCallback) @@ -3974,6 +4000,48 @@ func (response UserGetCurrent405JSONResponse) VisitUserGetCurrentResponse(w http return json.NewEncoder(w).Encode(response) } +type UserUpdateGithubAppOauthCallbackRequestObject struct { +} + +type UserUpdateGithubAppOauthCallbackResponseObject interface { + VisitUserUpdateGithubAppOauthCallbackResponse(w http.ResponseWriter) error +} + +type UserUpdateGithubAppOauthCallback302ResponseHeaders struct { + Location string +} + +type UserUpdateGithubAppOauthCallback302Response struct { + Headers UserUpdateGithubAppOauthCallback302ResponseHeaders +} + +func (response UserUpdateGithubAppOauthCallback302Response) VisitUserUpdateGithubAppOauthCallbackResponse(w http.ResponseWriter) error { + w.Header().Set("location", fmt.Sprint(response.Headers.Location)) + w.WriteHeader(302) + return nil +} + +type UserUpdateGithubAppOauthStartRequestObject struct { +} + +type UserUpdateGithubAppOauthStartResponseObject interface { + VisitUserUpdateGithubAppOauthStartResponse(w http.ResponseWriter) error +} + +type UserUpdateGithubAppOauthStart302ResponseHeaders struct { + Location string +} + +type UserUpdateGithubAppOauthStart302Response struct { + Headers UserUpdateGithubAppOauthStart302ResponseHeaders +} + +func (response UserUpdateGithubAppOauthStart302Response) VisitUserUpdateGithubAppOauthStartResponse(w http.ResponseWriter) error { + w.Header().Set("location", fmt.Sprint(response.Headers.Location)) + w.WriteHeader(302) + return nil +} + type UserUpdateGithubOauthCallbackRequestObject struct { } @@ -4787,6 +4855,10 @@ type StrictServerInterface interface { UserGetCurrent(ctx echo.Context, request UserGetCurrentRequestObject) (UserGetCurrentResponseObject, error) + UserUpdateGithubAppOauthCallback(ctx echo.Context, request UserUpdateGithubAppOauthCallbackRequestObject) (UserUpdateGithubAppOauthCallbackResponseObject, error) + + UserUpdateGithubAppOauthStart(ctx echo.Context, request UserUpdateGithubAppOauthStartRequestObject) (UserUpdateGithubAppOauthStartResponseObject, error) + UserUpdateGithubOauthCallback(ctx echo.Context, request UserUpdateGithubOauthCallbackRequestObject) (UserUpdateGithubOauthCallbackResponseObject, error) UserUpdateGithubOauthStart(ctx echo.Context, request UserUpdateGithubOauthStartRequestObject) (UserUpdateGithubOauthStartResponseObject, error) @@ -5861,6 +5933,52 @@ func (sh *strictHandler) UserGetCurrent(ctx echo.Context) error { return nil } +// UserUpdateGithubAppOauthCallback operation middleware +func (sh *strictHandler) UserUpdateGithubAppOauthCallback(ctx echo.Context) error { + var request UserUpdateGithubAppOauthCallbackRequestObject + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.UserUpdateGithubAppOauthCallback(ctx, request.(UserUpdateGithubAppOauthCallbackRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UserUpdateGithubAppOauthCallback") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(UserUpdateGithubAppOauthCallbackResponseObject); ok { + return validResponse.VisitUserUpdateGithubAppOauthCallbackResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("Unexpected response type: %T", response) + } + return nil +} + +// UserUpdateGithubAppOauthStart operation middleware +func (sh *strictHandler) UserUpdateGithubAppOauthStart(ctx echo.Context) error { + var request UserUpdateGithubAppOauthStartRequestObject + + handler := func(ctx echo.Context, request interface{}) (interface{}, error) { + return sh.ssi.UserUpdateGithubAppOauthStart(ctx, request.(UserUpdateGithubAppOauthStartRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "UserUpdateGithubAppOauthStart") + } + + response, err := handler(ctx, request) + + if err != nil { + return err + } else if validResponse, ok := response.(UserUpdateGithubAppOauthStartResponseObject); ok { + return validResponse.VisitUserUpdateGithubAppOauthStartResponse(ctx.Response()) + } else if response != nil { + return fmt.Errorf("Unexpected response type: %T", response) + } + return nil +} + // UserUpdateGithubOauthCallback operation middleware func (sh *strictHandler) UserUpdateGithubOauthCallback(ctx echo.Context) error { var request UserUpdateGithubOauthCallbackRequestObject @@ -6386,131 +6504,131 @@ func (sh *strictHandler) WorkflowVersionGetDefinition(ctx echo.Context, workflow // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/frontend/app/src/lib/api/generated/Api.ts b/frontend/app/src/lib/api/generated/Api.ts index da1fd7590..4d7e9f3a3 100644 --- a/frontend/app/src/lib/api/generated/Api.ts +++ b/frontend/app/src/lib/api/generated/Api.ts @@ -186,13 +186,11 @@ export class Api extends HttpClient this.request({ path: `/api/v1/users/github/start`, method: "GET", - secure: true, ...params, }); /** @@ -202,12 +200,42 @@ export class Api extends HttpClient this.request({ path: `/api/v1/users/github/callback`, method: "GET", + ...params, + }); + /** + * @description Starts the OAuth flow + * + * @tags User + * @name UserUpdateGithubAppOauthStart + * @summary Start OAuth flow + * @request GET:/api/v1/users/github-app/start + * @secure + */ + userUpdateGithubAppOauthStart = (params: RequestParams = {}) => + this.request({ + path: `/api/v1/users/github-app/start`, + method: "GET", + secure: true, + ...params, + }); + /** + * @description Completes the OAuth flow + * + * @tags User + * @name UserUpdateGithubAppOauthCallback + * @summary Complete OAuth flow + * @request GET:/api/v1/users/github-app/callback + * @secure + */ + userUpdateGithubAppOauthCallback = (params: RequestParams = {}) => + this.request({ + path: `/api/v1/users/github-app/callback`, + method: "GET", secure: true, ...params, }); diff --git a/frontend/app/src/pages/auth/login/index.tsx b/frontend/app/src/pages/auth/login/index.tsx index 0edad6425..113c1246d 100644 --- a/frontend/app/src/pages/auth/login/index.tsx +++ b/frontend/app/src/pages/auth/login/index.tsx @@ -10,6 +10,7 @@ import useApiMeta from '../hooks/use-api-meta'; import { Loading } from '@/components/ui/loading'; import { Icons } from '@/components/ui/icons'; import useErrorParam from '../hooks/use-error-param'; +import React from 'react'; export default function Login() { useErrorParam(); @@ -22,19 +23,27 @@ export default function Login() { const schemes = meta.data?.data?.auth?.schemes || []; const basicEnabled = schemes.includes('basic'); const googleEnabled = schemes.includes('google'); + const githubEnabled = schemes.includes('github'); let prompt = 'Enter your email and password below.'; - if (basicEnabled && googleEnabled) { - prompt = 'Enter your email and password below, or continue with Google.'; - } else if (googleEnabled) { - prompt = 'Continue with Google.'; + if (basicEnabled && (googleEnabled || githubEnabled)) { + prompt = + 'Enter your email and password below, or continue with a supported provider.'; + } else if (googleEnabled || githubEnabled) { + prompt = 'Continue with a supported provider.'; } else if (basicEnabled) { prompt = 'Enter your email and password below.'; } else { prompt = 'No login methods are enabled.'; } + const forms = [ + basicEnabled && , + googleEnabled && , + githubEnabled && , + ].filter(Boolean); + return (
@@ -55,20 +64,12 @@ export default function Login() {

{prompt}

- {basicEnabled && } - {basicEnabled && schemes.length > 1 && ( -
-
- -
-
- - Or continue with - -
-
- )} - {googleEnabled && } + {forms.map((form, index) => ( + + {form} + {index < schemes.length - 1 && } + + ))}

By clicking continue, you agree to our{' '} +

+ +
+
+ + Or continue with + +
+
+ ); +} + function BasicLogin() { const navigate = useNavigate(); const [fieldErrors, setFieldErrors] = useState>({}); @@ -130,3 +146,14 @@ export function GoogleLogin() { ); } + +export function GithubLogin() { + return ( + + + + ); +} diff --git a/frontend/app/src/pages/auth/register/index.tsx b/frontend/app/src/pages/auth/register/index.tsx index 79c116114..96f8cdcc1 100644 --- a/frontend/app/src/pages/auth/register/index.tsx +++ b/frontend/app/src/pages/auth/register/index.tsx @@ -8,8 +8,9 @@ import { useState } from 'react'; import { useApiError } from '@/lib/hooks'; import useApiMeta from '../hooks/use-api-meta'; import { Loading } from '@/components/ui/loading'; -import { GoogleLogin } from '../login'; +import { GithubLogin, GoogleLogin, OrContinueWith } from '../login'; import useErrorParam from '../hooks/use-error-param'; +import React from 'react'; export default function Register() { useErrorParam(); @@ -22,20 +23,27 @@ export default function Register() { const schemes = meta.data?.data?.auth?.schemes || []; const basicEnabled = schemes.includes('basic'); const googleEnabled = schemes.includes('google'); + const githubEnabled = schemes.includes('github'); let prompt = 'Create an account to get started.'; - if (basicEnabled && googleEnabled) { + if (basicEnabled && (googleEnabled || githubEnabled)) { prompt = - 'Enter your email and password to create an account, or continue with Google.'; - } else if (googleEnabled) { - prompt = 'Continue with Google.'; + 'Enter your email and password to create an account, or continue with a supported provider.'; + } else if (googleEnabled || githubEnabled) { + prompt = 'Continue with a supported provider.'; } else if (basicEnabled) { prompt = 'Create an account to get started.'; } else { prompt = 'No login methods are enabled.'; } + const forms = [ + basicEnabled && , + googleEnabled && , + githubEnabled && , + ].filter(Boolean); + return (
@@ -56,20 +64,12 @@ export default function Register() {

{prompt}

- {basicEnabled && } - {basicEnabled && schemes.length > 1 && ( -
-
- -
-
- - Or continue with - -
-
- )} - {googleEnabled && } + {forms.map((form, index) => ( + + {form} + {index < schemes.length - 1 && } + + ))}

By clicking continue, you agree to our{' '} :///api/v1/users/github/callback` +- Callback URL: `:///api/v1/users/github-app/callback` - The **Request user authorization (OAuth) during installation** checkbox should be checked. - Webhook URL: `:///api/v1/github/webhook` - Webhook secret: generate a random webhook secret for your domain, for example by running `cat /dev/urandom | base64 | head -c 32`. **Make note of this secret, as you will need it later**. diff --git a/frontend/docs/pages/self-hosting/github-app-setup.mdx b/frontend/docs/pages/self-hosting/github-app-setup.mdx index 03baf1b7a..79e3450a3 100644 --- a/frontend/docs/pages/self-hosting/github-app-setup.mdx +++ b/frontend/docs/pages/self-hosting/github-app-setup.mdx @@ -7,7 +7,7 @@ You can configure a Github app to integrate Hatchet with your Github repositorie To create a Github app that can read from your repositories, navigate to your organization settings page (alternately, you can navigate to your personal settings page) and select **Developer Settings** in the sidebar. Go to **Github Apps** and select **New Github App**. You should use the following settings: - Homepage URL: you can set this as https://hatchet.run, or some other domain for your organization. -- Callback URL: `:///api/v1/users/github/callback` +- Callback URL: `:///api/v1/users/github-app/callback` - The **Request user authorization (OAuth) during installation** checkbox should be checked. - Webhook URL: `:///api/v1/github/webhook` - Webhook secret: generate a random webhook secret for your domain, for example by running `cat /dev/urandom | base64 | head -c 32`. **Make note of this secret, as you will need it later**. diff --git a/internal/auth/oauth/configs.go b/internal/auth/oauth/configs.go index 460c7240a..220652173 100644 --- a/internal/auth/oauth/configs.go +++ b/internal/auth/oauth/configs.go @@ -14,6 +14,8 @@ type Config struct { const ( GoogleAuthURL string = "https://accounts.google.com/o/oauth2/v2/auth" GoogleTokenURL string = "https://oauth2.googleapis.com/token" // #nosec G101 + GithubAuthURL string = "https://github.com/login/oauth/authorize" + GithubTokenURL string = "https://github.com/login/oauth/access_token" // #nosec G101 ) func NewGoogleClient(cfg *Config) *oauth2.Config { @@ -28,3 +30,16 @@ func NewGoogleClient(cfg *Config) *oauth2.Config { Scopes: cfg.Scopes, } } + +func NewGithubClient(cfg *Config) *oauth2.Config { + return &oauth2.Config{ + ClientID: cfg.ClientID, + ClientSecret: cfg.ClientSecret, + Endpoint: oauth2.Endpoint{ + AuthURL: GithubAuthURL, + TokenURL: GithubTokenURL, + }, + RedirectURL: cfg.BaseURL + "/api/v1/users/github/callback", + Scopes: cfg.Scopes, + } +} diff --git a/internal/config/loader/loader.go b/internal/config/loader/loader.go index 19b54fc74..d10c6f0ef 100644 --- a/internal/config/loader/loader.go +++ b/internal/config/loader/loader.go @@ -239,6 +239,23 @@ func GetServerConfigFromConfigfile(dc *database.Config, cf *server.ServerConfigF auth.GoogleOAuthConfig = gClient } + if cf.Auth.Github.Enabled { + if cf.Auth.Github.ClientID == "" { + return nil, nil, fmt.Errorf("github client id is required") + } + + if cf.Auth.Github.ClientSecret == "" { + return nil, nil, fmt.Errorf("github client secret is required") + } + + auth.GithubOAuthConfig = oauth.NewGithubClient(&oauth.Config{ + ClientID: cf.Auth.Github.ClientID, + ClientSecret: cf.Auth.Github.ClientSecret, + BaseURL: cf.Runtime.ServerURL, + Scopes: cf.Auth.Github.Scopes, + }) + } + encryptionSvc, err := loadEncryptionSvc(cf) if err != nil { diff --git a/internal/config/server/server.go b/internal/config/server/server.go index f8fec22e4..39242c06d 100644 --- a/internal/config/server/server.go +++ b/internal/config/server/server.go @@ -144,6 +144,8 @@ type ConfigFileAuth struct { Cookie ConfigFileAuthCookie `mapstructure:"cookie" json:"cookie,omitempty"` Google ConfigFileAuthGoogle `mapstructure:"google" json:"google,omitempty"` + + Github ConfigFileAuthGithub `mapstructure:"github" json:"github,omitempty"` } type ConfigFileVCS struct { @@ -169,6 +171,14 @@ type ConfigFileAuthGoogle struct { Scopes []string `mapstructure:"scopes" json:"scopes,omitempty" default:"[\"openid\", \"profile\", \"email\"]"` } +type ConfigFileAuthGithub struct { + Enabled bool `mapstructure:"enabled" json:"enabled,omitempty" default:"false"` + + ClientID string `mapstructure:"clientID" json:"clientID,omitempty"` + ClientSecret string `mapstructure:"clientSecret" json:"clientSecret,omitempty"` + Scopes []string `mapstructure:"scopes" json:"scopes,omitempty" default:"[\"read:user\", \"user:email\"]"` +} + type ConfigFileAuthCookie struct { Name string `mapstructure:"name" json:"name,omitempty" default:"hatchet"` Domain string `mapstructure:"domain" json:"domain,omitempty"` @@ -191,6 +201,8 @@ type AuthConfig struct { GoogleOAuthConfig *oauth2.Config + GithubOAuthConfig *oauth2.Config + JWTManager token.JWTManager } @@ -278,6 +290,10 @@ func BindAllEnv(v *viper.Viper) { _ = v.BindEnv("auth.google.clientID", "SERVER_AUTH_GOOGLE_CLIENT_ID") _ = v.BindEnv("auth.google.clientSecret", "SERVER_AUTH_GOOGLE_CLIENT_SECRET") _ = v.BindEnv("auth.google.scopes", "SERVER_AUTH_GOOGLE_SCOPES") + _ = v.BindEnv("auth.github.enabled", "SERVER_AUTH_GITHUB_ENABLED") + _ = v.BindEnv("auth.github.clientID", "SERVER_AUTH_GITHUB_CLIENT_ID") + _ = v.BindEnv("auth.github.clientSecret", "SERVER_AUTH_GITHUB_CLIENT_SECRET") + _ = v.BindEnv("auth.github.scopes", "SERVER_AUTH_GITHUB_SCOPES") // task queue options // legacy options diff --git a/internal/integrations/vcs/github/github_app_oauth.go b/internal/integrations/vcs/github/github_app_oauth.go index a430a0402..25fbbb5d8 100644 --- a/internal/integrations/vcs/github/github_app_oauth.go +++ b/internal/integrations/vcs/github/github_app_oauth.go @@ -57,7 +57,7 @@ func NewGithubAppConf( AuthURL: GithubAuthURL, TokenURL: GithubTokenURL, }, - RedirectURL: cfg.BaseURL + "/api/v1/users/github/callback", + RedirectURL: cfg.BaseURL + "/api/v1/users/github-app/callback", Scopes: cfg.Scopes, }, }, nil diff --git a/internal/repository/user.go b/internal/repository/user.go index c37543b9d..fec8f89c3 100644 --- a/internal/repository/user.go +++ b/internal/repository/user.go @@ -20,7 +20,7 @@ type CreateUserOpts struct { } type OAuthOpts struct { - Provider string `validate:"required,oneof=google"` + Provider string `validate:"required,oneof=google github"` ProviderUserId string `validate:"required,min=1"` AccessToken []byte `validate:"required,min=1"` RefreshToken *[]byte // optional diff --git a/pkg/client/rest/gen.go b/pkg/client/rest/gen.go index e36ffa1b5..60a8ed6ce 100644 --- a/pkg/client/rest/gen.go +++ b/pkg/client/rest/gen.go @@ -1192,6 +1192,12 @@ type ClientInterface interface { // UserGetCurrent request UserGetCurrent(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // UserUpdateGithubAppOauthCallback request + UserUpdateGithubAppOauthCallback(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + + // UserUpdateGithubAppOauthStart request + UserUpdateGithubAppOauthStart(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) + // UserUpdateGithubOauthCallback request UserUpdateGithubOauthCallback(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) @@ -1831,6 +1837,30 @@ func (c *Client) UserGetCurrent(ctx context.Context, reqEditors ...RequestEditor return c.Client.Do(req) } +func (c *Client) UserUpdateGithubAppOauthCallback(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUserUpdateGithubAppOauthCallbackRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) UserUpdateGithubAppOauthStart(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewUserUpdateGithubAppOauthStartRequest(c.Server) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + func (c *Client) UserUpdateGithubOauthCallback(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) { req, err := NewUserUpdateGithubOauthCallbackRequest(c.Server) if err != nil { @@ -3966,6 +3996,60 @@ func NewUserGetCurrentRequest(server string) (*http.Request, error) { return req, nil } +// NewUserUpdateGithubAppOauthCallbackRequest generates requests for UserUpdateGithubAppOauthCallback +func NewUserUpdateGithubAppOauthCallbackRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/users/github-app/callback") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewUserUpdateGithubAppOauthStartRequest generates requests for UserUpdateGithubAppOauthStart +func NewUserUpdateGithubAppOauthStartRequest(server string) (*http.Request, error) { + var err error + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/api/v1/users/github-app/start") + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + // NewUserUpdateGithubOauthCallbackRequest generates requests for UserUpdateGithubOauthCallback func NewUserUpdateGithubOauthCallbackRequest(server string) (*http.Request, error) { var err error @@ -4933,6 +5017,12 @@ type ClientWithResponsesInterface interface { // UserGetCurrentWithResponse request UserGetCurrentWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserGetCurrentResponse, error) + // UserUpdateGithubAppOauthCallbackWithResponse request + UserUpdateGithubAppOauthCallbackWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubAppOauthCallbackResponse, error) + + // UserUpdateGithubAppOauthStartWithResponse request + UserUpdateGithubAppOauthStartWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubAppOauthStartResponse, error) + // UserUpdateGithubOauthCallbackWithResponse request UserUpdateGithubOauthCallbackWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubOauthCallbackResponse, error) @@ -5943,6 +6033,48 @@ func (r UserGetCurrentResponse) StatusCode() int { return 0 } +type UserUpdateGithubAppOauthCallbackResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r UserUpdateGithubAppOauthCallbackResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UserUpdateGithubAppOauthCallbackResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type UserUpdateGithubAppOauthStartResponse struct { + Body []byte + HTTPResponse *http.Response +} + +// Status returns HTTPResponse.Status +func (r UserUpdateGithubAppOauthStartResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r UserUpdateGithubAppOauthStartResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + type UserUpdateGithubOauthCallbackResponse struct { Body []byte HTTPResponse *http.Response @@ -6833,6 +6965,24 @@ func (c *ClientWithResponses) UserGetCurrentWithResponse(ctx context.Context, re return ParseUserGetCurrentResponse(rsp) } +// UserUpdateGithubAppOauthCallbackWithResponse request returning *UserUpdateGithubAppOauthCallbackResponse +func (c *ClientWithResponses) UserUpdateGithubAppOauthCallbackWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubAppOauthCallbackResponse, error) { + rsp, err := c.UserUpdateGithubAppOauthCallback(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseUserUpdateGithubAppOauthCallbackResponse(rsp) +} + +// UserUpdateGithubAppOauthStartWithResponse request returning *UserUpdateGithubAppOauthStartResponse +func (c *ClientWithResponses) UserUpdateGithubAppOauthStartWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubAppOauthStartResponse, error) { + rsp, err := c.UserUpdateGithubAppOauthStart(ctx, reqEditors...) + if err != nil { + return nil, err + } + return ParseUserUpdateGithubAppOauthStartResponse(rsp) +} + // UserUpdateGithubOauthCallbackWithResponse request returning *UserUpdateGithubOauthCallbackResponse func (c *ClientWithResponses) UserUpdateGithubOauthCallbackWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*UserUpdateGithubOauthCallbackResponse, error) { rsp, err := c.UserUpdateGithubOauthCallback(ctx, reqEditors...) @@ -8616,6 +8766,38 @@ func ParseUserGetCurrentResponse(rsp *http.Response) (*UserGetCurrentResponse, e return response, nil } +// ParseUserUpdateGithubAppOauthCallbackResponse parses an HTTP response from a UserUpdateGithubAppOauthCallbackWithResponse call +func ParseUserUpdateGithubAppOauthCallbackResponse(rsp *http.Response) (*UserUpdateGithubAppOauthCallbackResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UserUpdateGithubAppOauthCallbackResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + +// ParseUserUpdateGithubAppOauthStartResponse parses an HTTP response from a UserUpdateGithubAppOauthStartWithResponse call +func ParseUserUpdateGithubAppOauthStartResponse(rsp *http.Response) (*UserUpdateGithubAppOauthStartResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &UserUpdateGithubAppOauthStartResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + return response, nil +} + // ParseUserUpdateGithubOauthCallbackResponse parses an HTTP response from a UserUpdateGithubOauthCallbackWithResponse call func ParseUserUpdateGithubOauthCallbackResponse(rsp *http.Response) (*UserUpdateGithubOauthCallbackResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) diff --git a/python-sdk/hatchet_sdk/clients/rest/api/user_api.py b/python-sdk/hatchet_sdk/clients/rest/api/user_api.py index ecf496c31..befa2fe94 100644 --- a/python-sdk/hatchet_sdk/clients/rest/api/user_api.py +++ b/python-sdk/hatchet_sdk/clients/rest/api/user_api.py @@ -821,6 +821,480 @@ class UserApi: + @validate_call + def user_update_github_app_oauth_callback( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> None: + """Complete OAuth flow + + Completes the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_callback_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def user_update_github_app_oauth_callback_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[None]: + """Complete OAuth flow + + Completes the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_callback_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def user_update_github_app_oauth_callback_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Complete OAuth flow + + Completes the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_callback_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _user_update_github_app_oauth_callback_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[str, str] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + + + # authentication setting + _auth_settings: List[str] = [ + 'cookieAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/users/github-app/callback', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + + @validate_call + def user_update_github_app_oauth_start( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> None: + """Start OAuth flow + + Starts the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_start_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ).data + + + @validate_call + def user_update_github_app_oauth_start_with_http_info( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> ApiResponse[None]: + """Start OAuth flow + + Starts the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_start_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + response_data.read() + return self.api_client.response_deserialize( + response_data=response_data, + response_types_map=_response_types_map, + ) + + + @validate_call + def user_update_github_app_oauth_start_without_preload_content( + self, + _request_timeout: Union[ + None, + Annotated[StrictFloat, Field(gt=0)], + Tuple[ + Annotated[StrictFloat, Field(gt=0)], + Annotated[StrictFloat, Field(gt=0)] + ] + ] = None, + _request_auth: Optional[Dict[StrictStr, Any]] = None, + _content_type: Optional[StrictStr] = None, + _headers: Optional[Dict[StrictStr, Any]] = None, + _host_index: Annotated[StrictInt, Field(ge=0, le=0)] = 0, + ) -> RESTResponseType: + """Start OAuth flow + + Starts the OAuth flow + + :param _request_timeout: timeout setting for this request. If one + number provided, it will be total request + timeout. It can also be a pair (tuple) of + (connection, read) timeouts. + :type _request_timeout: int, tuple(int, int), optional + :param _request_auth: set to override the auth_settings for an a single + request; this effectively ignores the + authentication in the spec for a single request. + :type _request_auth: dict, optional + :param _content_type: force content-type for the request. + :type _content_type: str, Optional + :param _headers: set to override the headers for a single + request; this effectively ignores the headers + in the spec for a single request. + :type _headers: dict, optional + :param _host_index: set to override the host_index for a single + request; this effectively ignores the host_index + in the spec for a single request. + :type _host_index: int, optional + :return: Returns the result object. + """ # noqa: E501 + + _param = self._user_update_github_app_oauth_start_serialize( + _request_auth=_request_auth, + _content_type=_content_type, + _headers=_headers, + _host_index=_host_index + ) + + _response_types_map: Dict[str, Optional[str]] = { + '302': None, + } + response_data = self.api_client.call_api( + *_param, + _request_timeout=_request_timeout + ) + return response_data.response + + + def _user_update_github_app_oauth_start_serialize( + self, + _request_auth, + _content_type, + _headers, + _host_index, + ) -> RequestSerialized: + + _host = None + + _collection_formats: Dict[str, str] = { + } + + _path_params: Dict[str, str] = {} + _query_params: List[Tuple[str, str]] = [] + _header_params: Dict[str, Optional[str]] = _headers or {} + _form_params: List[Tuple[str, str]] = [] + _files: Dict[str, str] = {} + _body_params: Optional[bytes] = None + + # process the path parameters + # process the query parameters + # process the header parameters + # process the form parameters + # process the body parameter + + + + + # authentication setting + _auth_settings: List[str] = [ + 'cookieAuth' + ] + + return self.api_client.param_serialize( + method='GET', + resource_path='/api/v1/users/github-app/start', + path_params=_path_params, + query_params=_query_params, + header_params=_header_params, + body=_body_params, + post_params=_form_params, + files=_files, + auth_settings=_auth_settings, + collection_formats=_collection_formats, + _host=_host, + _request_auth=_request_auth + ) + + + + @validate_call def user_update_github_oauth_callback( self, @@ -1037,7 +1511,6 @@ class UserApi: # authentication setting _auth_settings: List[str] = [ - 'cookieAuth' ] return self.api_client.param_serialize( @@ -1274,7 +1747,6 @@ class UserApi: # authentication setting _auth_settings: List[str] = [ - 'cookieAuth' ] return self.api_client.param_serialize(