diff --git a/internal/api/user.go b/internal/api/user.go index da74c402f..9e47440ef 100644 --- a/internal/api/user.go +++ b/internal/api/user.go @@ -92,6 +92,13 @@ func (a *API) UserUpdate(w http.ResponseWriter, r *http.Request) error { user := getUser(ctx) session := getSession(ctx) + claims := getClaims(ctx) + + if claims != nil && claims.PendingPasswordSet { + if params.Email != "" || params.Phone != "" || params.Data != nil || params.AppData != nil { + return apierrors.NewForbiddenError(apierrors.ErrorCodeUserNotAllowed, "Session requires password to be set before updating other fields") + } + } if err := a.validateUserUpdateParams(ctx, params); err != nil { return err diff --git a/internal/tokens/service.go b/internal/tokens/service.go index 640847496..c50d51686 100644 --- a/internal/tokens/service.go +++ b/internal/tokens/service.go @@ -81,6 +81,7 @@ type AccessTokenClaims struct { AuthenticationMethodReference AMRClaim `json:"amr,omitempty"` SessionId string `json:"session_id,omitempty"` IsAnonymous bool `json:"is_anonymous"` + PendingPasswordSet bool `json:"pending_password_set,omitempty"` ClientID string `json:"client_id,omitempty"` Scope string `json:"scope,omitempty"` } @@ -694,6 +695,10 @@ func (s *Service) GenerateAccessToken(r *http.Request, tx *storage.Connection, p scopes = *session.Scopes } + userHasPassword := params.User.HasPassword() + isPendingPasswordSet := session.IsRecovery() || + (!userHasPassword && params.User.InvitedAt != nil) + claims := &v0hooks.AccessTokenClaims{ RegisteredClaims: jwt.RegisteredClaims{ Subject: params.User.ID.String(), @@ -711,6 +716,7 @@ func (s *Service) GenerateAccessToken(r *http.Request, tx *storage.Connection, p AuthenticatorAssuranceLevel: aal.String(), AuthenticationMethodReference: amr, IsAnonymous: params.User.IsAnonymous, + PendingPasswordSet: isPendingPasswordSet, ClientID: clientID, Scope: scopes, }