Skip to content
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
5 changes: 5 additions & 0 deletions .changeset/rotten-goats-repeat.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sigstore/sign': patch
---

Fix for selecting correct OIDC claim when calculating proof-of-possession for Fulcio
70 changes: 35 additions & 35 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 20 additions & 5 deletions packages/sign/src/__tests__/util/oidc.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,11 @@ limitations under the License.
import { extractJWTSubject } from '../../util/oidc';

describe('extractJWTSubject', () => {
describe('when the JWT is issued by accounts.google.com', () => {
describe('when the JWT has a verified email', () => {
const payload = {
iss: 'https://accounts.google.com',
email: '[email protected]',
email_verified: true,
};

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

describe('when the JWT is issued by sigstore.dev', () => {
describe('when the JWT has an unverified email', () => {
const payload = {
iss: 'https://oauth2.sigstore.dev/auth',
email: '[email protected]',
email_verified: false,
};

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

it('should return the email address', () => {
expect(extractJWTSubject(jwt)).toBe(payload.email);
it('throws an error', () => {
expect(() => extractJWTSubject(jwt)).toThrow(
'JWT email not verified by issuer'
);
});
});

describe('when the JWT is a generic JWT', () => {
describe('when the JWT has a sub claim', () => {
const payload = {
iss: 'https://example.com',
sub: '[email protected]',
Expand All @@ -53,4 +57,15 @@ describe('extractJWTSubject', () => {
expect(extractJWTSubject(jwt)).toBe(payload.sub);
});
});

describe('when the JWT has neither an email nor a sub claim', () => {
const payload = {
iss: 'https://example.com',
};
const jwt = `.${Buffer.from(JSON.stringify(payload)).toString('base64')}.`;

it('throws an error', () => {
expect(() => extractJWTSubject(jwt)).toThrow('JWT subject not found');
});
});
});
18 changes: 12 additions & 6 deletions packages/sign/src/util/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,23 @@ type JWTSubject = {
iss: string;
sub: string;
email: string;
email_verified: boolean;
};

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

switch (payload.iss) {
case 'https://accounts.google.com':
case 'https://oauth2.sigstore.dev/auth':
return payload.email;
default:
return payload.sub;
if (payload.email) {
if (!payload.email_verified) {
throw new Error('JWT email not verified by issuer');
}
return payload.email;
}

if (payload.sub) {
return payload.sub;
} else {
throw new Error('JWT subject not found');
}
}
Loading