Skip to content

Commit

Permalink
oauthflow: Add SubjectFromUnverifiedToken (sigstore#1826)
Browse files Browse the repository at this point in the history
This commit adds a SubjectFromUnverifiedToken function to accompany
SubjectFromToken. Unlike SubjectFromToken which accepts a token that is
verified against the issuing IDP, this function operates on the raw
bytes of the token. This is useful for clients or libraries that need to
know the subject of the token to sign proof of key possession over when
requesting a certificate from Fulcio, but do not want to verify the
token's signature as Fulcio will do so anyway.

Signed-off-by: Aditya Sirish <[email protected]>
  • Loading branch information
adityasaky authored Sep 3, 2024
1 parent b27128f commit 4c750b7
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 0 deletions.
10 changes: 10 additions & 0 deletions pkg/oauthflow/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,16 @@ func SubjectFromToken(tok *oidc.IDToken) (string, error) {
return subjectFromClaims(claims)
}

// SubjectFromUnverifiedToken extracts the subject claim from the raw bytes of
// an OIDC identity token.
func SubjectFromUnverifiedToken(tok []byte) (string, error) {
claims := claims{}
if err := json.Unmarshal(tok, &claims); err != nil {
return "", err
}
return subjectFromClaims(claims)
}

func subjectFromClaims(c claims) (string, error) {
if c.Email != "" {
if !c.Verified {
Expand Down
70 changes: 70 additions & 0 deletions pkg/oauthflow/flow_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,3 +267,73 @@ func TestSubjectFromToken(t *testing.T) {
})
}
}

func TestSubjectFromUnverifiedToken(t *testing.T) {
tests := map[string]struct {
Subject string
Email string
EmailVerified interface{}
ExpectedSubject string
WantErr bool
}{
`Email as subject`: {
Email: "[email protected]",
EmailVerified: true,
Subject: "foobar",
ExpectedSubject: "[email protected]",
WantErr: false,
},
`Subject as subject`: {
Subject: "foobar",
ExpectedSubject: "foobar",
WantErr: false,
},
`no email or subject`: {
WantErr: true,
},
`String email_verified value`: {
Email: "[email protected]",
EmailVerified: "true",
ExpectedSubject: "[email protected]",
WantErr: false,
},
`Email not verified`: {
Email: "[email protected]",
EmailVerified: false,
WantErr: true,
},
`invalid email_verified property`: {
Email: "[email protected]",
EmailVerified: "foo",
WantErr: true,
},
}
for name, test := range tests {
t.Run(name, func(t *testing.T) {
inputClaims := map[string]interface{}{
"email": test.Email,
"sub": test.Subject,
}

if test.EmailVerified != nil {
inputClaims["email_verified"] = test.EmailVerified
}

token, err := json.Marshal(inputClaims)
if err != nil {
t.Fatal(err)
}

subject, err := SubjectFromUnverifiedToken(token)
if err != nil {
if !test.WantErr {
t.Fatal("didn't expect error", err)
}
}

if subject != test.ExpectedSubject {
t.Errorf("got %v subject and expected %v", subject, test.Subject)
}
})
}
}

0 comments on commit 4c750b7

Please sign in to comment.