Skip to content

Commit

Permalink
enhance(oidc): add more claims (#1172)
Browse files Browse the repository at this point in the history
  • Loading branch information
JordanSussman authored Sep 5, 2024
1 parent d0fa4f7 commit b998ec1
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 9 deletions.
3 changes: 3 additions & 0 deletions api/oi_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func GetOpenIDConfig(c *gin.Context) {
"iat",
"iss",
"aud",
"branch",
"build_number",
"build_id",
"repo",
Expand All @@ -54,6 +55,8 @@ func GetOpenIDConfig(c *gin.Context) {
"actor_scm_id",
"commands",
"image",
"image_name",
"image_tag",
"request",
"event",
"sha",
Expand Down
15 changes: 9 additions & 6 deletions api/types/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,21 @@ type OpenIDConfig struct {
// OpenIDClaims struct is an extension of the JWT standard claims. It
// includes information relevant to OIDC services.
type OpenIDClaims struct {
BuildNumber int `json:"build_number,omitempty"`
BuildID int64 `json:"build_id,omitempty"`
Actor string `json:"actor,omitempty"`
ActorSCMID string `json:"actor_scm_id,omitempty"`
Repo string `json:"repo,omitempty"`
TokenType string `json:"token_type,omitempty"`
Image string `json:"image,omitempty"`
Request string `json:"request,omitempty"`
Branch string `json:"branch,omitempty"`
BuildID int64 `json:"build_id,omitempty"`
BuildNumber int `json:"build_number,omitempty"`
Commands bool `json:"commands,omitempty"`
Event string `json:"event,omitempty"`
Image string `json:"image,omitempty"`
ImageName string `json:"image_name,omitempty"`
ImageTag string `json:"image_tag,omitempty"`
Ref string `json:"ref,omitempty"`
Repo string `json:"repo,omitempty"`
Request string `json:"request,omitempty"`
SHA string `json:"sha,omitempty"`
TokenType string `json:"token_type,omitempty"`
jwt.RegisteredClaims
}

Expand Down
32 changes: 29 additions & 3 deletions internal/token/mint.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"errors"
"fmt"
"strings"
"time"

"github.com/golang-jwt/jwt/v5"
Expand Down Expand Up @@ -120,7 +121,7 @@ func (tm *Manager) MintToken(mto *MintTokenOpts) (string, error) {

tk := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

//sign token with configured private signing key
// sign token with configured private signing key
token, err := tk.SignedString([]byte(tm.PrivateKeyHMAC))
if err != nil {
return "", fmt.Errorf("unable to sign token: %w", err)
Expand All @@ -134,6 +135,8 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab
// initialize claims struct
var claims = new(api.OpenIDClaims)

var err error

// validate provided claims
if len(mto.Repo) == 0 {
return "", errors.New("missing repo for ID token")
Expand All @@ -154,6 +157,7 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab
// set claims based on input
claims.Actor = mto.Build.GetSender()
claims.ActorSCMID = mto.Build.GetSenderSCMID()
claims.Branch = mto.Build.GetBranch()
claims.BuildNumber = mto.Build.GetNumber()
claims.BuildID = mto.Build.GetID()
claims.Repo = mto.Repo
Expand All @@ -164,6 +168,12 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab
claims.Audience = mto.Audience
claims.TokenType = mto.TokenType
claims.Image = mto.Image

claims.ImageName, claims.ImageTag, err = imageParse(mto.Image)
if err != nil {
return "", err
}

claims.Request = mto.Request
claims.Commands = mto.Commands

Expand All @@ -175,7 +185,7 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab
tk := jwt.NewWithClaims(jwt.SigningMethodRS256, claims)

// verify key is active in the database before signing
_, err := db.GetActiveJWK(ctx, tm.RSAKeySet.KID)
_, err = db.GetActiveJWK(ctx, tm.RSAKeySet.KID)
if err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return "", fmt.Errorf("unable to get active public key: %w", err)
Expand All @@ -191,7 +201,7 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab
// set KID header
tk.Header["kid"] = tm.RSAKeySet.KID

//sign token with configured private signing key
// sign token with configured private signing key
token, err := tk.SignedString(tm.RSAKeySet.PrivateKey)
if err != nil {
return "", fmt.Errorf("unable to sign token: %w", err)
Expand All @@ -201,3 +211,19 @@ func (tm *Manager) MintIDToken(ctx context.Context, mto *MintTokenOpts, db datab

return token, nil
}

// imageParse parses the given image string and returns the image name and tag.
// If no tag is provided in the image string, "latest" is used as the tag.
// If the image string is invalid, an error is returned.
func imageParse(image string) (string, string, error) {
parts := strings.Split(image, ":")

switch len(parts) {
case 1:
return image, "latest", nil
case 2:
return parts[0], parts[1], nil
default:
return "", "", fmt.Errorf("invalid image format: %s", image)
}
}
61 changes: 61 additions & 0 deletions internal/token/mint_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: Apache-2.0

package token

import "testing"

func Test_imageParse(t *testing.T) {
type args struct {
image string
}
tests := []struct {
name string
args args
wantName string
wantTag string
wantErr bool
}{
{
name: "image with tag",
args: args{
image: "alpine:1.20",
},
wantName: "alpine",
wantTag: "1.20",
wantErr: false,
},
{
name: "image without latest tag",
args: args{
image: "alpine:latest",
},
wantName: "alpine",
wantTag: "latest",
wantErr: false,
},
{
name: "image without tag",
args: args{
image: "alpine",
},
wantName: "alpine",
wantTag: "latest",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := imageParse(tt.args.image)
if (err != nil) != tt.wantErr {
t.Errorf("imageParse() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.wantName {
t.Errorf("imageParse() got = %v, wantName %v", got, tt.wantName)
}
if got1 != tt.wantTag {
t.Errorf("imageParse() got1 = %v, wantName %v", got1, tt.wantTag)
}
})
}
}
3 changes: 3 additions & 0 deletions mock/server/authentication.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const (
"iat",
"iss",
"aud",
"branch",
"build_number",
"build_id",
"repo",
Expand All @@ -40,6 +41,8 @@ const (
"actor_scm_id",
"commands",
"image",
"image_name",
"image_tag",
"request"
],
"id_token_signing_alg_values_supported": [
Expand Down

0 comments on commit b998ec1

Please sign in to comment.