Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/warm-spies-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': patch
---

Enforce the `azp` (authorized party) claim when `authorizedParties` is configured. Previously, a session token that was missing the `azp` claim was accepted even when `authorizedParties` was set, allowing the authorized-parties check to be bypassed by omitting the claim. Now, when `authorizedParties` is configured, a token with a missing or empty `azp` claim is rejected. Tokens without `azp` continue to be accepted when no `authorizedParties` are configured.
13 changes: 12 additions & 1 deletion packages/backend/src/jwt/__tests__/assertions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,10 +204,21 @@ describe('assertSubClaim(sub?)', () => {
});

describe('assertAuthorizedPartiesClaim(azp?, authorizedParties?)', () => {
it('does not throw if azp missing or empty', () => {
it('does not throw if azp missing or empty and no authorizedParties are configured', () => {
expect(() => assertAuthorizedPartiesClaim()).not.toThrow();
expect(() => assertAuthorizedPartiesClaim('')).not.toThrow();
expect(() => assertAuthorizedPartiesClaim(undefined)).not.toThrow();
expect(() => assertAuthorizedPartiesClaim('', [])).not.toThrow();
expect(() => assertAuthorizedPartiesClaim(undefined, [])).not.toThrow();
});

it('throws error if azp is missing or empty but authorizedParties are configured', () => {
expect(() => assertAuthorizedPartiesClaim('', ['azp-1'])).toThrow(
`Invalid JWT Authorized party claim (azp) "". Expected "azp-1".`,
);
expect(() => assertAuthorizedPartiesClaim(undefined, ['azp-1'])).toThrow(
`Invalid JWT Authorized party claim (azp) undefined. Expected "azp-1".`,
);
});

it('does not throw if authorizedParties missing or empty', () => {
Expand Down
10 changes: 8 additions & 2 deletions packages/backend/src/jwt/assertions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,17 @@ export const assertSubClaim = (sub?: string) => {
};

export const assertAuthorizedPartiesClaim = (azp?: string, authorizedParties?: string[]) => {
if (!azp || !authorizedParties || authorizedParties.length === 0) {
// When no authorized parties are configured there is nothing to enforce, so
// an azp-less token is accepted (a warning is surfaced elsewhere).
if (!authorizedParties || authorizedParties.length === 0) {
return;
}

if (!authorizedParties.includes(azp)) {
// Once authorized parties are configured the azp claim must be present and
// match one of them. Returning early on a missing/empty azp would let any
// token bypass the authorized-parties check simply by omitting the claim,
// defeating the purpose of configuring them.
if (!azp || !authorizedParties.includes(azp)) {
throw new TokenVerificationError({
reason: TokenVerificationErrorReason.TokenInvalidAuthorizedParties,
message: `Invalid JWT Authorized party claim (azp) ${JSON.stringify(azp)}. Expected "${authorizedParties}".`,
Expand Down
Loading