Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDK-4394] Add organization name validation #507

Merged
merged 1 commit into from
Jul 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion auth0/authentication/async_token_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ async def verify(
token (str): The JWT to verify.
nonce (str, optional): The nonce value sent during authentication.
max_age (int, optional): The max_age value sent during authentication.
organization (str, optional): The expected organization ID (org_id) claim value. This should be specified
organization (str, optional): The expected organization ID (org_id) or orgnization name (org_name) claim value. This should be specified
when logging in to an organization.

Returns:
Expand Down
36 changes: 25 additions & 11 deletions auth0/authentication/token_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ def verify(
token (str): The JWT to verify.
nonce (str, optional): The nonce value sent during authentication.
max_age (int, optional): The max_age value sent during authentication.
organization (str, optional): The expected organization ID (org_id) claim value. This should be specified
organization (str, optional): The expected organization ID (org_id) or orgnization name (org_name) claim value. This should be specified
when logging in to an organization.

Returns:
Expand Down Expand Up @@ -402,16 +402,30 @@ def _verify_payload(

# Organization
if organization:
if "org_id" not in payload or not isinstance(payload["org_id"], str):
raise TokenValidationError(
"Organization (org_id) claim must be a string present in the ID"
" token"
)
if payload["org_id"] != organization:
raise TokenValidationError(
"Organization (org_id) claim mismatch in the ID token; expected"
' "{}", found "{}"'.format(organization, payload["org_id"])
)
if organization.startswith("org_"):
if "org_id" not in payload or not isinstance(payload["org_id"], str):
raise TokenValidationError(
"Organization (org_id) claim must be a string present in the ID"
" token"
)
if payload["org_id"] != organization:
raise TokenValidationError(
"Organization (org_id) claim mismatch in the ID token; expected"
' "{}", found "{}"'.format(organization, payload["org_id"])
)
else:
if "org_name" not in payload or not isinstance(
payload["org_name"], str
):
raise TokenValidationError(
"Organization (org_name) claim must be a string present in the ID"
" token"
)
if payload["org_name"] != organization.lower():
raise TokenValidationError(
"Organization (org_name) claim mismatch in the ID token; expected"
' "{}", found "{}"'.format(organization, payload["org_name"])
)

# Authorized party
if isinstance(payload["aud"], list) and len(payload["aud"]) > 1:
Expand Down
47 changes: 44 additions & 3 deletions auth0/test/authentication/test_token_verifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,48 @@ def test_passes_when_org_present_and_matches(self):
tv._clock = MOCKED_CLOCK
tv.verify(token, organization="org_123")

def test_fails_when_org_specified_but_not_present(self):
def test_fails_when_org_name_specified_but_not_present(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MX0.wotJnUdD5IfdZMewF_-BnHc0pI56uwzwr5qaSXvSu9w"
self.assert_fails_with_error(
token,
"Organization (org_name) claim must be a string present in the ID token",
signature_verifier=SymmetricSignatureVerifier(HMAC_SHARED_SECRET),
organization="org-123",
)

def test_fails_when_org_name_specified_but_not_string(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJvcmdfbmFtZSI6NDIsImlzcyI6Imh0dHBzOi8vdG9rZW5zLXRlc3QuYXV0aDAuY29tLyIsImV4cCI6MTU4Nzc2NTM2MSwiaWF0IjoxNTg3NTkyNTYxfQ.RXu-dz1u2pftk_iInk1To8z9g1B6TVA-5FAwoCx85T0"
self.assert_fails_with_error(
token,
"Organization (org_name) claim must be a string present in the ID token",
signature_verifier=SymmetricSignatureVerifier(HMAC_SHARED_SECRET),
organization="org-123",
)

def test_fails_when_org_name_specified_but_does_not_match(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJvcmdfbmFtZSI6Im9yZy1hYmMiLCJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MX0.P_ldJGEaFg58cARwGMtog_KTsqv7cGJZXoS9xdTEkvQ"
self.assert_fails_with_error(
token,
'Organization (org_name) claim mismatch in the ID token; expected "org-123",'
' found "org-abc"',
signature_verifier=SymmetricSignatureVerifier(HMAC_SHARED_SECRET),
organization="org-123",
)

def test_succeeds_when_org_name_specified_matches(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJvcmdfbmFtZSI6Im9yZy0xMjMiLCJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MX0.P8Kba8Fgamyiw1qw_lBfp2OAzWn6NOLL6fBCDQhGvyc"
sv = SymmetricSignatureVerifier(HMAC_SHARED_SECRET)
tv = TokenVerifier(
signature_verifier=sv,
issuer=expectations["issuer"],
audience=expectations["audience"],
)
tv._clock = MOCKED_CLOCK
response = tv.verify(token)
self.assertIn("org_name", response)
self.assertEqual("org-123", response["org_name"])

def test_fails_when_org_id_specified_but_not_present(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MX0.wotJnUdD5IfdZMewF_-BnHc0pI56uwzwr5qaSXvSu9w"
self.assert_fails_with_error(
token,
Expand All @@ -515,7 +556,7 @@ def test_fails_when_org_specified_but_not_present(self):
organization="org_123",
)

def test_fails_when_org_specified_but_not_(self):
def test_fails_when_org_id_specified_but_not_string(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJvcmdfaWQiOjQyLCJpc3MiOiJodHRwczovL3Rva2Vucy10ZXN0LmF1dGgwLmNvbS8iLCJleHAiOjE1ODc3NjUzNjEsImlhdCI6MTU4NzU5MjU2MX0.fGL1_akaHikdovS7NRYla3flne1xdtCjP0ei_CRxO6k"
self.assert_fails_with_error(
token,
Expand All @@ -524,7 +565,7 @@ def test_fails_when_org_specified_but_not_(self):
organization="org_123",
)

def test_fails_when_org_specified_but_does_not_match(self):
def test_fails_when_org_id_specified_but_does_not_match(self):
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdXRoMHxzZGs0NThma3MiLCJhdWQiOiJ0b2tlbnMtdGVzdC0xMjMiLCJvcmdfaWQiOiJvcmdfMTIzIiwiaXNzIjoiaHR0cHM6Ly90b2tlbnMtdGVzdC5hdXRoMC5jb20vIiwiZXhwIjoxNTg3NzY1MzYxLCJpYXQiOjE1ODc1OTI1NjF9.hjSPgJpg0Dn2z0giCdGqVLD5Kmqy_yMYlSkgwKD7ahQ"
self.assert_fails_with_error(
token,
Expand Down