Repository: auth0/auth0.js
Pull request #1210 (2021-09-17 12:14:22)

[SDK-2754] Send X-Request-Language header to /passwordless/start
### Changes

This PR allows the developer to set `xRequestLanguage` when calling the `/passwordless/start` endpoint, which sets the `X-Request-Language` header. This allows the passwordless email template to be configured with different languages as desired.

### References

#1051

### Testing

Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this library has unit and/or integration testing, tests should be added for new functionality and existing tests should complete without errors.

- [x] This change adds unit test coverage
- [ ] This change adds integration test coverage

### Checklist

- [x] I have read the [Auth0 general contribution guidelines](https://github.com/auth0/open-source-template/blob/master/GENERAL-CONTRIBUTING.md)
- [x] I have read the [Auth0 Code of Conduct](https://github.com/auth0/open-source-template/blob/master/CODE-OF-CONDUCT.md)
- [x] All tests and linters described in the [Develop section](https://github.com/auth0/auth0.js#develop) run without errors



Patch:
diff --git a/README.md b/README.md
index ee02fa7..43b77e7 100644
--- a/README.md
+++ b/README.md
@@ -255,20 +255,6 @@ var auth0 = new auth0.Management({
 
 [Organizations](https://auth0.com/docs/organizations) is a set of features that provide better support for developers who build and maintain SaaS and Business-to-Business (B2B) applications.
 
-Using Organizations, you can:
-
-- Represent teams, business customers, partner companies, or any logical grouping of users that should have different ways of accessing your applications, as organizations.
-
-- Manage their membership in a variety of ways, including user invitation.
-
-- Configure branded, federated login flows for each organization.
-
-- Implement role-based access control, such that users can have different roles when authenticating in the context of different organizations.
-
-- Build administration capabilities into your products, using Organizations APIs, so that those businesses can manage their own organizations.
-
-Note that Organizations is currently only available to customers on our Enterprise and Startup subscription plans.
-
 ### Log in to an organization
 
 To log in to a specific organization, pass the ID of the organization as the `organization` parameter when creating the `WebAuth` client:
diff --git a/package-lock.json b/package-lock.json
index 25d3f60..828ad01 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10507,7 +10507,8 @@
       "version": "npm:paseto@3.0.1",
       "resolved": "https://registry.npmjs.org/paseto/-/paseto-3.0.1.tgz",
       "integrity": "sha512-K43Fi/W3vogewQHcHX+etF8Kx7Va037xLauTamK0AGTOLsE3CPW4jdRaeFvoaVIh6ybZiFg+nXVSc6ckIr0XVw==",
-      "dev": true
+      "dev": true,
+      "optional": true
     },
     "path-exists": {
       "version": "2.1.0",
diff --git a/package.json b/package.json
index b7274b7..a803a43 100644
--- a/package.json
+++ b/package.json
@@ -38,12 +38,11 @@
     "lint": "eslint ./src",
     "lint:fix": "eslint --fix ./src",
     "publish:cdn": "ccu --trace",
-    "jsdocs": "jsdoc --configure .jsdoc.json --verbose",
-    "precommit": "pretty-quick --staged"
+    "jsdocs": "jsdoc --configure .jsdoc.json --verbose"
   },
   "husky": {
     "hooks": {
-      "pre-commit": "lint-staged"
+      "pre-commit": "pretty-quick --staged && lint-staged"
     }
   },
   "lint-staged": {
diff --git a/src/authentication/passwordless-authentication.js b/src/authentication/passwordless-authentication.js
index 24acfb2..28848d8 100644
--- a/src/authentication/passwordless-authentication.js
+++ b/src/authentication/passwordless-authentication.js
@@ -122,6 +122,9 @@ PasswordlessAuthentication.prototype.start = function(options, cb) {
 
   url = urljoin(this.baseOptions.rootUrl, 'passwordless', 'start');
 
+  var xRequestLanguage = options.xRequestLanguage;
+  delete options.xRequestLanguage;
+
   body = objectHelper
     .merge(this.baseOptions, [
       'clientID',
@@ -154,8 +157,12 @@ PasswordlessAuthentication.prototype.start = function(options, cb) {
 
   body = objectHelper.toSnakeCase(body, ['auth0Client', 'authParams']);
 
+  var postOptions = xRequestLanguage
+    ? { xRequestLanguage: xRequestLanguage }
+    : undefined;
+
   return this.request
-    .post(url)
+    .post(url, postOptions)
     .send(body)
     .end(responseHandler(cb));
 };
diff --git a/src/helper/request-builder.js b/src/helper/request-builder.js
index 58b8de9..a17847e 100644
--- a/src/helper/request-builder.js
+++ b/src/helper/request-builder.js
@@ -88,6 +88,13 @@ RequestBuilder.prototype.setCommonConfiguration = function(
   var headers = this.headers;
   ongoingRequest = ongoingRequest.set('Content-Type', 'application/json');
 
+  if (options.xRequestLanguage) {
+    ongoingRequest = ongoingRequest.set(
+      'X-Request-Language',
+      options.xRequestLanguage
+    );
+  }
+
   var keys = Object.keys(this.headers);
 
   for (var a = 0; a < keys.length; a++) {
diff --git a/src/web-auth/index.js b/src/web-auth/index.js
index d98672d..2c22ac8 100644
--- a/src/web-auth/index.js
+++ b/src/web-auth/index.js
@@ -670,6 +670,7 @@ WebAuth.prototype.changePassword = function(options, cb) {
  * @param {String} [options.email] email where to send the `code` or `link`. This parameter is mutually exclusive with `phoneNumber`
  * @param {String} options.connection name of the passwordless connection
  * @param {Object} [options.authParams] additional Auth parameters when using `link`
+ * @param {Object} [options.xRequestLanguage] value for the X-Request-Language header. If not set, the language is detected using the client browser.
  * @param {Function} cb
  * @see   {@link https://auth0.com/docs/api/authentication#passwordless}
  * @memberof WebAuth.prototype


Test patch:
diff --git a/test/authentication/passwordless.test.js b/test/authentication/passwordless.test.js
index 3e091f9..66f7d1b 100644
--- a/test/authentication/passwordless.test.js
+++ b/test/authentication/passwordless.test.js
@@ -277,6 +277,64 @@ describe('auth0.authentication', function() {
         }
       );
     });
+
+    it('should call passwordless start with X-Request-Language header set', function(done) {
+      var auth0 = new Authentication({
+        domain: 'me.auth0.com',
+        clientID: '...',
+        redirectUri: 'will be overridden',
+        responseType: 'code',
+        _sendTelemetry: false,
+        scope: 'will be overridden'
+      });
+
+      sinon.stub(request, 'post').callsFake(function(url) {
+        expect(url).to.be('https://me.auth0.com/passwordless/start');
+        return new RequestMock({
+          body: {
+            client_id: '...',
+            connection: 'the_connection',
+            email: 'me@example.com',
+            send: 'code',
+            authParams: {
+              scope: 'openid email',
+              redirect_uri: 'http://page.com/othercallback',
+              response_type: 'token',
+              protocol: 'wsfed'
+            }
+          },
+          headers: {
+            'Content-Type': 'application/json',
+            'X-Request-Language': 'de-DE'
+          },
+          cb: function(cb) {
+            cb(null, {
+              body: {}
+            });
+          }
+        });
+      });
+
+      auth0.passwordless.start(
+        {
+          connection: 'the_connection',
+          email: 'me@example.com',
+          send: 'code',
+          authParams: {
+            redirectUri: 'http://page.com/othercallback',
+            protocol: 'wsfed',
+            responseType: 'token',
+            scope: 'openid email'
+          },
+          xRequestLanguage: 'de-DE'
+        },
+        function(err, data) {
+          expect(err).to.be(null);
+          expect(data).to.eql({});
+          done();
+        }
+      );
+    });
   });
 
   context('passwordless verify', function() {