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
3 changes: 3 additions & 0 deletions internal/node/id_generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const (
DuoScheme = "DUO-"
OryScheme = "ORY-"
IdpScheme = "IDP-"
PingScheme = "PING-"
SelfScheme = "AGNTCY-"
)

Expand Down Expand Up @@ -65,6 +66,8 @@ func (g *idGenerator) GenerateFromProof(
scheme = OryScheme
case oidc.IdpProviderName:
scheme = IdpScheme
case oidc.PingProviderName:
scheme = PingScheme
Comment on lines +69 to +70

Copilot AI Jan 16, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new PingProviderName case in the switch statement lacks test coverage. The existing tests in id_service_test.go cover other providers like DuoProviderName and SelfProviderName, but there's no test case verifying that Ping provider generates IDs with the "PING-" prefix. Consider adding a test similar to TestGenerateID_Should_Not_Return_Errors but using oidc.PingProviderName to verify the ID generation for Ping providers.

Copilot uses AI. Check for mistakes.
case oidc.SelfProviderName:
scheme = SelfScheme
default:
Expand Down
38 changes: 31 additions & 7 deletions pkg/oidc/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const (
DuoProviderName
OryProviderName
IdpProviderName
PingProviderName
SelfProviderName
)

Expand All @@ -55,9 +56,11 @@ type providerMetadata struct {
JWKSURL string `json:"jwks_uri"`
}

const defaultCacheSize = 10 * 1024 * 1024 // 10MB
const defaultCacheExpiration = 24 // 24 hours
const defaultAcceptableSkew = 5 * time.Second // 5 seconds
const (
defaultCacheSize = 10 * 1024 * 1024 // 10MB
defaultCacheExpiration = 24 // 24 hours
defaultAcceptableSkew = 5 * time.Second // 5 seconds
)

type CachedJwks struct {
Jwks string
Expand Down Expand Up @@ -122,7 +125,9 @@ func (p *parser) VerifyJwt(ctx context.Context, parsedJwt *ParsedJWT) error {
}

// Verify the JWT signature
_, err = jws.Verify([]byte(*parsedJwt.jwt), jws.WithKeySet(jwks))
// The algorithm is inferred from the key automatically
// In some providers there is no "alg" specified in the JWKS
_, err = jws.Verify([]byte(*parsedJwt.jwt), jws.WithKeySet(jwks, jws.WithInferAlgorithmFromKey(true)))
if err != nil {
return err
}
Expand Down Expand Up @@ -205,9 +210,28 @@ func (p *parser) GetClaims(
return nil, errors.New("failed to decode JWT: missing 'iss' claim")
}

subject, ok := jwtToken.Subject()
if !ok {
return nil, errors.New("failed to decode JWT: missing 'sub' claim")
var subOk bool

var cidError, clientIDError error

var sub, cid, clientID string

// Get the subject from 'sub', 'cid' or 'client_id' claim
sub, subOk = jwtToken.Subject()
cidError = jwtToken.Get("cid", &cid)
clientIDError = jwtToken.Get("client_id", &clientID)

if !subOk && cidError != nil && clientIDError != nil {
return nil, errors.New("failed to decode JWT: missing 'sub', 'cid' or 'client_id' claim")
}

subject := sub
if subject == "" {
if cidError == nil && cid != "" {
subject = cid
} else if clientIDError == nil && clientID != "" {
subject = clientID
}
}

var subJWK map[string]any
Expand Down
9 changes: 9 additions & 0 deletions pkg/oidc/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ func (p *parser) detectProviderName(
return OktaProviderName, nil
case isDuo(headers, providerUrl.Host):
return DuoProviderName, nil
case isPing(headers, providerUrl.Host):
return PingProviderName, nil
default:
return IdpProviderName, nil
}
Expand All @@ -53,6 +55,13 @@ func isDuo(headers http.Header, host string) bool {
strings.HasPrefix(host, "duosecurity.com"))
}

func isPing(_ http.Header, host string) bool {
return strings.HasSuffix(host, "pingone.com") ||
strings.HasSuffix(host, "pingone.eu") ||
strings.HasSuffix(host, "pingone.com.au") ||
strings.HasSuffix(host, "pingone.ca")
}

func getProviderMetadata(ctx context.Context, issuer string) (*providerMetadata, error) {
metadata, oidcErr := getOidcProviderMetadata(ctx, issuer)
if oidcErr == nil {
Expand Down
Loading