auth0-lock 14.1.0 → 14.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ name: Claude Code PR Review
2
+
3
+ on:
4
+ issue_comment:
5
+ types: [created]
6
+ pull_request_review_comment:
7
+ types: [created]
8
+ pull_request_review:
9
+ types: [submitted]
10
+
11
+ jobs:
12
+ claude-review:
13
+ permissions:
14
+ contents: write
15
+ issues: write
16
+ pull-requests: write
17
+ id-token: write
18
+ uses: auth0/auth0-ai-pr-analyzer-gh-action/.github/workflows/claude-code-review.yml@main
@@ -37,18 +37,18 @@ jobs:
37
37
  run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection.
38
38
 
39
39
  - name: Checkout
40
- uses: actions/checkout@v4
40
+ uses: actions/checkout@v6
41
41
 
42
42
  - name: Initialize CodeQL
43
- uses: github/codeql-action/init@v3
43
+ uses: github/codeql-action/init@v4
44
44
  with:
45
45
  languages: ${{ matrix.language }}
46
46
  queries: +security-and-quality
47
47
 
48
48
  - name: Autobuild
49
- uses: github/codeql-action/autobuild@v3
49
+ uses: github/codeql-action/autobuild@v4
50
50
 
51
51
  - name: Perform CodeQL Analysis
52
- uses: github/codeql-action/analyze@v3
52
+ uses: github/codeql-action/analyze@v4
53
53
  with:
54
54
  category: '/language:${{ matrix.language }}'
@@ -29,7 +29,7 @@ jobs:
29
29
 
30
30
  steps:
31
31
  # Checkout the code
32
- - uses: actions/checkout@v4
32
+ - uses: actions/checkout@v6
33
33
  with:
34
34
  fetch-depth: 0
35
35
 
@@ -29,7 +29,7 @@ jobs:
29
29
  - if: github.actor == 'dependabot[bot]' || github.event_name == 'merge_group'
30
30
  run: exit 0 # Skip unnecessary test runs for dependabot and merge queues. Artifically flag as successful, as this is a required check for branch protection.
31
31
 
32
- - uses: actions/checkout@v4
32
+ - uses: actions/checkout@v6
33
33
  with:
34
34
  ref: ${{ github.event.pull_request.head.sha || github.ref }}
35
35
 
@@ -25,7 +25,7 @@ jobs:
25
25
 
26
26
  steps:
27
27
  - name: Checkout code
28
- uses: actions/checkout@v4
28
+ uses: actions/checkout@v6
29
29
  with:
30
30
  ref: ${{ github.event.pull_request.head.sha || github.ref }}
31
31
 
package/.version CHANGED
@@ -1 +1 @@
1
- v14.1.0
1
+ v14.2.1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Change Log
2
2
 
3
+ ## [v14.2.1](https://github.com/auth0/lock/tree/v14.2.1) (2025-12-03)
4
+ [Full Changelog](https://github.com/auth0/lock/compare/v14.2.0...v14.2.1)
5
+
6
+ **Fixed**
7
+ - Fix: connectionResolver receives incorrect field value when switching between Login and Sign-up tabs [\#2697](https://github.com/auth0/lock/pull/2697) ([ankita10119](https://github.com/ankita10119))
8
+
9
+ ## [v14.2.0](https://github.com/auth0/lock/tree/v14.2.0) (2025-10-21)
10
+ [Full Changelog](https://github.com/auth0/lock/compare/v14.1.0...v14.2.0)
11
+
12
+ **Added**
13
+ - feat: add Claude Code PR Review workflow [\#2679](https://github.com/auth0/lock/pull/2679) ([ankita10119](https://github.com/ankita10119))
14
+
15
+ **Fixed**
16
+ - fix: captcha not rendering for initial signup screen in classic login [\#2677](https://github.com/auth0/lock/pull/2677) ([paebanks](https://github.com/paebanks))
17
+
3
18
  ## [v14.1.0](https://github.com/auth0/lock/tree/v14.1.0) (2025-09-12)
4
19
  [Full Changelog](https://github.com/auth0/lock/compare/v14.0.0...v14.1.0)
5
20
 
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  ![Downloads](https://img.shields.io/npm/dw/auth0-lock)
4
4
  [![License](https://img.shields.io/:license-mit-blue.svg?style=flat)](https://opensource.org/licenses/MIT)
5
5
  [![Build Status](https://github.com/auth0/lock/actions/workflows/test.yml/badge.svg)](https://github.com/auth0/lock/actions/workflows/test.yml)
6
+ [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/auth0/lock)
6
7
 
7
8
  > :warning: Lock is built using React 18 from v12 onwards. Getting issues? Please [submit a bug report](https://github.com/auth0/lock/issues/new?assignees=&labels=bug+report,v12&template=report_a_bug.md&title=).
8
9
 
@@ -30,7 +31,7 @@ From CDN
30
31
 
31
32
  ```html
32
33
  <!-- Latest patch release (recommended for production) -->
33
- <script src="https://cdn.auth0.com/js/lock/14.1.0/lock.min.js"></script>
34
+ <script src="https://cdn.auth0.com/js/lock/14.2.1/lock.min.js"></script>
34
35
  ```
35
36
 
36
37
  ### Configure Auth0
@@ -137,4 +138,4 @@ Please do not report security vulnerabilities on the public GitHub issue tracker
137
138
  </p>
138
139
  <p align="center">
139
140
  This project is licensed under the MIT license. See the <a href="https://github.com/auth0/lock/blob/master/LICENSE"> LICENSE</a> file for more info.
140
- </p>
141
+ </p>
@@ -0,0 +1,9 @@
1
+ const JSDOMEnvironment = require('jest-environment-jsdom').default;
2
+
3
+ module.exports = class CustomJsdomEnvironment extends JSDOMEnvironment {
4
+ async setup() {
5
+ await super.setup();
6
+ // Expose jsdom instance globally so tests can call jsdom.reconfigure()
7
+ this.global.jsdom = this.dom;
8
+ }
9
+ };
@@ -54,10 +54,8 @@ describe('setup', function () {
54
54
  var model = mock.calls[0][1].toJS();
55
55
  expect(model.auth.redirectUrl).toBe('https://test.com/path/');
56
56
  });
57
- it.only('default redirectUrl should work when `window.location.origin` is not available', function () {
58
- jsdom.reconfigure({
59
- url: 'https://test.com/path/#not-this-part'
60
- });
57
+ it('default redirectUrl should work when `window.location.origin` is not available', function () {
58
+ (0, _testUtils.setURL)('https://test.com/path/#not-this-part');
61
59
  var options = {};
62
60
  setup('id', 'clientID', 'domain', options, 'hookRunner', 'emitEventFn');
63
61
  var _mockInit2 = mockInit,
@@ -13,7 +13,9 @@ var getSyncRemoteData = function getSyncRemoteData() {
13
13
  return require('core/remote_data').syncRemoteData;
14
14
  };
15
15
  jest.mock('sync', function () {
16
- return jest.fn();
16
+ return jest.fn(function (model, key, options) {
17
+ return model;
18
+ });
17
19
  });
18
20
  jest.mock('connection/enterprise', function () {
19
21
  return {
@@ -30,7 +32,11 @@ jest.mock('core/index', function () {
30
32
  id: function id() {
31
33
  return 'id';
32
34
  },
33
- emitEvent: jest.fn()
35
+ emitEvent: jest.fn(),
36
+ setCaptcha: jest.fn(),
37
+ setPasswordlessCaptcha: jest.fn(),
38
+ setPasswordResetCaptcha: jest.fn(),
39
+ setSignupChallenge: jest.fn()
34
40
  };
35
41
  });
36
42
  jest.mock('core/sso/data', function () {
@@ -40,6 +46,12 @@ jest.mock('core/sso/data', function () {
40
46
  })
41
47
  };
42
48
  });
49
+ jest.mock('core/web_api', function () {
50
+ return {
51
+ getChallenge: jest.fn(),
52
+ getSignupChallenge: jest.fn()
53
+ };
54
+ });
43
55
  describe('remote_data.syncRemoteData()', function () {
44
56
  beforeEach(function () {
45
57
  jest.clearAllMocks();
@@ -63,4 +75,123 @@ describe('remote_data.syncRemoteData()', function () {
63
75
  });
64
76
  });
65
77
  });
78
+ describe('captcha syncing', function () {
79
+ it('should sync login captcha when initialScreen is login', function () {
80
+ var syncRemoteData = getSyncRemoteData();
81
+ syncRemoteData('mockModel', 'login');
82
+ var syncCalls = require('sync').mock.calls;
83
+ var captchaCall = syncCalls.find(function (c) {
84
+ return c[1] === 'captcha';
85
+ });
86
+ var signupCaptchaCall = syncCalls.find(function (c) {
87
+ return c[1] === 'signupCaptcha';
88
+ });
89
+ expect(captchaCall).toBeDefined();
90
+ expect(signupCaptchaCall).toBeUndefined();
91
+ });
92
+ it('should sync signup captcha when initialScreen is signUp', function () {
93
+ var syncRemoteData = getSyncRemoteData();
94
+ syncRemoteData('mockModel', 'signUp');
95
+ var syncCalls = require('sync').mock.calls;
96
+ var captchaCall = syncCalls.find(function (c) {
97
+ return c[1] === 'captcha';
98
+ });
99
+ var signupCaptchaCall = syncCalls.find(function (c) {
100
+ return c[1] === 'signupCaptcha';
101
+ });
102
+ expect(captchaCall).toBeUndefined();
103
+ expect(signupCaptchaCall).toBeDefined();
104
+ });
105
+ it('should call webApi.getChallenge when login captcha syncFn is executed', function () {
106
+ var mockModel = {
107
+ get: jest.fn().mockReturnValue('test-id')
108
+ };
109
+ var syncRemoteData = getSyncRemoteData();
110
+ require('core/web_api').getChallenge.mockClear();
111
+ syncRemoteData(mockModel, 'login');
112
+ var syncCalls = require('sync').mock.calls;
113
+ var captchaCall = syncCalls.find(function (c) {
114
+ return c[1] === 'captcha';
115
+ });
116
+ var mockCallback = jest.fn();
117
+ captchaCall[2].syncFn(mockModel, mockCallback);
118
+ expect(require('core/web_api').getChallenge).toHaveBeenCalledWith('test-id', expect.any(Function));
119
+ });
120
+ it('should call webApi.getSignupChallenge when signup captcha syncFn is executed', function () {
121
+ var mockModel = {
122
+ get: jest.fn().mockReturnValue('test-id')
123
+ };
124
+ var syncRemoteData = getSyncRemoteData();
125
+ require('core/web_api').getSignupChallenge.mockClear();
126
+ syncRemoteData(mockModel, 'signUp');
127
+ var syncCalls = require('sync').mock.calls;
128
+ var signupCaptchaCall = syncCalls.find(function (c) {
129
+ return c[1] === 'signupCaptcha';
130
+ });
131
+ var mockCallback = jest.fn();
132
+ signupCaptchaCall[2].syncFn(mockModel, mockCallback);
133
+ expect(require('core/web_api').getSignupChallenge).toHaveBeenCalledWith('test-id', expect.any(Function));
134
+ });
135
+ it('should default to login captcha when no initialScreen is provided', function () {
136
+ var syncRemoteData = getSyncRemoteData();
137
+ syncRemoteData('mockModel'); // No second parameter
138
+
139
+ var syncCalls = require('sync').mock.calls;
140
+ var captchaCall = syncCalls.find(function (c) {
141
+ return c[1] === 'captcha';
142
+ });
143
+ var signupCaptchaCall = syncCalls.find(function (c) {
144
+ return c[1] === 'signupCaptcha';
145
+ });
146
+ expect(captchaCall).toBeDefined();
147
+ expect(signupCaptchaCall).toBeUndefined();
148
+ });
149
+ it('should not sync any captcha for other initialScreen values', function () {
150
+ var syncRemoteData = getSyncRemoteData();
151
+ syncRemoteData('mockModel', 'forgotPassword');
152
+ var syncCalls = require('sync').mock.calls;
153
+ var captchaCall = syncCalls.find(function (c) {
154
+ return c[1] === 'captcha';
155
+ });
156
+ var signupCaptchaCall = syncCalls.find(function (c) {
157
+ return c[1] === 'signupCaptcha';
158
+ });
159
+ expect(captchaCall).toBeUndefined();
160
+ expect(signupCaptchaCall).toBeUndefined();
161
+ });
162
+ it('should handle webApi.getSignupChallenge errors gracefully', function () {
163
+ var mockModel = {
164
+ get: jest.fn().mockReturnValue('test-id')
165
+ };
166
+ var syncRemoteData = getSyncRemoteData();
167
+ syncRemoteData(mockModel, 'signUp');
168
+ var syncCalls = require('sync').mock.calls;
169
+ var signupCaptchaCall = syncCalls.find(function (c) {
170
+ return c[1] === 'signupCaptcha';
171
+ });
172
+ require('core/web_api').getSignupChallenge.mockImplementation(function (id, cb) {
173
+ cb(new Error('404 Not Found'), null);
174
+ });
175
+ var mockCallback = jest.fn();
176
+ signupCaptchaCall[2].syncFn(mockModel, mockCallback);
177
+ expect(mockCallback).toHaveBeenCalledWith(null, null);
178
+ });
179
+ it('should handle webApi.getChallenge errors gracefully', function () {
180
+ var mockModel = {
181
+ get: jest.fn().mockReturnValue('test-id')
182
+ };
183
+ var syncRemoteData = getSyncRemoteData();
184
+ syncRemoteData(mockModel, 'login');
185
+ var syncCalls = require('sync').mock.calls;
186
+ var captchaCall = syncCalls.find(function (c) {
187
+ return c[1] === 'captcha';
188
+ });
189
+ require('core/web_api').getChallenge.mockImplementation(function (id, cb) {
190
+ cb(new Error('Network error'), null);
191
+ });
192
+ var mockCallback = jest.fn();
193
+ captchaCall[2].syncFn(mockModel, mockCallback);
194
+ expect(mockCallback).toHaveBeenCalledWith(null, null);
195
+ });
196
+ });
66
197
  });
@@ -1,15 +1,11 @@
1
1
  "use strict";
2
2
 
3
3
  var _web_api = _interopRequireDefault(require("../../core/web_api"));
4
+ var _testUtils = require("../testUtils");
4
5
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
5
- function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
6
- function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
7
- function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
8
- function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
9
- function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
10
- function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
11
6
  describe('Auth0WebApi', function () {
12
7
  var originalWindow;
8
+ var originalLocation;
13
9
  var LOCK_ID = 'lock-id';
14
10
  var CLIENT_ID = 'client-id';
15
11
  var DEFAULT_DOMAIN = 'test.com';
@@ -18,17 +14,16 @@ describe('Auth0WebApi', function () {
18
14
  };
19
15
  beforeEach(function () {
20
16
  originalWindow = window.window;
17
+ originalLocation = window.location;
21
18
  });
22
19
  afterEach(function () {
23
20
  window.window = originalWindow;
21
+ delete window.location;
22
+ window.location = originalLocation;
24
23
  });
25
24
  describe('setupClient', function () {
26
25
  it('sets the correct options when is on the hosted login page', function () {
27
- delete window.location;
28
- window.location = _objectSpread(_objectSpread({}, originalWindow.location), {}, {
29
- host: DEFAULT_DOMAIN,
30
- search: ''
31
- });
26
+ (0, _testUtils.setURL)("https://".concat(DEFAULT_DOMAIN, "/"));
32
27
  _web_api.default.setupClient(LOCK_ID, CLIENT_ID, DEFAULT_DOMAIN, {
33
28
  redirect: true
34
29
  });
@@ -41,40 +36,24 @@ describe('Auth0WebApi', function () {
41
36
  }));
42
37
  });
43
38
  it('sets redirect: true when on the same origin as the specified domain', function () {
44
- delete window.location;
45
- window.location = _objectSpread(_objectSpread({}, originalWindow.location), {}, {
46
- host: DEFAULT_DOMAIN,
47
- search: ''
48
- });
39
+ (0, _testUtils.setURL)("https://".concat(DEFAULT_DOMAIN, "/"));
49
40
  _web_api.default.setupClient(LOCK_ID, CLIENT_ID, DEFAULT_DOMAIN, {});
50
41
  expect(client().authOpt.popup).toBe(false);
51
42
  });
52
43
  it('sets redirect: false when on a different origin as the specified domain', function () {
53
- delete window.location;
54
- window.location = _objectSpread(_objectSpread({}, originalWindow.location), {}, {
55
- host: 'test-other.com',
56
- search: ''
57
- });
44
+ (0, _testUtils.setURL)('https://test-other.com/');
58
45
  _web_api.default.setupClient(LOCK_ID, CLIENT_ID, DEFAULT_DOMAIN, {});
59
46
  expect(client().authOpt.popup).toBe(true);
60
47
  });
61
48
  it('forces popup and sso mode for cordova, only when not running in the hosted environment', function () {
62
- delete window.location;
63
- window.location = _objectSpread(_objectSpread({}, originalWindow.location), {}, {
64
- host: DEFAULT_DOMAIN,
65
- search: ''
66
- });
49
+ (0, _testUtils.setURL)("https://".concat(DEFAULT_DOMAIN, "/"));
67
50
  window.cordova = true;
68
51
  _web_api.default.setupClient(LOCK_ID, CLIENT_ID, DEFAULT_DOMAIN, {});
69
52
  expect(client().authOpt.popup).toBe(false);
70
53
  expect(client().authOpt.sso).toBeUndefined();
71
54
  });
72
55
  it('forces popup and sso mode for electron, only when not running in the hosted environment', function () {
73
- delete window.location;
74
- window.location = _objectSpread(_objectSpread({}, originalWindow.location), {}, {
75
- host: DEFAULT_DOMAIN,
76
- search: ''
77
- });
56
+ (0, _testUtils.setURL)("https://".concat(DEFAULT_DOMAIN, "/"));
78
57
  window.electron = true;
79
58
  _web_api.default.setupClient(LOCK_ID, CLIENT_ID, DEFAULT_DOMAIN, {});
80
59
  expect(client().authOpt.popup).toBe(false);
@@ -61,11 +61,37 @@ var extractPropsFromWrapper = exports.extractPropsFromWrapper = function extract
61
61
  };
62
62
 
63
63
  // Newer (> Jest v22) versions don't allow modification of location.href
64
- // but can use `jsdom.reconfigure` when `jsdom` is exposed globally.
65
- // https://www.npmjs.com/package/jest-environment-jsdom-global
64
+ // Try jsdom.reconfigure() first (via custom environment), then fall back to property mocking
66
65
  var setURL = exports.setURL = function setURL(url) {
67
- jsdom.reconfigure({
68
- url: url
66
+ // Approach 1: Use jsdom.reconfigure() if available (cleanest)
67
+ if (global.jsdom && typeof global.jsdom.reconfigure === 'function') {
68
+ global.jsdom.reconfigure({
69
+ url: url
70
+ });
71
+ return;
72
+ }
73
+
74
+ // Approach 2: Fallback - Mock location properties with Object.defineProperty
75
+ var parsedUrl = new URL(url);
76
+ delete window.location;
77
+ Object.defineProperty(window, 'location', {
78
+ value: {
79
+ href: parsedUrl.href,
80
+ origin: parsedUrl.origin,
81
+ protocol: parsedUrl.protocol,
82
+ host: parsedUrl.host,
83
+ hostname: parsedUrl.hostname,
84
+ port: parsedUrl.port,
85
+ pathname: parsedUrl.pathname,
86
+ search: parsedUrl.search,
87
+ hash: parsedUrl.hash,
88
+ // Add toString for debugging
89
+ toString: function toString() {
90
+ return parsedUrl.href;
91
+ }
92
+ },
93
+ writable: true,
94
+ configurable: true
69
95
  });
70
96
  };
71
97
  var expectMockToMatch = exports.expectMockToMatch = function expectMockToMatch(_ref2, numberOfCalls) {
@@ -13,6 +13,11 @@ jest.mock('store/index', function () {
13
13
  jest.mock('ui/box/chrome', function () {
14
14
  return (0, _testUtils.mockComponent)('chrome');
15
15
  });
16
+ jest.mock('connection/database/index', function () {
17
+ return {
18
+ databaseUsernameValue: jest.fn()
19
+ };
20
+ });
16
21
  var mockEvent = {
17
22
  preventDefault: function preventDefault() {}
18
23
  };
@@ -77,14 +82,23 @@ describe('Container', function () {
77
82
  describe('with a custom `connectionResolver`', function () {
78
83
  var connectionResolverMock;
79
84
  var setResolvedConnectionMock;
85
+ var databaseUsernameValueMock;
80
86
  beforeEach(function () {
81
87
  connectionResolverMock = jest.fn();
82
88
  setResolvedConnectionMock = jest.fn();
89
+ databaseUsernameValueMock = require('connection/database/index').databaseUsernameValue;
90
+
91
+ // Set default return value for databaseUsernameValue mock
92
+ databaseUsernameValueMock.mockReturnValue('peter_picked@pickledpepper.com');
83
93
  require('core/index').connectionResolver = function () {
84
94
  return connectionResolverMock;
85
95
  };
86
96
  require('core/index').setResolvedConnection = setResolvedConnectionMock;
87
97
  });
98
+ afterEach(function () {
99
+ // Reset mock between tests and restore default return value
100
+ databaseUsernameValueMock.mockReset().mockReturnValue('peter_picked@pickledpepper.com');
101
+ });
88
102
  it('calls `connectionResolver` onSubmit', function () {
89
103
  var c = getContainer();
90
104
  c.handleSubmit(mockEvent);
@@ -111,6 +125,48 @@ describe('Container', function () {
111
125
  expect(mock.calls.length).toBe(1);
112
126
  expect(mock.calls[0]).toMatchSnapshot();
113
127
  });
128
+ it('prioritizes email over username on signUp screen', function () {
129
+ databaseUsernameValueMock.mockReturnValue('test@example.com');
130
+ var c = getContainer({
131
+ screenName: 'main.signUp'
132
+ });
133
+ c.handleSubmit(mockEvent);
134
+
135
+ // Should call databaseUsernameValue with emailFirst: true
136
+ expect(databaseUsernameValueMock).toHaveBeenCalledWith(expect.anything(), {
137
+ emailFirst: true
138
+ });
139
+
140
+ // connectionResolver should receive the email value
141
+ var _connectionResolverMo2 = connectionResolverMock,
142
+ mock = _connectionResolverMo2.mock;
143
+ expect(mock.calls[0][0]).toBe('test@example.com');
144
+ });
145
+ it('prioritizes username over email on login screen', function () {
146
+ databaseUsernameValueMock.mockReturnValue('testuser');
147
+ var c = getContainer({
148
+ screenName: 'main.login'
149
+ });
150
+ c.handleSubmit(mockEvent);
151
+
152
+ // Should call databaseUsernameValue with empty options (default behavior)
153
+ expect(databaseUsernameValueMock).toHaveBeenCalledWith(expect.anything(), {});
154
+
155
+ // connectionResolver should receive the username value
156
+ var _connectionResolverMo3 = connectionResolverMock,
157
+ mock = _connectionResolverMo3.mock;
158
+ expect(mock.calls[0][0]).toBe('testuser');
159
+ });
160
+ it('uses default behavior when screenName is not main.signUp', function () {
161
+ databaseUsernameValueMock.mockReturnValue('defaultvalue');
162
+ var c = getContainer({
163
+ screenName: 'forgotPassword'
164
+ });
165
+ c.handleSubmit(mockEvent);
166
+
167
+ // Should call databaseUsernameValue with empty options (default behavior)
168
+ expect(databaseUsernameValueMock).toHaveBeenCalledWith(expect.anything(), {});
169
+ });
114
170
  });
115
171
  describe('when suppressSubmitOverlay is true', function () {
116
172
  it('it does not display the overlay when submitting', function () {
@@ -34,7 +34,7 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol"
34
34
  function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
35
35
  function setupLock(id, clientID, domain, options, hookRunner, emitEventFn, handleEventFn) {
36
36
  var m = l.setup(id, clientID, domain, options, hookRunner, emitEventFn, handleEventFn);
37
- m = (0, _remote_data.syncRemoteData)(m);
37
+ m = (0, _remote_data.syncRemoteData)(m, options === null || options === void 0 ? void 0 : options.initialScreen);
38
38
  (0, _preload_utils.img)(l.ui.logo(m) || _container.defaultProps.logo);
39
39
  _web_api.default.setupClient(id, clientID, domain, l.withAuthOptions(m, _objectSpread(_objectSpread({}, options), {}, {
40
40
  popupOptions: l.ui.popupOptions(m)
@@ -19,6 +19,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
19
19
  // shouldn't depend on this
20
20
 
21
21
  function syncRemoteData(m) {
22
+ var initialScreen = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'login';
22
23
  if (l.useTenantInfo(m)) {
23
24
  m = (0, _sync.default)(m, 'client', {
24
25
  syncFn: function syncFn(m, cb) {
@@ -69,13 +70,24 @@ function syncRemoteData(m) {
69
70
  }
70
71
  }
71
72
  });
72
- m = (0, _sync.default)(m, 'captcha', {
73
- syncFn: function syncFn(m, cb) {
74
- _web_api.default.getChallenge(m.get('id'), function (err, r) {
75
- cb(null, r);
76
- });
77
- },
78
- successFn: _index2.setCaptcha
79
- });
73
+ if (initialScreen === 'login') {
74
+ m = (0, _sync.default)(m, 'captcha', {
75
+ syncFn: function syncFn(m, cb) {
76
+ _web_api.default.getChallenge(m.get('id'), function (err, r) {
77
+ cb(null, r);
78
+ });
79
+ },
80
+ successFn: _index2.setCaptcha
81
+ });
82
+ } else if (initialScreen === 'signUp') {
83
+ m = (0, _sync.default)(m, 'signupCaptcha', {
84
+ syncFn: function syncFn(m, cb) {
85
+ _web_api.default.getSignupChallenge(m.get('id'), function (err, r) {
86
+ cb(null, r);
87
+ });
88
+ },
89
+ successFn: _index2.setSignupChallenge
90
+ });
91
+ }
80
92
  return m;
81
93
  }
@@ -169,5 +169,5 @@ function trimAuthParams() {
169
169
  return p;
170
170
  }
171
171
  function getVersion() {
172
- return "14.1.0";
172
+ return "14.2.1";
173
173
  }
@@ -46,6 +46,11 @@ var CaptchaPane = exports.default = /*#__PURE__*/function (_React$Component) {
46
46
  var captcha = (0, _captcha2.getCaptchaConfig)(lock, flow);
47
47
  var value = (0, _index3.getFieldValue)(lock, 'captcha');
48
48
  var isValid = !(0, _index3.isFieldVisiblyInvalid)(lock, 'captcha');
49
+
50
+ // If no captcha config, don't render anything
51
+ if (!captcha) {
52
+ return null;
53
+ }
49
54
  var provider = captcha.get('provider');
50
55
  if ((0, _third_party_captcha.isThirdPartyCaptcha)(provider)) {
51
56
  function handleChange(value) {
package/lib/i18n.js CHANGED
@@ -90,7 +90,7 @@ function assertLanguage(m, language, base) {
90
90
  function syncLang(m, language, _cb) {
91
91
  (0, _cdn_utils.load)({
92
92
  method: 'registerLanguageDictionary',
93
- url: "".concat(l.languageBaseUrl(m), "/js/lock/").concat("14.1.0", "/").concat(language, ".js"),
93
+ url: "".concat(l.languageBaseUrl(m), "/js/lock/").concat("14.2.1", "/").concat(language, ".js"),
94
94
  check: function check(str) {
95
95
  return str && str === language;
96
96
  },
package/lib/lock.js CHANGED
@@ -36,7 +36,7 @@ var Auth0Lock = exports.default = /*#__PURE__*/function (_Core) {
36
36
  _inherits(Auth0Lock, _Core);
37
37
  return _createClass(Auth0Lock);
38
38
  }(_core.default); // telemetry
39
- Auth0Lock.version = "14.1.0";
39
+ Auth0Lock.version = "14.2.1";
40
40
 
41
41
  // TODO: should we have different telemetry for classic/passwordless?
42
42
  // TODO: should we set telemetry info before each request?
@@ -36,4 +36,4 @@ var Auth0LockPasswordless = exports.default = /*#__PURE__*/function (_Core) {
36
36
  _inherits(Auth0LockPasswordless, _Core);
37
37
  return _createClass(Auth0LockPasswordless);
38
38
  }(_core.default);
39
- Auth0LockPasswordless.version = "14.1.0";
39
+ Auth0LockPasswordless.version = "14.2.1";
@@ -11,6 +11,7 @@ var _button = require("./button");
11
11
  var l = _interopRequireWildcard(require("../../core/index"));
12
12
  var c = _interopRequireWildcard(require("../../field/index"));
13
13
  var _index3 = require("../../store/index");
14
+ var _index4 = require("../../connection/database/index");
14
15
  function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
15
16
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
16
17
  function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
@@ -109,7 +110,9 @@ var Container = exports.default = /*#__PURE__*/function (_React$Component) {
109
110
  return _createClass(Container, [{
110
111
  key: "checkConnectionResolver",
111
112
  value: function checkConnectionResolver(done) {
112
- var contentProps = this.props.contentProps;
113
+ var _this$props = this.props,
114
+ contentProps = _this$props.contentProps,
115
+ screenName = _this$props.screenName;
113
116
  var lock = contentProps.model;
114
117
  var connectionResolver = l.connectionResolver(lock);
115
118
  if (!connectionResolver) {
@@ -122,7 +125,13 @@ var Container = exports.default = /*#__PURE__*/function (_React$Component) {
122
125
  connections: connections,
123
126
  id: id
124
127
  };
125
- var userInputValue = c.getFieldValue(lock, 'username') || c.getFieldValue(lock, 'email');
128
+
129
+ // On signUp screen, use emailFirst option to prioritize email over username
130
+ // On login screen, use default behavior (username first)
131
+ var isSignUpScreen = screenName === 'main.signUp';
132
+ var userInputValue = (0, _index4.databaseUsernameValue)(lock, isSignUpScreen ? {
133
+ emailFirst: true
134
+ } : {});
126
135
  connectionResolver(userInputValue, context, function (resolvedConnection) {
127
136
  (0, _index3.swap)(_index3.updateEntity, 'lock', l.id(lock), function (m) {
128
137
  return l.setResolvedConnection(m, resolvedConnection);
@@ -177,9 +186,9 @@ var Container = exports.default = /*#__PURE__*/function (_React$Component) {
177
186
  }, {
178
187
  key: "handleClose",
179
188
  value: function handleClose() {
180
- var _this$props = this.props,
181
- closeHandler = _this$props.closeHandler,
182
- isSubmitting = _this$props.isSubmitting;
189
+ var _this$props2 = this.props,
190
+ closeHandler = _this$props2.closeHandler,
191
+ isSubmitting = _this$props2.isSubmitting;
183
192
  if (!isSubmitting) {
184
193
  closeHandler();
185
194
  }
@@ -187,9 +196,9 @@ var Container = exports.default = /*#__PURE__*/function (_React$Component) {
187
196
  }, {
188
197
  key: "handleEsc",
189
198
  value: function handleEsc() {
190
- var _this$props2 = this.props,
191
- closeHandler = _this$props2.closeHandler,
192
- escHandler = _this$props2.escHandler;
199
+ var _this$props3 = this.props,
200
+ closeHandler = _this$props3.closeHandler,
201
+ escHandler = _this$props3.escHandler;
193
202
  escHandler ? escHandler() : this.handleClose();
194
203
  }
195
204
  }, {
@@ -202,35 +211,35 @@ var Container = exports.default = /*#__PURE__*/function (_React$Component) {
202
211
  }, {
203
212
  key: "render",
204
213
  value: function render() {
205
- var _this$props3 = this.props,
206
- autofocus = _this$props3.autofocus,
207
- avatar = _this$props3.avatar,
208
- auxiliaryPane = _this$props3.auxiliaryPane,
209
- backHandler = _this$props3.backHandler,
210
- badgeLink = _this$props3.badgeLink,
211
- closeHandler = _this$props3.closeHandler,
212
- contentComponent = _this$props3.contentComponent,
213
- contentProps = _this$props3.contentProps,
214
- disableSubmitButton = _this$props3.disableSubmitButton,
215
- disallowClose = _this$props3.disallowClose,
216
- error = _this$props3.error,
217
- info = _this$props3.info,
218
- isMobile = _this$props3.isMobile,
219
- isModal = _this$props3.isModal,
220
- isSubmitting = _this$props3.isSubmitting,
221
- logo = _this$props3.logo,
222
- primaryColor = _this$props3.primaryColor,
223
- screenName = _this$props3.screenName,
224
- showBadge = _this$props3.showBadge,
225
- submitButtonLabel = _this$props3.submitButtonLabel,
226
- submitHandler = _this$props3.submitHandler,
227
- success = _this$props3.success,
228
- tabs = _this$props3.tabs,
229
- terms = _this$props3.terms,
230
- title = _this$props3.title,
231
- classNames = _this$props3.classNames,
232
- scrollGlobalMessagesIntoView = _this$props3.scrollGlobalMessagesIntoView,
233
- suppressSubmitOverlay = _this$props3.suppressSubmitOverlay;
214
+ var _this$props4 = this.props,
215
+ autofocus = _this$props4.autofocus,
216
+ avatar = _this$props4.avatar,
217
+ auxiliaryPane = _this$props4.auxiliaryPane,
218
+ backHandler = _this$props4.backHandler,
219
+ badgeLink = _this$props4.badgeLink,
220
+ closeHandler = _this$props4.closeHandler,
221
+ contentComponent = _this$props4.contentComponent,
222
+ contentProps = _this$props4.contentProps,
223
+ disableSubmitButton = _this$props4.disableSubmitButton,
224
+ disallowClose = _this$props4.disallowClose,
225
+ error = _this$props4.error,
226
+ info = _this$props4.info,
227
+ isMobile = _this$props4.isMobile,
228
+ isModal = _this$props4.isModal,
229
+ isSubmitting = _this$props4.isSubmitting,
230
+ logo = _this$props4.logo,
231
+ primaryColor = _this$props4.primaryColor,
232
+ screenName = _this$props4.screenName,
233
+ showBadge = _this$props4.showBadge,
234
+ submitButtonLabel = _this$props4.submitButtonLabel,
235
+ submitHandler = _this$props4.submitHandler,
236
+ success = _this$props4.success,
237
+ tabs = _this$props4.tabs,
238
+ terms = _this$props4.terms,
239
+ title = _this$props4.title,
240
+ classNames = _this$props4.classNames,
241
+ scrollGlobalMessagesIntoView = _this$props4.scrollGlobalMessagesIntoView,
242
+ suppressSubmitOverlay = _this$props4.suppressSubmitOverlay;
234
243
  var badge = showBadge ? /*#__PURE__*/_react.default.createElement(BottomBadge, {
235
244
  link: badgeLink
236
245
  }) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auth0-lock",
3
- "version": "14.1.0",
3
+ "version": "14.2.1",
4
4
  "description": "Auth0 Lock",
5
5
  "author": "Auth0 <support@auth0.com> (http://auth0.com)",
6
6
  "license": "MIT",
@@ -40,7 +40,7 @@
40
40
  "i18n:validate": "node -r esm scripts/lang-audit.js"
41
41
  },
42
42
  "devDependencies": {
43
- "@auth0/component-cdn-uploader": "^2.2.2",
43
+ "@auth0/component-cdn-uploader": "^2.4.2",
44
44
  "@babel/core": "^7.0.0",
45
45
  "@babel/eslint-parser": "^7.22.9",
46
46
  "@babel/plugin-proposal-class-properties": "^7.0.0",
@@ -94,8 +94,7 @@
94
94
  "grunt-webpack": "^5.0.0",
95
95
  "husky": "^7.0.2",
96
96
  "jest": "^29.3.1",
97
- "jest-environment-jsdom": "^29.3.1",
98
- "jest-environment-jsdom-global": "^4.0.0",
97
+ "jest-environment-jsdom": "^30.2.0",
99
98
  "karma": "^6.4.1",
100
99
  "karma-babel-preprocessor": "^8.0.2",
101
100
  "karma-browserify": "^8.1.0",
@@ -119,7 +118,7 @@
119
118
  "webpack-dev-server": "^4.11.1"
120
119
  },
121
120
  "dependencies": {
122
- "auth0-js": "^9.27.0",
121
+ "auth0-js": "^9.29.0",
123
122
  "auth0-password-policies": "^1.0.2",
124
123
  "blueimp-md5": "^2.19.0",
125
124
  "classnames": "^2.3.2",
@@ -135,7 +134,7 @@
135
134
  "react-transition-group": "^2.2.1",
136
135
  "trim": "^1.0.1",
137
136
  "url-join": "^1.1.0",
138
- "validator": "^13.6.0"
137
+ "validator": "^13.15.22"
139
138
  },
140
139
  "ccu": {
141
140
  "name": "lock",
@@ -178,7 +177,7 @@
178
177
  "lcov",
179
178
  "text-summary"
180
179
  ],
181
- "testEnvironment": "jest-environment-jsdom-global"
180
+ "testEnvironment": "<rootDir>/customJsdomEnvironment.js"
182
181
  },
183
182
  "lint-staged": {
184
183
  "*.{js,jsx}": [