Skip to content

Conversation

swittk
Copy link

@swittk swittk commented Oct 7, 2025

Pull Request

Issue

Closes: #9560

Approach

  • Optional autoSignupOnLogin boolean flag for Parse-server configuration
  • A retry try/catch wrapper around login to support the new flag, creating a signup user only when the flag is true & conditions are met.

Tasks

  • Add tests

Summary by CodeRabbit

  • New Features

    • Optional auto-signup on login: enabling this will create a user and complete login when credentials match no existing account.
  • Configuration

    • New option autoSignupOnLogin (default: false). Configurable via PARSE_SERVER_AUTO_SIGNUP_ON_LOGIN.
  • Documentation

    • Configuration references and public types updated to document autoSignupOnLogin.
  • Tests

    • Added tests validating automatic user creation and login when enabled.

Copy link

I will reformat the title to use the proper commit message syntax.

@parse-github-assistant parse-github-assistant bot changed the title feat: added autosignuponlogin feat: Added autosignuponlogin Oct 7, 2025
Copy link

🚀 Thanks for opening this pull request!

@parseplatformorg
Copy link
Contributor

parseplatformorg commented Oct 7, 2025

Snyk checks have passed. No issues have been found so far.

Status Scanner Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

Copy link

coderabbitai bot commented Oct 7, 2025

📝 Walkthrough

Walkthrough

Adds a new server option autoSignupOnLogin, wires it into config, options, docs and typings, updates UsersRouter to automatically create a user on login-if-not-found, and adds tests (including a duplicated test block) and validation for the new option.

Changes

Cohort / File(s) Summary
Tests: Auto-signup on login
spec/ParseUser.spec.js
Adds a test "auto signs up user on login when enabled" that enables autoSignupOnLogin, performs REST login for a new username/password, asserts returned username and sessionToken, verifies login via Parse.User.logIn, then resets config. The test block is added twice (duplicate).
Config validation
src/Config.js
Adds autoSignupOnLogin to options validation: introduces validateAutoSignupOnLogin and calls it from validateOptions.
Options definitions and docs
src/Options/Definitions.js, src/Options/docs.js, src/Options/index.js
Adds public option autoSignupOnLogin (PARSE_SERVER_AUTO_SIGNUP_ON_LOGIN, boolean, default false) to definitions and docs; also adds emailVerifyTokenValidityDuration to index.js.
Login flow: auto-signup
src/Routers/UsersRouter.js
Extends handleLogIn to attempt auto-signup on OBJECT_NOT_FOUND when enabled: adds helpers _getLoginPayload, _prepareAutoSignupCredentials, _autoSignupOnLogin; creates temporary signup session, retries authentication, and attempts cleanup of the temp session token.
Type definitions
types/Options/index.d.ts
Adds optional boolean autoSignupOnLogin?: boolean to ParseServerOptions typings.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant UR as UsersRouter
  participant AU as Auth Service
  participant UC as User Create (REST)
  participant SS as Sessions

  C->>UR: POST /login {username/email, password}
  UR->>AU: Authenticate
  AU-->>UR: Error OBJECT_NOT_FOUND
  alt autoSignupOnLogin enabled and credentials present
    UR->>UC: Create user (auto-signup)
    UC-->>UR: {userId, tempSessionToken}
    note over UR: store temp signup session token
    UR->>AU: Authenticate (retry)
    AU-->>UR: {sessionToken, user}
    UR->>SS: Revoke tempSessionToken
    SS-->>UR: OK / NOT_FOUND
    UR-->>C: 200 {sessionToken, user}
  else disabled or invalid credentials
    UR-->>C: Error OBJECT_NOT_FOUND
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The pull request introduces an unrelated addition of the emailVerifyTokenValidityDuration configuration option in src/Options/index.js, which is not specified in issue #9560 or required for the autoSignupOnLogin feature. This change diverges from the scope of automatically creating users on login and should be reviewed or removed. The duplicate test block in ParseUser.spec.js also suggests potential accidental replication that is orthogonal to the feature’s objectives. Remove the emailVerifyTokenValidityDuration addition from this pull request or move it to a dedicated PR, and eliminate the duplicated test block to keep the change focused on the autoSignupOnLogin feature.
✅ Passed checks (4 passed)
Check name Status Explanation
Title Check ✅ Passed The title “feat: Added autosignuponlogin” succinctly conveys the introduction of the autoSignupOnLogin feature and reflects the main change implemented in the pull request. It is clear, concise, and directly related to the primary functionality added without extraneous details. Therefore, it meets the criteria for a descriptive and focused pull request title.
Linked Issues Check ✅ Passed The changes fully address the objectives of issue #9560 by introducing the autoSignupOnLogin configuration option, integrating its validation, documentation, type definitions, and implementing the login flow shortcut to auto-create users when no matching user exists. All coding-related requirements, including configuration, route behavior, and test coverage, have been met in accordance with the linked issue. No unmet objectives remain.
Description Check ✅ Passed The pull request description follows the repository template by including the required sections for Pull Request metadata, Issue linkage, Approach, and Tasks. The Issue section correctly references and closes the linked issue, the Approach describes the changes in detail, and the Tasks checklist notes that tests were added. While additional tasks for documentation updates and security checks could be documented if relevant, the description is largely complete and structured according to the template.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (4)
spec/ParseUser.spec.js (1)

189-213: Always restore server config; use try/finally to prevent leakage

If an assertion throws before the last lines, autoSignupOnLogin may remain enabled and affect other tests.

Apply try/finally so logout and config reset always run.

 it('auto signs up user on login when enabled', async () => {
-  await reconfigureServer({ autoSignupOnLogin: true });
-  const username = 'autoLoginUser';
-  const password = 'autoLoginPass';
-  const response = await request({
-    method: 'POST',
-    url: 'http://localhost:8378/1/login',
-    headers: {
-      'X-Parse-Application-Id': Parse.applicationId,
-      'X-Parse-REST-API-Key': 'rest',
-      'Content-Type': 'application/json',
-    },
-    body: {
-      username,
-      password,
-    },
-  });
-  expect(response.data.username).toBe(username);
-  expect(response.data.sessionToken).toBeDefined();
-
-  const user = await Parse.User.logIn(username, password);
-  expect(user).toBeDefined();
-  await Parse.User.logOut();
-  await reconfigureServer({ autoSignupOnLogin: false });
+  await reconfigureServer({ autoSignupOnLogin: true });
+  const username = 'autoLoginUser';
+  const password = 'autoLoginPass';
+  try {
+    const response = await request({
+      method: 'POST',
+      url: 'http://localhost:8378/1/login',
+      headers: {
+        'X-Parse-Application-Id': Parse.applicationId,
+        'X-Parse-REST-API-Key': 'rest',
+        'Content-Type': 'application/json',
+      },
+      body: { username, password },
+    });
+    expect(response.data.username).toBe(username);
+    expect(response.data.sessionToken).toBeDefined();
+    const user = await Parse.User.logIn(username, password);
+    expect(user).toBeDefined();
+  } finally {
+    await Parse.User.logOut().catch(() => {});
+    await reconfigureServer({ autoSignupOnLogin: false });
+  }
 });
src/Routers/UsersRouter.js (3)

366-393: Gate auto-signup strictly and align normalization

  • Don’t auto-signup if a user with provided username/email exists; avoids enumeration on wrong password.
  • Avoid trimming here unless authentication does the same; differing normalization can create unintended accounts.

Apply the following changes (make function async, add existence check, remove trim):

-  _prepareAutoSignupCredentials(req, error) {
+  async _prepareAutoSignupCredentials(req, error) {
     if (!req.config.autoSignupOnLogin) {
       return null;
     }
     if (!(error instanceof Parse.Error) || error.code !== Parse.Error.OBJECT_NOT_FOUND) {
       return null;
     }
     if (req.body && req.body.authData) {
       return null;
     }
     const payload = this._getLoginPayload(req);
-    const rawUsername = typeof payload.username === 'string' ? payload.username.trim() : '';
-    const rawEmail = typeof payload.email === 'string' ? payload.email.trim() : '';
-    const password = payload.password;
-    const hasUsername = rawUsername.length > 0;
-    const hasEmail = rawEmail.length > 0;
+    const rawUsername = typeof payload.username === 'string' ? payload.username : '';
+    const rawEmail = typeof payload.email === 'string' ? payload.email : '';
+    const password = payload.password;
+    const hasUsername = rawUsername.length > 0;
+    const hasEmail = rawEmail.length > 0;
     if (!hasUsername && !hasEmail) {
       return null;
     }
     if (typeof password !== 'string') {
       return null;
     }
+    // Only auto-signup when there is no existing user for supplied identifier(s)
+    let query;
+    if (hasEmail && hasUsername) {
+      query = { email: rawEmail, username: rawUsername };
+    } else if (hasEmail) {
+      query = { email: rawEmail };
+    } else {
+      query = { $or: [{ username: rawUsername }, { email: rawUsername }] };
+    }
+    const existing = await req.config.database.find('_User', query, {}, Auth.maintenance(req.config));
+    if (existing && existing.length) {
+      return null;
+    }
     return {
       username: hasUsername ? rawUsername : rawEmail,
       email: hasEmail ? rawEmail : undefined,
       password,
     };
   }

395-422: Handle duplicate username/email errors gracefully to avoid race-induced failures

If another request created the user between your existence check and create, rest.create will throw USERNAME_TAKEN / EMAIL_TAKEN. Swallow these and continue to re-authenticate.

Apply:

   async _autoSignupOnLogin(req, credentials) {
     const userData = {
       username: credentials.username,
       password: credentials.password,
     };
     if (credentials.email !== undefined) {
       userData.email = credentials.email;
     }
@@
-    const result = await rest.create(
-      req.config,
-      req.auth,
-      '_User',
-      userData,
-      req.info.clientSDK,
-      req.info.context
-    );
-    const user = result?.response;
-    if (!user) {
-      throw new Parse.Error(
-        Parse.Error.INTERNAL_SERVER_ERROR,
-        'Unable to automatically sign up user.'
-      );
-    }
-    return user.sessionToken;
+    try {
+      const result = await rest.create(
+        req.config,
+        req.auth,
+        '_User',
+        userData,
+        req.info.clientSDK,
+        req.info.context
+      );
+      const user = result?.response;
+      if (!user) {
+        throw new Parse.Error(
+          Parse.Error.INTERNAL_SERVER_ERROR,
+          'Unable to automatically sign up user.'
+        );
+      }
+      return user.sessionToken;
+    } catch (e) {
+      if (e && (e.code === Parse.Error.USERNAME_TAKEN || e.code === Parse.Error.EMAIL_TAKEN)) {
+        // Likely created concurrently; let caller re-attempt authentication
+        return null;
+      }
+      throw e;
+    }
   }

377-382: Normalization mismatch (trim) may create unintended accounts

Login authentication doesn’t trim username/email, but auto-signup does. This can auto-create “[email protected]” when the user typed “ [email protected] ”.

Remove trim here (or apply consistent normalization across both paths). See suggested diff in the comment above for Lines 366-393.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between be362fe and 486a405.

📒 Files selected for processing (7)
  • spec/ParseUser.spec.js (1 hunks)
  • src/Config.js (3 hunks)
  • src/Options/Definitions.js (1 hunks)
  • src/Options/docs.js (1 hunks)
  • src/Options/index.js (1 hunks)
  • src/Routers/UsersRouter.js (3 hunks)
  • types/Options/index.d.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Options/Definitions.js (1)
resources/buildConfigDefinitions.js (1)
  • parsers (12-12)
spec/ParseUser.spec.js (1)
spec/helper.js (2)
  • reconfigureServer (171-205)
  • Parse (4-4)
🪛 Biome (2.1.2)
src/Options/index.js

[error] 197-197: Expected a statement but instead found '?'.

Expected a statement here.

(parse)

🔇 Additional comments (4)
src/Options/index.js (1)

193-197: Option surfaced correctly; verify Biome parse error on Line 197

The field and docs look consistent with the surrounding Flow types. However, static analysis flagged “Expected a statement but instead found ‘?’” at Line 197; likely a parser/config hiccup or stray char.

  • Please re-run lint/format locally and ensure Biome/ESLint parse this file cleanly.
  • If needed, regenerate options or normalize the line to remove hidden chars.
types/Options/index.d.ts (1)

80-80: Types aligned

autoSignupOnLogin?: boolean matches the public option surface.

src/Options/docs.js (1)

89-89: Docs added and consistent

JSDoc for autoSignupOnLogin matches Definitions/help and default.

src/Options/Definitions.js (1)

485-491: Definition looks good

Env, help, parser, and default for autoSignupOnLogin are correct and aligned with the option surface.

@swittk
Copy link
Author

swittk commented Oct 9, 2025

any more changes needed?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/Routers/UsersRouter.js (1)

365-378: Consider extracting shared payload logic.

This method duplicates the query/body fallback logic from _authenticateUserFromRequest (lines 71-77). While the duplication is minimal, extracting this shared logic into _getLoginPayload and calling it from both methods would improve maintainability.

Optional refactor:

Reuse _getLoginPayload in _authenticateUserFromRequest:

 _authenticateUserFromRequest(req) {
   return new Promise((resolve, reject) => {
-    // Use query parameters instead if provided in url
-    let payload = req.body || {};
-    if (
-      (!payload.username && req.query && req.query.username) ||
-      (!payload.email && req.query && req.query.email)
-    ) {
-      payload = req.query;
-    }
-    const { username, email, password, ignoreEmailVerification } = payload;
+    const payload = this._getLoginPayload(req);
+    const { username, email, password } = payload;
+    const ignoreEmailVerification = (req.body || req.query)?.ignoreEmailVerification;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 486a405 and b8aeaa0.

📒 Files selected for processing (1)
  • src/Routers/UsersRouter.js (4 hunks)
🔇 Additional comments (4)
src/Routers/UsersRouter.js (4)

203-245: Implementation addresses race condition and enumeration concerns.

The current implementation successfully handles the concerns raised in previous reviews:

  1. Race condition (lines 217-228): Catches duplicate-user errors during signup and re-authenticates, allowing concurrent login requests to succeed when another request creates the user first.
  2. User enumeration: By re-authenticating on USERNAME_TAKEN/EMAIL_TAKEN errors, wrong-password scenarios correctly return OBJECT_NOT_FOUND (matching standard login behavior) rather than leaking account existence.
  3. Session cleanup (lines 229-239): Properly destroys the temporary signup session token with appropriate error handling.

The implementation aligns well with the discussion in past reviews.


383-410: LGTM!

The guard conditions and credential preparation logic are well-structured:

  • Comprehensive checks ensure auto-signup only proceeds when appropriate (feature enabled, OBJECT_NOT_FOUND error, no authData, valid credentials).
  • Proper trimming and validation of username and email fields.
  • Correctly handles the case where only email is provided by using it as the username (line 406), ensuring the username field is always populated for user creation.

412-439: LGTM!

The implementation correctly delegates to rest.create, ensuring all existing validation, triggers, schema checks, and side effects are executed. The error handling properly covers the edge case where user creation might succeed but not return a user object.


744-744: LGTM!

Standardizing error serialization with JSON.stringify improves consistency in error logging.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add Parse Server option autoSignupOnLogin to automatically create a user on log-in if none exists

3 participants