Repository: auth0/node-auth0
Pull request #884 (2023-07-14 11:48:11)

Support Organization Name
### Changes

Adds support for the organization name by extending the ID Token validation as follows:

- If organization is set and has the `org_` prefix, we expect an org_id claim
- If organization is set and does not have the `org_` prefix, we expect an org_name claim

### 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
- [x] 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 existing and new tests complete without errors



Patch:
diff --git a/src/auth/idToken.js b/src/auth/idToken.js
index 67743b07d..4c439b864 100644
--- a/src/auth/idToken.js
+++ b/src/auth/idToken.js
@@ -84,14 +84,28 @@ const validate = function (token, options) {
 
   // Organization
   if (options.organization) {
-    if (!payload.org_id || typeof payload.org_id !== 'string') {
-      throw new Error('Organization Id (org_id) claim must be a string present in the ID token');
-    }
-
-    if (payload.org_id !== options.organization) {
-      throw new Error(
-        `Organization Id (org_id) claim value mismatch in the ID token; expected "${options.organization}", found "${payload.org_id}"'`
-      );
+    if (options.organization.indexOf('org_') === 0) {
+      if (!payload.org_id || typeof payload.org_id !== 'string') {
+        throw new Error('Organization Id (org_id) claim must be a string present in the ID token');
+      }
+
+      if (payload.org_id !== options.organization) {
+        throw new Error(
+          `Organization Id (org_id) claim value mismatch in the ID token; expected "${options.organization}", found "${payload.org_id}"'`
+        );
+      }
+    } else {
+      if (!payload.org_name || typeof payload.org_name !== 'string') {
+        throw new Error(
+          'Organization Name (org_name) claim must be a string present in the ID token'
+        );
+      }
+
+      if (payload.org_name.toLowerCase() !== options.organization.toLowerCase()) {
+        throw new Error(
+          `Organization Name (org_name) claim value mismatch in the ID token; expected "${options.organization}", found "${payload.org_name}"'`
+        );
+      }
     }
   }
 


Test patch:
diff --git a/test/idToken.tests.js b/test/idToken.tests.js
index 2ed53b3b9..0dcf9bf64 100644
--- a/test/idToken.tests.js
+++ b/test/idToken.tests.js
@@ -259,27 +259,55 @@ describe('idToken.validate', () => {
       'Authentication Time (auth_time) claim in the ID token indicates that too much time has passed since the last end-user authentication.'
     );
   });
-  it('should throw when organization is in options, but org_id missing from claim', () => {
-    expectedOptions.organization = 'testorg';
+  it('should throw when organization id is in options, but org_id missing from claim', () => {
+    expectedOptions.organization = 'org_123';
 
     expect(() => {
       idToken.validate(generateJWT({ org_id: undefined }), expectedOptions);
     }).to.throw('Organization Id (org_id) claim must be a string present in the ID token');
   });
-  it('should throw when org claim doesnt match org expected', () => {
+
+  it('should throw when organization name is in options, but org_name missing from claim', () => {
     expectedOptions.organization = 'testorg';
 
     expect(() => {
-      idToken.validate(generateJWT({ org_id: 'notExpectedOrg' }), expectedOptions);
+      idToken.validate(generateJWT({ org_name: undefined }), expectedOptions);
+    }).to.throw('Organization Name (org_name) claim must be a string present in the ID token');
+  });
+
+  it('should throw when org id claim doesnt match org expected', () => {
+    expectedOptions.organization = 'org_123';
+
+    expect(() => {
+      idToken.validate(generateJWT({ org_id: 'org_1234' }), expectedOptions);
     }).to.throw(
-      'Organization Id (org_id) claim value mismatch in the ID token; expected "testorg", found "notExpectedOrg'
+      'Organization Id (org_id) claim value mismatch in the ID token; expected "org_123", found "org_1234'
     );
   });
+
+  it('should throw when org name claim doesnt match org expected', () => {
+    expectedOptions.organization = 'testorg';
+
+    expect(() => {
+      idToken.validate(generateJWT({ org_name: 'notExpectedOrg' }), expectedOptions);
+    }).to.throw(
+      'Organization Name (org_name) claim value mismatch in the ID token; expected "testorg", found "notExpectedOrg'
+    );
+  });
+
   it('should NOT throw when org_id matches expected organization', () => {
+    expectedOptions.organization = 'org_123';
+
+    expect(() => {
+      idToken.validate(generateJWT({ org_id: 'org_123' }), expectedOptions);
+    }).not.to.throw();
+  });
+
+  it('should NOT throw when org_name matches expected organization', () => {
     expectedOptions.organization = 'testorg';
 
     expect(() => {
-      idToken.validate(generateJWT({ org_id: 'testorg' }), expectedOptions);
+      idToken.validate(generateJWT({ org_name: 'testorg' }), expectedOptions);
     }).not.to.throw();
   });
 });