Skip to content

Commit 619c77f

Browse files
committed
fix for extracting JWT subject
Signed-off-by: Brian DeHamer <[email protected]>
1 parent 21dd66d commit 619c77f

File tree

4 files changed

+72
-46
lines changed

4 files changed

+72
-46
lines changed

.changeset/rotten-goats-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@sigstore/sign': patch
3+
---
4+
5+
Fix for selecting correct OIDC claim when calculating proof-of-possession for Fulcio

package-lock.json

Lines changed: 35 additions & 35 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/sign/src/__tests__/util/oidc.test.ts

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ limitations under the License.
1616
import { extractJWTSubject } from '../../util/oidc';
1717

1818
describe('extractJWTSubject', () => {
19-
describe('when the JWT is issued by accounts.google.com', () => {
19+
describe('when the JWT has a verified email', () => {
2020
const payload = {
2121
iss: 'https://accounts.google.com',
2222
23+
email_verified: true,
2324
};
2425

2526
const jwt = `.${Buffer.from(JSON.stringify(payload)).toString('base64')}.`;
@@ -29,20 +30,23 @@ describe('extractJWTSubject', () => {
2930
});
3031
});
3132

32-
describe('when the JWT is issued by sigstore.dev', () => {
33+
describe('when the JWT has an unverified email', () => {
3334
const payload = {
3435
iss: 'https://oauth2.sigstore.dev/auth',
3536
37+
email_verified: false,
3638
};
3739

3840
const jwt = `.${Buffer.from(JSON.stringify(payload)).toString('base64')}.`;
3941

40-
it('should return the email address', () => {
41-
expect(extractJWTSubject(jwt)).toBe(payload.email);
42+
it('throws an error', () => {
43+
expect(() => extractJWTSubject(jwt)).toThrow(
44+
'JWT email not verified by issuer'
45+
);
4246
});
4347
});
4448

45-
describe('when the JWT is a generic JWT', () => {
49+
describe('when the JWT has a sub claim', () => {
4650
const payload = {
4751
iss: 'https://example.com',
4852
@@ -53,4 +57,15 @@ describe('extractJWTSubject', () => {
5357
expect(extractJWTSubject(jwt)).toBe(payload.sub);
5458
});
5559
});
60+
61+
describe('when the JWT has neither an email nor a sub claim', () => {
62+
const payload = {
63+
iss: 'https://example.com',
64+
};
65+
const jwt = `.${Buffer.from(JSON.stringify(payload)).toString('base64')}.`;
66+
67+
it('throws an error', () => {
68+
expect(() => extractJWTSubject(jwt)).toThrow('JWT subject not found');
69+
});
70+
});
5671
});

packages/sign/src/util/oidc.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,23 @@ type JWTSubject = {
1919
iss: string;
2020
sub: string;
2121
email: string;
22+
email_verified: boolean;
2223
};
2324

2425
export function extractJWTSubject(jwt: string): string {
2526
const parts = jwt.split('.', 3);
2627
const payload: JWTSubject = JSON.parse(enc.base64Decode(parts[1]));
2728

28-
switch (payload.iss) {
29-
case 'https://accounts.google.com':
30-
case 'https://oauth2.sigstore.dev/auth':
31-
return payload.email;
32-
default:
33-
return payload.sub;
29+
if (payload.email) {
30+
if (!payload.email_verified) {
31+
throw new Error('JWT email not verified by issuer');
32+
}
33+
return payload.email;
34+
}
35+
36+
if (payload.sub) {
37+
return payload.sub;
38+
} else {
39+
throw new Error('JWT subject not found');
3440
}
3541
}

0 commit comments

Comments
 (0)