Skip to content

Commit

Permalink
[SDK-4413] Support Organization Name (#669)
Browse files Browse the repository at this point in the history
  • Loading branch information
poovamraj authored Jul 17, 2023
1 parent ef24ca5 commit 1dc8875
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 16 deletions.
2 changes: 1 addition & 1 deletion EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -993,7 +993,7 @@ Note that Organizations is currently only available to customers on our Enterpri

```kotlin
WebAuthProvider.login(account)
.withOrganization(organizationId)
.withOrganization(organizationIdOrName)
.start(this, callback)
```

Expand Down
24 changes: 17 additions & 7 deletions auth0/src/main/java/com/auth0/android/provider/IdTokenVerifier.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,23 @@ internal class IdTokenVerifier {
throw NonceClaimMismatchException(verifyOptions.nonce, nonceClaim)
}
}
if (verifyOptions.organization != null) {
val orgClaim = token.organizationId
if (TextUtils.isEmpty(orgClaim)) {
throw OrgClaimMissingException()
}
if (verifyOptions.organization != orgClaim) {
throw OrgClaimMismatchException(verifyOptions.organization, orgClaim)
verifyOptions.organization?.let {organizationInput ->
if(organizationInput.startsWith("org_")) {
val orgClaim = token.organizationId
if (TextUtils.isEmpty(orgClaim)) {
throw OrgClaimMissingException()
}
if (organizationInput != orgClaim) {
throw OrgClaimMismatchException(organizationInput, orgClaim)
}
} else {
val orgNameClaim = token.organizationName
if (TextUtils.isEmpty(orgNameClaim)) {
throw OrgNameClaimMissingException()
}
if (!organizationInput.equals(orgNameClaim, true)) {
throw OrgNameClaimMismatchException(organizationInput, orgNameClaim)
}
}
}
if (audience.size > 1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,43 @@ public class OrgClaimMismatchException internal constructor(expected: String?, r
}
}

/**
* This Exception is thrown when Organization Name (org_name) claim is missing in the ID Token
*/
public class OrgNameClaimMissingException internal constructor() : TokenValidationException(MESSAGE) {
private companion object {
private const val MESSAGE = "Organization Name (org_name) claim must be a string present in the ID token"
}

/**
* To avoid backward compatibility issue, we still have the toString conversion similar to the
* old [TokenValidationException] that was thrown
*/
override fun toString(): String {
return "${this.javaClass.superclass.name}: $message"
}
}

/**
* This Exception is thrown when the Organization Name (org_name) claim found in the ID token is not the
* one that was expected
*/
public class OrgNameClaimMismatchException internal constructor(expected: String?, received: String?) :
TokenValidationException(message(expected, received)) {
private companion object {
private fun message(expected: String?, received: String?): String =
"Organization Name (org_name) claim mismatch in the ID token; expected \"$expected\", found \"$received\""
}

/**
* To avoid backward compatibility issue, we still have the toString conversion similar to the
* old [TokenValidationException] that was thrown
*/
override fun toString(): String {
return "${this.javaClass.superclass.name}: $message"
}
}

/**
* This Exception is thrown when Authorized Party (azp) claim is missing in the ID Token
*/
Expand Down
2 changes: 2 additions & 0 deletions auth0/src/main/java/com/auth0/android/request/internal/Jwt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ internal class Jwt(rawToken: String) {
val issuer: String?
val nonce: String?
val organizationId: String?
val organizationName: String?
val issuedAt: Date?
val expiresAt: Date?
val authorizedParty: String?
Expand All @@ -46,6 +47,7 @@ internal class Jwt(rawToken: String) {
issuer = decodedPayload["iss"] as String?
nonce = decodedPayload["nonce"] as String?
organizationId = decodedPayload["org_id"] as String?
organizationName = decodedPayload["org_name"] as String?
issuedAt = (decodedPayload["iat"] as? Double)?.let { Date(it.toLong() * 1000) }
expiresAt = (decodedPayload["exp"] as? Double)?.let { Date(it.toLong() * 1000) }
authorizedParty = decodedPayload["azp"] as String?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_AUDIENCE_ARRAY;
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_ISSUER;
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_NONCE;
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_ORGANIZATION;
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_ORGANIZATION_ID;
import static com.auth0.android.provider.JwtTestUtils.EXPECTED_ORGANIZATION_NAME;
import static com.auth0.android.provider.JwtTestUtils.FIXED_CLOCK_CURRENT_TIME_MS;
import static com.auth0.android.provider.JwtTestUtils.createJWTBody;
import static com.auth0.android.provider.JwtTestUtils.createTestJWT;
Expand Down Expand Up @@ -52,7 +53,7 @@ public void setUp() {
}

@Test
public void shouldPassAllClaimsVerification() throws Exception {
public void shouldPassAllClaimsVerificationWithOrgId() throws Exception {
long clock = FIXED_CLOCK_CURRENT_TIME_MS / 1000;
long authTime = clock - 1;

Expand All @@ -62,12 +63,33 @@ public void shouldPassAllClaimsVerification() throws Exception {
jwtBody.put("azp", EXPECTED_AUDIENCE);
jwtBody.put("auth_time", authTime);
jwtBody.put("nonce", EXPECTED_NONCE);
jwtBody.put("org_id", EXPECTED_ORGANIZATION);
jwtBody.put("org_id", EXPECTED_ORGANIZATION_ID);

String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setNonce(EXPECTED_NONCE);
options.setOrganization(EXPECTED_ORGANIZATION);
options.setOrganization(EXPECTED_ORGANIZATION_ID);
options.setMaxAge(60 * 2);
idTokenVerifier.verify(jwt, options, true);
}

@Test
public void shouldPassAllClaimsVerificationWithOrgName() throws Exception {
long clock = FIXED_CLOCK_CURRENT_TIME_MS / 1000;
long authTime = clock - 1;

Map<String, Object> jwtBody = createJWTBody();
//Overrides
jwtBody.put("aud", EXPECTED_AUDIENCE_ARRAY);
jwtBody.put("azp", EXPECTED_AUDIENCE);
jwtBody.put("auth_time", authTime);
jwtBody.put("nonce", EXPECTED_NONCE);
jwtBody.put("org_name", EXPECTED_ORGANIZATION_NAME);

String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setNonce(EXPECTED_NONCE);
options.setOrganization(EXPECTED_ORGANIZATION_NAME);
options.setMaxAge(60 * 2);
idTokenVerifier.verify(jwt, options, true);
}
Expand Down Expand Up @@ -236,6 +258,62 @@ public void shouldFailWhenNonceClaimIsRequiredAndHasUnexpectedValue() {
assertEquals(message, e.getMessage());
}

@Test
public void shouldNotFailWhenOrganizationNameClaimIsMissingButNotRequired() throws Exception {
Map<String, Object> jwtBody = createJWTBody("org_name");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
idTokenVerifier.verify(jwt, options, true);
}

@Test
public void shouldFailWhenOrganizationNameClaimIsMissingAndRequired() {
String message = "Organization Name (org_name) claim must be a string present in the ID token";
Exception e = Assert.assertThrows(message, OrgNameClaimMissingException.class, () -> {
Map<String, Object> jwtBody = createJWTBody("org_name");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION_NAME);
idTokenVerifier.verify(jwt, options, true);
});
assertEquals("com.auth0.android.provider.TokenValidationException: " + message, e.toString());
assertEquals(message, e.getMessage());
}

@Test
public void shouldFailWhenOrganizationNameClaimIsRequiredAndHasUnexpectedValue() {
String message = "Organization Name (org_name) claim mismatch in the ID token; expected \"__test_org_name__\", found \"--invalid--\"";
Exception e = Assert.assertThrows(message, OrgNameClaimMismatchException.class, () -> {
Map<String, Object> jwtBody = createJWTBody();
jwtBody.put("org_name", "--invalid--");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION_NAME);
idTokenVerifier.verify(jwt, options, true);
});
assertEquals("com.auth0.android.provider.TokenValidationException: " + message, e.toString());
assertEquals(message, e.getMessage());
}
@Test
public void shouldNotFailWhenOrganizationNameClaimIsRequiredAndHasSameValue() throws Exception {
Map<String, Object> jwtBody = createJWTBody();
jwtBody.put("org_name", EXPECTED_ORGANIZATION_NAME);
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION_NAME);
idTokenVerifier.verify(jwt, options, true);
}

@Test
public void shouldNotFailWhenOrganizationNameClaimIsRequiredAndHasSameValueInDifferentCase() throws Exception {
Map<String, Object> jwtBody = createJWTBody();
jwtBody.put("org_name", "__tESt_OrG_nAme__");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION_NAME);
idTokenVerifier.verify(jwt, options, true);
}

@Test
public void shouldNotFailWhenOrganizationIdClaimIsMissingButNotRequired() throws Exception {
Map<String, Object> jwtBody = createJWTBody("org_id");
Expand All @@ -251,7 +329,7 @@ public void shouldFailWhenOrganizationIdClaimIsMissingAndRequired() {
Map<String, Object> jwtBody = createJWTBody("org_id");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION);
options.setOrganization(EXPECTED_ORGANIZATION_ID);
idTokenVerifier.verify(jwt, options, true);
});
assertEquals("com.auth0.android.provider.TokenValidationException: " + message, e.toString());
Expand All @@ -260,13 +338,13 @@ public void shouldFailWhenOrganizationIdClaimIsMissingAndRequired() {

@Test
public void shouldFailWhenOrganizationIdClaimIsRequiredAndHasUnexpectedValue() {
String message = "Organization Id (org_id) claim mismatch in the ID token; expected \"__test_org_id__\", found \"--invalid--\"";
String message = "Organization Id (org_id) claim mismatch in the ID token; expected \"org___test_org_id__\", found \"--invalid--\"";
Exception e = Assert.assertThrows(message, OrgClaimMismatchException.class, () -> {
Map<String, Object> jwtBody = createJWTBody();
jwtBody.put("org_id", "--invalid--");
String token = createTestJWT("none", jwtBody);
Jwt jwt = new Jwt(token);
options.setOrganization(EXPECTED_ORGANIZATION);
options.setOrganization(EXPECTED_ORGANIZATION_ID);
idTokenVerifier.verify(jwt, options, true);
});
assertEquals("com.auth0.android.provider.TokenValidationException: " + message, e.toString());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ public class JwtTestUtils {
static final String[] EXPECTED_AUDIENCE_ARRAY = new String[]{"__test_client_id__", "__test_other_client_id__"};
static final String EXPECTED_AUDIENCE = "__test_client_id__";
static final String EXPECTED_NONCE = "__test_nonce__";
static final String EXPECTED_ORGANIZATION = "__test_org_id__";
static final String EXPECTED_ORGANIZATION_ID = "org___test_org_id__";
static final String EXPECTED_ORGANIZATION_NAME = "__test_org_name__";
static final Object EXPECTED_SUBJECT = "__test_subject__";

private static final String RSA_PRIVATE_KEY = "src/test/resources/rsa_private.pem";
Expand Down

0 comments on commit 1dc8875

Please sign in to comment.