add new property IdentifierDefaultLogoTargetURI

Signed-off-by: Christian Richter <c.richter@opencloud.eu>
This commit is contained in:
Christian Richter
2025-04-16 13:58:02 +02:00
parent 515cd1f978
commit 16307e036d
41 changed files with 123 additions and 287 deletions

2
go.mod
View File

@@ -345,6 +345,8 @@ replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/pl
replace go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3
replace github.com/libregraph/lico => github.com/dragonchaser/lico v0.0.0-20250416114507-5a6cbb7004e8
// exclude the v2 line of go-sqlite3 which was released accidentally and prevents pulling in newer versions of go-sqlite3
// see https://github.com/mattn/go-sqlite3/issues/965 for more details
exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible

4
go.sum
View File

@@ -279,6 +279,8 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/dnsimple/dnsimple-go v0.63.0/go.mod h1:O5TJ0/U6r7AfT8niYNlmohpLbCSG+c71tQlGr9SeGrg=
github.com/dragonchaser/lico v0.0.0-20250416114507-5a6cbb7004e8 h1:VGvosYciKneejSTsOvbZ04Mjebz4trryst/cDL5cbMM=
github.com/dragonchaser/lico v0.0.0-20250416114507-5a6cbb7004e8/go.mod h1:GLIhLiUD3QUvbdZ+d7tKdkTwaotVQ3qhC8t1biWzFf8=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e h1:rcHHSQqzCgvlwP0I/fQ8rQMn/MpHE5gWSLdtpxtP6KQ=
@@ -725,8 +727,6 @@ github.com/leonelquinteros/gotext v1.7.1 h1:/JNPeE3lY5JeVYv2+KBpz39994W3W9fmZCGq
github.com/leonelquinteros/gotext v1.7.1/go.mod h1:I0WoFDn9u2D3VbPnnDPT8mzZu0iSXG8iih+AH2fHHqg=
github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk=
github.com/libregraph/idm v0.5.0/go.mod h1:BGMwIQ/6orJSPVzJ1x6kgG2JyG9GY05YFmbsnaD80k0=
github.com/libregraph/lico v0.65.1 h1:7ENAoAgbetZkJSwa1dMMP5WvXMTQ5E/3LI4uRDhwjEk=
github.com/libregraph/lico v0.65.1/go.mod h1:6w+kgoTYiXpJ7VriAaKJfeyF0eV/Stapd9pnK64du84=
github.com/libregraph/oidc-go v1.1.0 h1:RyudjL3UyQblqeBQI06W53PniWobqODeeyAy6v/HumA=
github.com/libregraph/oidc-go v1.1.0/go.mod h1:qW9ubcXvZrfbbWZBaLMuk7bt5qAUMYyt9/NtXQt07Cw=
github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=

View File

@@ -101,6 +101,7 @@ type Settings struct {
IdentifierScopesConf string `yaml:"-"` // unused
IdentifierDefaultBannerLogo string
IdentifierDefaultSignInPageText string `yaml:"default_sign_in_page_text" env:"IDP_DEFAULT_SIGNIN_PAGE_TEXT" desc:"" introductionVersion:"2.0.0"`
IdentifierDefaultLogoTargetURI string `yaml:"default_logo_target_uri" env:"IDP_DEFAULT_LOGO_TARGET_URI" desc:"Default logo target URI." introductionVersion:"%%NEXT%%"`
IdentifierDefaultUsernameHintText string
IdentifierUILocales []string

View File

@@ -58,6 +58,7 @@ func DefaultConfig() *config.Config {
IdentifierScopesConf: "",
IdentifierDefaultBannerLogo: "",
IdentifierDefaultSignInPageText: "",
IdentifierDefaultLogoTargetURI: "",
IdentifierDefaultUsernameHintText: "",
SigningKid: "private-key",
SigningMethod: "PS256",

View File

@@ -33,7 +33,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/longsleep/rndm"
"github.com/sirupsen/logrus"
@@ -260,6 +260,9 @@ func (bs *bootstrap) initialize(settings *Settings) error {
if settings.IdentifierDefaultSignInPageText != "" {
bs.config.IdentifierDefaultSignInPageText = &settings.IdentifierDefaultSignInPageText
}
if settings.IdentifierDefaultLogoTargetURI != "" {
bs.config.IdentifierDefaultLogoTargetURI = &settings.IdentifierDefaultLogoTargetURI
}
if settings.IdentifierDefaultUsernameHintText != "" {
bs.config.IdentifierDefaultUsernameHintText = &settings.IdentifierDefaultUsernameHintText
}

View File

@@ -24,7 +24,7 @@ import (
"net/http"
"net/url"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/config"
)
@@ -51,6 +51,7 @@ type Config struct {
IdentifierScopesConf string
IdentifierDefaultBannerLogo []byte
IdentifierDefaultSignInPageText *string
IdentifierDefaultLogoTargetURI *string
IdentifierDefaultUsernameHintText *string
IdentifierUILocales []string

View File

@@ -44,6 +44,7 @@ type Settings struct {
IdentifierScopesConf string
IdentifierDefaultBannerLogo string
IdentifierDefaultSignInPageText string
IdentifierDefaultLogoTargetURI string
IdentifierDefaultUsernameHintText string
IdentifierUILocales []string
SigningKid string

View File

@@ -16,10 +16,8 @@ import (
"strings"
"github.com/go-jose/go-jose/v3"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/sirupsen/logrus"
"github.com/libregraph/lico/signing"
)
func parseJSONWebKey(jsonBytes []byte) (*jose.JSONWebKey, error) {
@@ -297,7 +295,7 @@ func validateSigners(bs *bootstrap) error {
if !haveECDSA {
return fmt.Errorf("no private key for signing method: %s", bs.config.SigningMethod.Alg())
}
case *signing.SigningMethodEdwardsCurve:
case *jwt.SigningMethodEd25519:
if !haveEd25519 {
return fmt.Errorf("no private key for signing method: %s", bs.config.SigningMethod.Alg())
}

View File

@@ -20,7 +20,7 @@ package lico
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/oidc"
"github.com/libregraph/lico/oidc/payload"
@@ -67,7 +67,7 @@ const (
// AccessTokenClaims define the claims found in access tokens issued.
type AccessTokenClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
TokenType TokenTypeValue `json:"lg.t"`
@@ -81,20 +81,16 @@ type AccessTokenClaims struct {
*oidc.SessionClaims
}
// Valid implements the jwt.Claims interface.
func (c AccessTokenClaims) Valid() error {
if err := c.StandardClaims.Valid(); err != nil {
return err
// Validate implements the jwt.ClaimsValidator interface.
func (c AccessTokenClaims) Validate() error {
if !c.TokenType.Is(TokenTypeAccessToken) {
return errors.New("not an access token")
}
if c.IdentityClaims != nil {
if err := c.IdentityClaims.Valid(); err != nil {
return err
}
if len(c.Audience) != 1 {
return errors.New("access token must have exactly one audience value")
}
if c.TokenType.Is(TokenTypeAccessToken) {
return nil
}
return errors.New("not an access token")
return nil
}
// AuthorizedScopes returns a map with scope keys and true value of all scopes
@@ -110,7 +106,7 @@ func (c AccessTokenClaims) AuthorizedScopes() map[string]bool {
// RefreshTokenClaims define the claims used by refresh tokens.
type RefreshTokenClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
TokenType TokenTypeValue `json:"lg.t"`
@@ -123,20 +119,17 @@ type RefreshTokenClaims struct {
IdentityProvider string `json:"lg.p,omitempty"`
}
// Valid implements the jwt.Claims interface.
func (c RefreshTokenClaims) Valid() error {
if err := c.StandardClaims.Valid(); err != nil {
return err
// Validate implements the jwt.ClaimsValidator interface.
func (c RefreshTokenClaims) Validate() error {
if !c.TokenType.Is(TokenTypeRefreshToken) {
return errors.New("not a refresh token")
}
if c.IdentityClaims != nil {
if err := c.IdentityClaims.Valid(); err != nil {
return err
}
if len(c.Audience) != 1 {
return errors.New("refresh token must have exactly one audience value")
}
if c.TokenType.Is(TokenTypeRefreshToken) {
return nil
}
return errors.New("not a refresh token")
return nil
}
// NumericIDClaims define the claims used with the konnect/id scope.
@@ -147,8 +140,8 @@ type NumericIDClaims struct {
NumericIDUsername string `json:"username,omitempty"`
}
// Valid implements the jwt.Claims interface.
func (c NumericIDClaims) Valid() error {
// Validate implements the jwt.ClaimsValidator interface.
func (c NumericIDClaims) Validate() error {
if c.NumericIDUsername == "" {
return errors.New("username claim not valid")
}
@@ -160,8 +153,8 @@ type UniqueUserIDClaims struct {
UniqueUserID string `json:"lg.uuid,omitempty"`
}
// Valid implements the jwt.Claims interface.
func (c UniqueUserIDClaims) Valid() error {
// Validate implements the jwt.ClaimsValidator interface.
func (c UniqueUserIDClaims) Validate() error {
if c.UniqueUserID == "" {
return errors.New("lg.uuid claim not valid")
}

View File

@@ -21,7 +21,7 @@ import (
"context"
"net/http"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
// key is an unexported type for keys defined in this package.

View File

@@ -23,7 +23,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/identifier/meta"
"github.com/libregraph/lico/identity/clients"

View File

@@ -27,7 +27,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"
"golang.org/x/oauth2"

View File

@@ -22,7 +22,7 @@ import (
"errors"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
konnect "github.com/libregraph/lico"
"github.com/libregraph/lico/identifier/backends"

View File

@@ -20,7 +20,7 @@ package identity
import (
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/oidc/payload"
)

View File

@@ -24,7 +24,7 @@ import (
"net/http"
"net/url"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
)

View File

@@ -28,7 +28,7 @@ import (
"sync"
"github.com/go-jose/go-jose/v3"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/sirupsen/logrus"

View File

@@ -20,7 +20,7 @@ package identity
import (
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/oidc/payload"
)

View File

@@ -18,17 +18,12 @@
package clients
import (
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
// RegistrationClaims are claims used to with dynamic clients.
type RegistrationClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
*ClientRegistration
}
// Valid implements the jwt claims interface.
func (crc RegistrationClaims) Valid() error {
return crc.StandardClaims.Valid()
}

View File

@@ -25,7 +25,7 @@ import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/longsleep/rndm"
"github.com/mendsley/gojwk"
"golang.org/x/crypto/blake2b"
@@ -54,9 +54,9 @@ type ClientRegistration struct {
ImplicitScopes []string `yaml:"implicit_scopes" json:"-"`
Dynamic bool `yaml:"-" json:"-"`
IDIssuedAt int64 `yaml:"-" json:"-"`
SecretExpiresAt int64 `yaml:"-" json:"-"`
Dynamic bool `yaml:"-" json:"-"`
IDIssuedAt time.Time `yaml:"-" json:"-"`
SecretExpiresAt time.Time `yaml:"-" json:"-"`
Contacts []string `yaml:"contacts,flow" json:"contacts,omitempty"`
Name string `yaml:"name" json:"name,omitempty"`
@@ -153,9 +153,9 @@ func (cr *ClientRegistration) SetDynamic(ctx context.Context, creator func(ctx c
}
// Initialize basic client registration data for dynamic client.
cr.IDIssuedAt = time.Now().Unix()
cr.IDIssuedAt = time.Now()
if registry.dynamicClientSecretDuration > 0 {
cr.SecretExpiresAt = time.Now().Add(registry.dynamicClientSecretDuration).Unix()
cr.SecretExpiresAt = time.Now().Add(registry.dynamicClientSecretDuration)
}
cr.Dynamic = true
@@ -168,10 +168,10 @@ func (cr *ClientRegistration) SetDynamic(ctx context.Context, creator func(ctx c
// client_id. See https://openid.net/specs/openid-connect-registration-1_0.html#StatelessRegistration
// for more information. We use a JWT as client_id.
claims := &RegistrationClaims{
StandardClaims: jwt.StandardClaims{
RegisteredClaims: jwt.RegisteredClaims{
Subject: sub,
IssuedAt: cr.IDIssuedAt,
ExpiresAt: cr.SecretExpiresAt,
IssuedAt: jwt.NewNumericDate(cr.IDIssuedAt),
ExpiresAt: jwt.NewNumericDate(cr.SecretExpiresAt),
},
ClientRegistration: cr,
}

View File

@@ -27,7 +27,7 @@ import (
"sync"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v2"
@@ -358,7 +358,7 @@ func (r *Registry) getDynamicClient(clientID string) (*ClientRegistration, bool)
// TODO(longsleep): Add secure client secret.
registration = claims.ClientRegistration
registration.ID = clientID
registration.Secret = claims.StandardClaims.Subject
registration.Secret = claims.RegisteredClaims.Subject
registration.Dynamic = true
}
}

View File

@@ -27,7 +27,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"

View File

@@ -23,7 +23,7 @@ import (
"net/http"
"strings"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"

View File

@@ -22,7 +22,7 @@ import (
"fmt"
"net/http"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/gorilla/mux"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"

View File

@@ -415,10 +415,14 @@ func (im *IdentifierIdentityManager) EndSession(ctx context.Context, rw http.Res
origin := utils.OriginFromRequestHeaders(req.Header)
clientID := ""
if esr.IDTokenHint != nil {
// Extended request, verify IDTokenHint and its claims if available.
esrClaims = esr.IDTokenHint.Claims.(*konnectoidc.IDTokenClaims)
clientDetails, err = im.clients.Lookup(ctx, esrClaims.Audience, "", esr.PostLogoutRedirectURI, origin, true)
if len(esrClaims.Audience) == 1 {
clientID = esrClaims.Audience[0]
}
clientDetails, err = im.clients.Lookup(ctx, clientID, "", esr.PostLogoutRedirectURI, origin, true)
if err != nil {
// This error is not fatal since according to
// the spec in https://openid.net/specs/openid-connect-session-1_0.html#RPLogout the
@@ -471,7 +475,7 @@ func (im *IdentifierIdentityManager) EndSession(ctx context.Context, rw http.Res
query.Add("flow", identifier.FlowOIDC)
}
if esrClaims != nil {
query.Add("client_id", esrClaims.Audience)
query.Add("client_id", clientID)
}
u.RawQuery = query.Encode()

View File

@@ -18,7 +18,7 @@
package identity
import (
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
// User defines a most simple user with an id defined as subject.

View File

@@ -20,7 +20,7 @@ package identity
import (
"fmt"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
konnectoidc "github.com/libregraph/lico/oidc"

View File

@@ -18,12 +18,12 @@
package oidc
import (
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
// IDTokenClaims define the claims found in OIDC ID Tokens.
type IDTokenClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
Nonce string `json:"nonce,omitempty"`
AuthTime int64 `json:"auth_time,omitempty"`
@@ -36,14 +36,10 @@ type IDTokenClaims struct {
*SessionClaims
}
// Valid implements the jwt.Claims interface.
func (c IDTokenClaims) Valid() (err error) {
return c.StandardClaims.Valid()
}
// ProfileClaims define the claims for the OIDC profile scope.
// https://openid.net/specs/openid-connect-basic-1_0.html#Scopes
type ProfileClaims struct {
jwt.RegisteredClaims
Name string `json:"name,omitempty"`
FamilyName string `json:"family_name,omitempty"`
GivenName string `json:"given_name,omitempty"`
@@ -60,14 +56,10 @@ func NewProfileClaims(claims jwt.Claims) *ProfileClaims {
return claims.(*ProfileClaims)
}
// Valid implements the jwt.Claims interface.
func (c ProfileClaims) Valid() error {
return nil
}
// EmailClaims define the claims for the OIDC email scope.
// https://openid.net/specs/openid-connect-basic-1_0.html#Scopes
type EmailClaims struct {
jwt.RegisteredClaims
Email string `json:"email,omitempty"`
EmailVerified bool `json:"email_verified"`
}
@@ -82,22 +74,12 @@ func NewEmailClaims(claims jwt.Claims) *EmailClaims {
return claims.(*EmailClaims)
}
// Valid implements the jwt.Claims interface.
func (c EmailClaims) Valid() error {
return nil
}
// UserInfoClaims define the claims defined by the OIDC UserInfo
// endpoint.
type UserInfoClaims struct {
Subject string `json:"sub,omitempty"`
}
// Valid implements the jwt.Claims interface.
func (c UserInfoClaims) Valid() error {
return nil
}
// SessionClaims define claims related to front end sessions, for example as
// specified by https://openid.net/specs/openid-connect-frontchannel-1_0.html
type SessionClaims struct {

View File

@@ -25,7 +25,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
konnectoidc "github.com/libregraph/lico/oidc"
@@ -337,9 +337,7 @@ func (ar *AuthenticationRequest) Validate(keyFunc jwt.Keyfunc) error {
}
if ar.RawIDTokenHint != "" {
parser := &jwt.Parser{
SkipClaimsValidation: true,
}
parser := jwt.NewParser(jwt.WithoutClaimsValidation())
idTokenHint, err := parser.ParseWithClaims(ar.RawIDTokenHint, &konnectoidc.IDTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
if keyFunc != nil {
return keyFunc(token)

View File

@@ -22,7 +22,7 @@ import (
"net/http"
"net/url"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
konnectoidc "github.com/libregraph/lico/oidc"
@@ -67,9 +67,7 @@ func NewEndSessionRequest(values url.Values, providerMetadata *oidc.WellKnown) (
// Validate validates the request data of the accociated endSession request.
func (esr *EndSessionRequest) Validate(keyFunc jwt.Keyfunc) error {
if esr.RawIDTokenHint != "" {
parser := &jwt.Parser{
SkipClaimsValidation: true,
}
parser := jwt.NewParser(jwt.WithoutClaimsValidation())
idTokenHint, err := parser.ParseWithClaims(esr.RawIDTokenHint, &konnectoidc.IDTokenClaims{}, func(token *jwt.Token) (interface{}, error) {
if keyFunc != nil {
return keyFunc(token)

View File

@@ -24,7 +24,7 @@ import (
"net/url"
"strings"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/mendsley/gojwk"

View File

@@ -20,7 +20,7 @@ package payload
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/identity/clients"
)
@@ -30,7 +30,7 @@ import (
// requests specified at
// https://openid.net/specs/openid-connect-core-1_0.html#JWTRequests
type RequestObjectClaims struct {
jwt.StandardClaims
jwt.RegisteredClaims
RawScope string `json:"scope"`
Claims *ClaimsRequest `json:"claims"`

View File

@@ -24,7 +24,7 @@ import (
"net/url"
"strings"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
konnectoidc "github.com/libregraph/lico/oidc"

View File

@@ -24,7 +24,7 @@ import (
"strings"
"github.com/go-jose/go-jose/v3"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"
"github.com/sirupsen/logrus"
@@ -429,14 +429,14 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
claims := tr.RefreshToken.Claims.(*konnect.RefreshTokenClaims)
// Ensure that the authorization code was issued to the client id.
if claims.Audience != tr.ClientID {
if claims.Audience[0] != tr.ClientID {
err = konnectoidc.NewOAuth2Error(oidc.ErrorCodeOAuth2InvalidGrant, "client_id mismatch")
goto done
}
// TODO(longsleep): Compare standard claims issuer.
userID, sessionRef := p.getUserIDAndSessionRefFromClaims(&claims.StandardClaims, nil, claims.IdentityClaims)
userID, sessionRef := p.getUserIDAndSessionRefFromClaims(&claims.RegisteredClaims, nil, claims.IdentityClaims)
if userID == "" {
err = konnectoidc.NewOAuth2Error(oidc.ErrorCodeOAuth2InvalidToken, "missing data in kc.identity claim")
goto done
@@ -496,7 +496,7 @@ func (p *Provider) TokenHandler(rw http.ResponseWriter, req *http.Request) {
// Create fake request for token generation.
ar = &payload.AuthenticationRequest{
ClientID: claims.Audience,
ClientID: claims.Audience[0],
}
// Remember refresh token claims, for use in access and id token generators later on.
@@ -602,7 +602,7 @@ func (p *Provider) UserInfoHandler(rw http.ResponseWriter, req *http.Request) {
var requestedClaimsMap []*payload.ClaimsRequestMap
var authorizedScopes map[string]bool
userID, sessionRef := p.getUserIDAndSessionRefFromClaims(&claims.StandardClaims, claims.SessionClaims, claims.IdentityClaims)
userID, sessionRef := p.getUserIDAndSessionRefFromClaims(&claims.RegisteredClaims, claims.SessionClaims, claims.IdentityClaims)
ctx := konnect.NewClaimsContext(konnect.NewRequestContext(req.Context(), req), claims)
@@ -628,7 +628,7 @@ func (p *Provider) UserInfoHandler(rw http.ResponseWriter, req *http.Request) {
found = false
}
if !found {
p.logger.WithField("sub", claims.StandardClaims.Subject).Debugln("userinfo request user not found")
p.logger.WithField("sub", claims.RegisteredClaims.Subject).Debugln("userinfo request user not found")
p.ErrorPage(rw, http.StatusNotFound, "", "user not found")
return
}
@@ -721,7 +721,7 @@ done:
// Support returning signed user info if the registered client requested it
// as specified in https://openid.net/specs/openid-connect-core-1_0.html#UserInfoResponse and
// https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata
registration, _ := p.clients.Get(ctx, claims.Audience)
registration, _ := p.clients.Get(ctx, claims.Audience[0])
if registration != nil {
if registration.RawUserInfoSignedResponseAlg != "" {
// Get alg.
@@ -934,8 +934,8 @@ done:
ClientID: cr.ID,
ClientSecret: cr.Secret,
ClientIDIssuedAt: cr.IDIssuedAt,
ClientSecretExpiresAt: cr.SecretExpiresAt,
ClientIDIssuedAt: cr.IDIssuedAt.Unix(),
ClientSecretExpiresAt: cr.SecretExpiresAt.Unix(),
ClientRegistrationRequest: *crr,
}

View File

@@ -20,7 +20,7 @@ package provider
import (
"errors"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/lico/identity"
"github.com/libregraph/lico/oidc/payload"

View File

@@ -29,7 +29,7 @@ import (
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/rs/cors"
"github.com/sirupsen/logrus"
@@ -42,7 +42,6 @@ import (
"github.com/libregraph/lico/managers"
konnectoidc "github.com/libregraph/lico/oidc"
"github.com/libregraph/lico/oidc/code"
"github.com/libregraph/lico/signing"
"github.com/libregraph/lico/utils"
)
@@ -208,7 +207,7 @@ func (p *Provider) SetSigningKey(id string, key crypto.Signer) error {
case *ecdsa.PrivateKey:
signingMethod = jwt.SigningMethodES256
case ed25519.PrivateKey:
signingMethod = signing.SigningMethodEdDSA
signingMethod = jwt.SigningMethodEdDSA
default:
return fmt.Errorf("unsupported signer type: %v", s)
}
@@ -307,7 +306,7 @@ func (p *Provider) SetSigningKey(id string, key crypto.Signer) error {
PrivateKey: key,
SigningMethod: jwt.SigningMethodPS512,
}
case *signing.SigningMethodEdwardsCurve:
case *jwt.SigningMethodEd25519:
p.signingKeys[signingMethod] = &SigningKey{
ID: id,
PrivateKey: key,
@@ -429,7 +428,7 @@ func (p *Provider) InitializeMetadata() error {
jwt.SigningMethodPS384.Alg(),
jwt.SigningMethodPS512.Alg(),
jwt.SigningMethodNone.Alg(),
signing.SigningMethodEdDSA.Alg(),
jwt.SigningMethodEdDSA.Alg(),
}
p.metadata.TokenEndpointAuthMethodsSupported = []string{
oidc.AuthMethodClientSecretBasic,

View File

@@ -23,7 +23,7 @@ import (
"encoding/gob"
"net/http"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/longsleep/rndm"
konnect "github.com/libregraph/lico"
@@ -112,11 +112,16 @@ func (p *Provider) unserializeSession(value string) (*payload.Session, error) {
return &session, nil
}
func (p *Provider) getUserIDAndSessionRefFromClaims(claims *jwt.StandardClaims, sessionClaims *oidc.SessionClaims, identityClaims jwt.MapClaims) (string, *string) {
func (p *Provider) getUserIDAndSessionRefFromClaims(claims *jwt.RegisteredClaims, sessionClaims *oidc.SessionClaims, identityClaims jwt.MapClaims) (string, *string) {
if claims == nil || identityClaims == nil {
return "", nil
}
if len(claims.Audience) != 1 {
return "", nil
}
audience := claims.Audience[0]
userIDClaim, _ := identityClaims[konnect.IdentifiedUserIDClaim].(string)
if userIDClaim == "" {
return userIDClaim, nil
@@ -136,5 +141,5 @@ func (p *Provider) getUserIDAndSessionRefFromClaims(claims *jwt.StandardClaims,
// NOTE(longsleep): Return the userID from claims and generate a session ref
// for it. Session refs use the userClaim if available and set by the
// underlaying backend.
return userIDClaim, identity.GetSessionRef(p.identityManager.Name(), claims.Audience, userClaim)
return userIDClaim, identity.GetSessionRef(p.identityManager.Name(), audience, userClaim)
}

View File

@@ -20,7 +20,7 @@ package provider
import (
"crypto"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
)
// A SigningKey bundles a signer with meta data and a signign method.

View File

@@ -22,7 +22,7 @@ import (
"fmt"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/golang-jwt/jwt/v5"
"github.com/libregraph/oidc-go"
"github.com/longsleep/rndm"
@@ -51,13 +51,13 @@ func (p *Provider) makeAccessToken(ctx context.Context, audience string, auth id
TokenType: konnect.TokenTypeAccessToken,
AuthorizedScopesList: authorizedScopesList,
AuthorizedClaimsRequest: auth.AuthorizedClaims(),
StandardClaims: jwt.StandardClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: p.issuerIdentifier,
Subject: auth.Subject(),
Audience: audience,
ExpiresAt: time.Now().Add(p.accessTokenDuration).Unix(),
IssuedAt: time.Now().Unix(),
Id: rndm.GenerateRandomString(24),
Audience: jwt.ClaimStrings{audience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(p.accessTokenDuration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: rndm.GenerateRandomString(24),
},
}
@@ -137,12 +137,12 @@ func (p *Provider) makeIDToken(ctx context.Context, ar *payload.AuthenticationRe
idTokenClaims := &konnectoidc.IDTokenClaims{
Nonce: ar.Nonce,
StandardClaims: jwt.StandardClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: p.issuerIdentifier,
Subject: publicSubject,
Audience: ar.ClientID,
ExpiresAt: time.Now().Add(p.idTokenDuration).Unix(),
IssuedAt: time.Now().Unix(),
Audience: jwt.ClaimStrings{ar.ClientID},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(p.idTokenDuration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
},
}
@@ -327,13 +327,13 @@ func (p *Provider) makeRefreshToken(ctx context.Context, audience string, auth i
ApprovedScopesList: approvedScopesList,
ApprovedClaimsRequest: auth.AuthorizedClaims(),
Ref: ref,
StandardClaims: jwt.StandardClaims{
RegisteredClaims: jwt.RegisteredClaims{
Issuer: p.issuerIdentifier,
Subject: auth.Subject(),
Audience: audience,
ExpiresAt: time.Now().Add(p.refreshTokenDuration).Unix(),
IssuedAt: time.Now().Unix(),
Id: rndm.GenerateRandomString(24),
Audience: jwt.ClaimStrings{audience},
ExpiresAt: jwt.NewNumericDate(time.Now().Add(p.refreshTokenDuration)),
IssuedAt: jwt.NewNumericDate(time.Now()),
ID: rndm.GenerateRandomString(24),
},
}

View File

@@ -1,44 +0,0 @@
/*
* Copyright 2017-2019 Kopano and its licensors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package signing
import (
"crypto"
"encoding/base64"
jwk "github.com/mendsley/gojwk"
"golang.org/x/crypto/ed25519"
)
// JWKFromPublicKey creates a JWK from a public key
func JWKFromPublicKey(key crypto.PublicKey) (*jwk.Key, error) {
switch key := key.(type) {
case ed25519.PublicKey:
jwt := &jwk.Key{
Kty: "OKP",
Crv: "Ed25519",
X: base64.RawURLEncoding.EncodeToString(key),
}
return jwt, nil
default:
return jwk.PublicKey(key)
}
}

View File

@@ -1,101 +0,0 @@
/*
* Copyright 2017-2019 Kopano and its licensors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package signing
import (
"crypto"
"crypto/rand"
"errors"
"github.com/golang-jwt/jwt/v4"
"golang.org/x/crypto/ed25519"
)
// Errors used by this package.
var (
ErrEdDSAVerification = errors.New("eddsa: verification error")
)
// SigningMethodEdwardsCurve implements the EdDSA family of signing methods.
type SigningMethodEdwardsCurve struct {
Name string
}
// Specific instances for EdDSA
var (
SigningMethodEdDSA *SigningMethodEdwardsCurve
)
func init() {
// EdDSA with Ed25519 https://tools.ietf.org/html/rfc8037#section-3.1.
SigningMethodEdDSA = &SigningMethodEdwardsCurve{"EdDSA"}
jwt.RegisterSigningMethod(SigningMethodEdDSA.Alg(), func() jwt.SigningMethod {
return SigningMethodEdDSA
})
}
// Alg implements the jwt.SigningMethod interface.
func (m *SigningMethodEdwardsCurve) Alg() string {
return m.Name
}
// Verify implements the jwt.SigningMethod interface.
func (m *SigningMethodEdwardsCurve) Verify(signingString, signature string, key interface{}) error {
var err error
// Decode the signature
var sig []byte
if sig, err = jwt.DecodeSegment(signature); err != nil {
return err
}
// Get the key
switch k := key.(type) {
case ed25519.PublicKey:
if len(k) != ed25519.PublicKeySize {
return jwt.ErrInvalidKey
}
if verifystatus := ed25519.Verify(k, []byte(signingString), sig); verifystatus == true {
return nil
} else {
return ErrEdDSAVerification
}
default:
return jwt.ErrInvalidKeyType
}
}
// Sign implements the jwt.SigningMethod interface.
func (m *SigningMethodEdwardsCurve) Sign(signingString string, key interface{}) (string, error) {
switch k := key.(type) {
case ed25519.PrivateKey:
if len(k) != ed25519.PrivateKeySize {
return "", jwt.ErrInvalidKey
}
if s, err := k.Sign(rand.Reader, []byte(signingString), crypto.Hash(0)); err == nil {
// We serialize the signature.
return jwt.EncodeSegment(s), nil
} else {
return "", err
}
default:
return "", jwt.ErrInvalidKeyType
}
}

4
vendor/modules.txt vendored
View File

@@ -870,7 +870,7 @@ github.com/libregraph/idm/server
github.com/libregraph/idm/server/handler
github.com/libregraph/idm/server/handler/boltdb
github.com/libregraph/idm/server/handler/ldif
# github.com/libregraph/lico v0.65.1
# github.com/libregraph/lico v0.65.1 => github.com/dragonchaser/lico v0.0.0-20250416114507-5a6cbb7004e8
## explicit; go 1.18
github.com/libregraph/lico
github.com/libregraph/lico/bootstrap
@@ -897,7 +897,6 @@ github.com/libregraph/lico/oidc/code/managers
github.com/libregraph/lico/oidc/payload
github.com/libregraph/lico/oidc/provider
github.com/libregraph/lico/server
github.com/libregraph/lico/signing
github.com/libregraph/lico/utils
github.com/libregraph/lico/version
# github.com/libregraph/oidc-go v1.1.0
@@ -2435,3 +2434,4 @@ stash.kopano.io/kgol/rndm
# github.com/unrolled/secure => github.com/DeepDiver1975/secure v0.0.0-20240611112133-abc838fb797c
# github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/kobergj/plugins/v4/store/nats-js-kv v0.0.0-20240807130109-f62bb67e8c90
# go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.20241115112658-b5d4de5ed9b3
# github.com/libregraph/lico => github.com/dragonchaser/lico v0.0.0-20250416114507-5a6cbb7004e8