MCPcopy Create free account
hub / github.com/bytebase/bytebase / Signup

Method Signup

backend/api/v1/auth_service.go:211–333  ·  view source on GitHub ↗

Signup registers a new user account (self-service). Creates a principal and assigns a workspace: - If the user's email was pre-invited to a workspace, joins that workspace. - Otherwise, creates a new workspace with the user as admin.

(ctx context.Context, req *connect.Request[v1pb.SignupRequest])

Source from the content-addressed store, hash-verified

209// - If the user's email was pre-invited to a workspace, joins that workspace.
210// - Otherwise, creates a new workspace with the user as admin.
211func (s *AuthService) Signup(ctx context.Context, req *connect.Request[v1pb.SignupRequest]) (*connect.Response[v1pb.LoginResponse], error) {
212 request := req.Msg
213 if request.Email == "" {
214 return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("email must be set"))
215 }
216 if request.Title == "" {
217 return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("title must be set"))
218 }
219 if request.Password == "" {
220 return nil, connect.NewError(connect.CodeInvalidArgument, errors.Errorf("password must be set"))
221 }
222 if err := validateEndUserEmail(request.Email); err != nil {
223 return nil, err
224 }
225
226 // Check if principal already exists.
227 existingUser, err := s.store.GetUserByEmail(ctx, request.Email)
228 if err != nil {
229 return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to find user by email"))
230 }
231 if existingUser != nil {
232 return nil, connect.NewError(connect.CodeAlreadyExists, errors.Errorf("email %s is already registered", request.Email))
233 }
234
235 // Resolve the target workspace (read-only) so we can check restrictions BEFORE
236 // any write — otherwise a rejected signup would leave an orphan user/workspace behind.
237 targetWorkspaceID, _, err := s.resolveWorkspaceIDByEmail(ctx, request.Email)
238 if err != nil {
239 return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to resolve target workspace"))
240 }
241 // Announce the workspace on every exit path so denied signups (DisallowSignup,
242 // password restriction) still produce audit entries. Uses targetWorkspaceID (resolved
243 // before any writes) rather than the provisioned workspaceID.
244 defer func() { common.SetAuditWorkspaceID(ctx, targetWorkspaceID) }()
245
246 restriction, err := getAccountRestriction(
247 ctx,
248 s.store,
249 s.licenseService,
250 s.profile.SaaS,
251 targetWorkspaceID,
252 )
253 if err != nil {
254 return nil, err
255 }
256 if restriction.DisallowSignup {
257 return nil, connect.NewError(connect.CodePermissionDenied, errors.Errorf("sign up is disallowed for this workspace %v", targetWorkspaceID))
258 }
259 if err := validatePasswordWithRestriction(request.Password, convertToStorePasswordRestriction(restriction.PasswordRestriction)); err != nil {
260 return nil, err
261 }
262
263 workspaceID, err := s.provisionWorkspaceForNewUser(ctx, request.Email)
264 if err != nil {
265 return nil, connect.NewError(connect.CodeInternal, errors.Wrapf(err, "failed to provision workspace"))
266 }
267
268 passwordHash, err := bcrypt.GenerateFromPassword([]byte(request.Password), bcrypt.DefaultCost)

Callers

nothing calls this directly

Calls 15

SetAuditWorkspaceIDFunction · 0.92
GetAccessTokenDurationFunction · 0.92
GenerateAccessTokenFunction · 0.92
GetTokenCookieFunction · 0.92
GenerateOpaqueTokenFunction · 0.92
GetRefreshTokenDurationFunction · 0.92
HashTokenFunction · 0.92
GetRefreshTokenCookieFunction · 0.92
BBErrorFunction · 0.92
FormatWorkspaceFunction · 0.92

Tested by

no test coverage detected