mirror of https://github.com/grafana/grafana.git
				
				
				
			[authn]: add GetIDClaims() to Requester (#91387)
* authn: add GetIDClaims() to Requester Co-Authored-By: Gabriel MABILLE <gamab@users.noreply.github.com> * authn: update StaticRequester Co-Authored-By: Gabriel MABILLE <gamab@users.noreply.github.com> * update auth/idtest/mock Co-Authored-By: Gabriel MABILLE <gamab@users.noreply.github.com> * Fix test Co-authored-by: Claudiu Dragalina-Paraipan <claudiu.dragalina@grafana.com> --------- Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: gamab <gabriel.mabille@grafana.com>
This commit is contained in:
		
							parent
							
								
									a940bb87be
								
							
						
					
					
						commit
						e2435f92f1
					
				|  | @ -3,6 +3,7 @@ module github.com/grafana/grafana/pkg/apimachinery | |||
| go 1.21.10 | ||||
| 
 | ||||
| require ( | ||||
| 	github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 | ||||
| 	github.com/stretchr/testify v1.9.0 | ||||
| 	k8s.io/apimachinery v0.29.3 | ||||
| 	k8s.io/apiserver v0.29.2 | ||||
|  | @ -12,6 +13,7 @@ require ( | |||
| require ( | ||||
| 	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect | ||||
| 	github.com/emicklei/go-restful/v3 v3.11.0 // indirect | ||||
| 	github.com/go-jose/go-jose/v3 v3.0.3 // indirect | ||||
| 	github.com/go-logr/logr v1.4.2 // indirect | ||||
| 	github.com/go-openapi/jsonpointer v0.21.0 // indirect | ||||
| 	github.com/go-openapi/jsonreference v0.20.4 // indirect | ||||
|  | @ -25,10 +27,15 @@ require ( | |||
| 	github.com/mailru/easyjson v0.7.7 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/patrickmn/go-cache v2.1.0+incompatible // indirect | ||||
| 	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect | ||||
| 	github.com/rogpeppe/go-internal v1.12.0 // indirect | ||||
| 	golang.org/x/crypto v0.24.0 // indirect | ||||
| 	golang.org/x/net v0.26.0 // indirect | ||||
| 	golang.org/x/sync v0.7.0 // indirect | ||||
| 	golang.org/x/sys v0.21.0 // indirect | ||||
| 	golang.org/x/text v0.16.0 // indirect | ||||
| 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect | ||||
| 	google.golang.org/grpc v1.65.0 // indirect | ||||
| 	google.golang.org/protobuf v1.34.2 // indirect | ||||
| 	gopkg.in/inf.v0 v0.9.1 // indirect | ||||
| 	gopkg.in/yaml.v2 v2.4.0 // indirect | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||
| github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= | ||||
| github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k= | ||||
| github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= | ||||
| github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= | ||||
| github.com/go-openapi/jsonreference v0.20.4 h1:bKlDxQxQJgwpUSgOENiMPzCTBVuc7vTdXSSgNeAhojU= | ||||
|  | @ -9,6 +10,7 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek | |||
| github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= | ||||
| github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= | ||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/grafana/authlib v0.0.0-20240730122259-a0d13672efb1 h1:EiaupmOnt6XF/LPxvagjTofWmByzYaf5VyMIF+w/71M= | ||||
| github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
|  | @ -16,12 +18,18 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | |||
| github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | ||||
| github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= | ||||
| golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= | ||||
| golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= | ||||
| golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= | ||||
| golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= | ||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= | ||||
| google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= | ||||
| google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||||
| gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= | ||||
| gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"fmt" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	authnlib "github.com/grafana/authlib/authn" | ||||
| 	"k8s.io/apiserver/pkg/authentication/user" | ||||
| ) | ||||
| 
 | ||||
|  | @ -77,6 +78,8 @@ type Requester interface { | |||
| 	// GetIDToken returns a signed token representing the identity that can be forwarded to plugins and external services.
 | ||||
| 	// Will only be set when featuremgmt.FlagIdForwarding is enabled.
 | ||||
| 	GetIDToken() string | ||||
| 	// GetIDClaims returns the claims of the ID token.
 | ||||
| 	GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] | ||||
| } | ||||
| 
 | ||||
| // IntIdentifier converts a string identifier to an int64.
 | ||||
|  |  | |||
|  | @ -1,6 +1,10 @@ | |||
| package identity | ||||
| 
 | ||||
| import "fmt" | ||||
| import ( | ||||
| 	"fmt" | ||||
| 
 | ||||
| 	authnlib "github.com/grafana/authlib/authn" | ||||
| ) | ||||
| 
 | ||||
| var _ Requester = &StaticRequester{} | ||||
| 
 | ||||
|  | @ -27,6 +31,7 @@ type StaticRequester struct { | |||
| 	// Permissions grouped by orgID and actions
 | ||||
| 	Permissions   map[int64]map[string][]string | ||||
| 	IDToken       string | ||||
| 	IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] | ||||
| 	CacheKey      string | ||||
| } | ||||
| 
 | ||||
|  | @ -208,3 +213,7 @@ func (u *StaticRequester) GetDisplayName() string { | |||
| func (u *StaticRequester) GetIDToken() string { | ||||
| 	return u.IDToken | ||||
| } | ||||
| 
 | ||||
| func (u *StaticRequester) GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] { | ||||
| 	return u.IDTokenClaims | ||||
| } | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ import ( | |||
| 
 | ||||
| type IDService interface { | ||||
| 	// SignIdentity signs a id token for provided identity that can be forwarded to plugins and external services
 | ||||
| 	SignIdentity(ctx context.Context, identity identity.Requester) (string, error) | ||||
| 	SignIdentity(ctx context.Context, id identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) | ||||
| 
 | ||||
| 	// RemoveIDToken removes any locally stored id tokens for key
 | ||||
| 	RemoveIDToken(ctx context.Context, identity identity.Requester) error | ||||
|  |  | |||
|  | @ -58,21 +58,30 @@ type Service struct { | |||
| 	nsMapper request.NamespaceMapper | ||||
| } | ||||
| 
 | ||||
| func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, error) { | ||||
| func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) { | ||||
| 	defer func(t time.Time) { | ||||
| 		s.metrics.tokenSigningDurationHistogram.Observe(time.Since(t).Seconds()) | ||||
| 	}(time.Now()) | ||||
| 
 | ||||
| 	cacheKey := prefixCacheKey(id.GetCacheKey()) | ||||
| 
 | ||||
| 	result, err, _ := s.si.Do(cacheKey, func() (interface{}, error) { | ||||
| 	type resultType struct { | ||||
| 		token    string | ||||
| 		idClaims *auth.IDClaims | ||||
| 	} | ||||
| 	result, err, _ := s.si.Do(cacheKey, func() (any, error) { | ||||
| 		namespace, identifier := id.GetTypedID() | ||||
| 
 | ||||
| 		cachedToken, err := s.cache.Get(ctx, cacheKey) | ||||
| 		if err == nil { | ||||
| 			s.metrics.tokenSigningFromCacheCounter.Inc() | ||||
| 			s.logger.FromContext(ctx).Debug("Cached token found", "namespace", namespace, "id", identifier) | ||||
| 			return string(cachedToken), nil | ||||
| 
 | ||||
| 			tokenClaims, err := s.extractTokenClaims(string(cachedToken)) | ||||
| 			if err != nil { | ||||
| 				return resultType{}, err | ||||
| 			} | ||||
| 			return resultType{token: string(cachedToken), idClaims: tokenClaims}, nil | ||||
| 		} | ||||
| 
 | ||||
| 		s.metrics.tokenSigningCounter.Inc() | ||||
|  | @ -104,21 +113,12 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri | |||
| 		token, err := s.signer.SignIDToken(ctx, claims) | ||||
| 		if err != nil { | ||||
| 			s.metrics.failedTokenSigningCounter.Inc() | ||||
| 			return "", err | ||||
| 			return resultType{}, nil | ||||
| 		} | ||||
| 
 | ||||
| 		parsed, err := jwt.ParseSigned(token) | ||||
| 		extracted, err := s.extractTokenClaims(token) | ||||
| 		if err != nil { | ||||
| 			s.metrics.failedTokenSigningCounter.Inc() | ||||
| 			return "", err | ||||
| 		} | ||||
| 
 | ||||
| 		extracted := auth.IDClaims{} | ||||
| 		// We don't need to verify the signature here, we are only interested in checking
 | ||||
| 		// when the token expires.
 | ||||
| 		if err := parsed.UnsafeClaimsWithoutVerification(&extracted); err != nil { | ||||
| 			s.metrics.failedTokenSigningCounter.Inc() | ||||
| 			return "", err | ||||
| 			return resultType{}, err | ||||
| 		} | ||||
| 
 | ||||
| 		expires := time.Until(extracted.Expiry.Time()) | ||||
|  | @ -126,14 +126,14 @@ func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (stri | |||
| 			s.logger.FromContext(ctx).Error("Failed to add id token to cache", "error", err) | ||||
| 		} | ||||
| 
 | ||||
| 		return token, nil | ||||
| 		return resultType{token: token, idClaims: claims}, nil | ||||
| 	}) | ||||
| 
 | ||||
| 	if err != nil { | ||||
| 		return "", err | ||||
| 		return "", nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return result.(string), nil | ||||
| 	return result.(resultType).token, result.(resultType).idClaims, nil | ||||
| } | ||||
| 
 | ||||
| func (s *Service) RemoveIDToken(ctx context.Context, id identity.Requester) error { | ||||
|  | @ -142,7 +142,7 @@ func (s *Service) RemoveIDToken(ctx context.Context, id identity.Requester) erro | |||
| 
 | ||||
| func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.Request) error { | ||||
| 	// FIXME(kalleep): we should probably lazy load this
 | ||||
| 	token, err := s.SignIdentity(ctx, identity) | ||||
| 	token, claims, err := s.SignIdentity(ctx, identity) | ||||
| 	if err != nil { | ||||
| 		if shouldLogErr(err) { | ||||
| 			namespace, id := identity.GetTypedID() | ||||
|  | @ -153,9 +153,28 @@ func (s *Service) hook(ctx context.Context, identity *authn.Identity, _ *authn.R | |||
| 	} | ||||
| 
 | ||||
| 	identity.IDToken = token | ||||
| 	identity.IDTokenClaims = claims | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (s *Service) extractTokenClaims(token string) (*authnlib.Claims[authnlib.IDTokenClaims], error) { | ||||
| 	parsed, err := jwt.ParseSigned(token) | ||||
| 	if err != nil { | ||||
| 		s.metrics.failedTokenSigningCounter.Inc() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	extracted := authnlib.Claims[authnlib.IDTokenClaims]{} | ||||
| 	// We don't need to verify the signature here, we are only interested in checking
 | ||||
| 	// when the token expires.
 | ||||
| 	if err := parsed.UnsafeClaimsWithoutVerification(&extracted); err != nil { | ||||
| 		s.metrics.failedTokenSigningCounter.Inc() | ||||
| 		return nil, err | ||||
| 	} | ||||
| 
 | ||||
| 	return &extracted, nil | ||||
| } | ||||
| 
 | ||||
| func getAudience(orgID int64) jwt.Audience { | ||||
| 	return jwt.Audience{fmt.Sprintf("org:%d", orgID)} | ||||
| } | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ func TestService_SignIdentity(t *testing.T) { | |||
| 			featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), | ||||
| 			&authntest.FakeService{}, nil, | ||||
| 		) | ||||
| 		token, err := s.SignIdentity(context.Background(), &authn.Identity{ID: identity.MustParseTypedID("user:1")}) | ||||
| 		token, _, err := s.SignIdentity(context.Background(), &authn.Identity{ID: identity.MustParseTypedID("user:1")}) | ||||
| 		require.NoError(t, err) | ||||
| 		require.NotEmpty(t, token) | ||||
| 	}) | ||||
|  | @ -81,7 +81,7 @@ func TestService_SignIdentity(t *testing.T) { | |||
| 			featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), | ||||
| 			&authntest.FakeService{}, nil, | ||||
| 		) | ||||
| 		token, err := s.SignIdentity(context.Background(), &authn.Identity{ | ||||
| 		token, _, err := s.SignIdentity(context.Background(), &authn.Identity{ | ||||
| 			ID:              identity.MustParseTypedID("user:1"), | ||||
| 			AuthenticatedBy: login.AzureADAuthModule, | ||||
| 			Login:           "U1", | ||||
|  | @ -97,4 +97,22 @@ func TestService_SignIdentity(t *testing.T) { | |||
| 		assert.Equal(t, "U1", claims.Rest.Username) | ||||
| 		assert.Equal(t, "user:edpu3nnt61se8e", claims.Rest.UID) | ||||
| 	}) | ||||
| 
 | ||||
| 	t.Run("should sign identity with authenticated by if user is externally authenticated", func(t *testing.T) { | ||||
| 		s := ProvideService( | ||||
| 			setting.NewCfg(), signer, remotecache.NewFakeCacheStorage(), | ||||
| 			featuremgmt.WithFeatures(featuremgmt.FlagIdForwarding), | ||||
| 			&authntest.FakeService{}, nil, | ||||
| 		) | ||||
| 		_, gotClaims, err := s.SignIdentity(context.Background(), &authn.Identity{ | ||||
| 			ID:              identity.MustParseTypedID("user:1"), | ||||
| 			AuthenticatedBy: login.AzureADAuthModule, | ||||
| 			Login:           "U1", | ||||
| 			UID:             identity.NewTypedIDString(identity.TypeUser, "edpu3nnt61se8e")}) | ||||
| 		require.NoError(t, err) | ||||
| 
 | ||||
| 		assert.Equal(t, login.AzureADAuthModule, gotClaims.Rest.AuthenticatedBy) | ||||
| 		assert.Equal(t, "U1", gotClaims.Rest.Username) | ||||
| 		assert.Equal(t, "user:edpu3nnt61se8e", gotClaims.Rest.UID) | ||||
| 	}) | ||||
| } | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ package idtest | |||
| import ( | ||||
| 	"context" | ||||
| 
 | ||||
| 	authnlib "github.com/grafana/authlib/authn" | ||||
| 
 | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| 	"github.com/grafana/grafana/pkg/services/auth" | ||||
| ) | ||||
|  | @ -10,15 +12,15 @@ import ( | |||
| var _ auth.IDService = (*MockService)(nil) | ||||
| 
 | ||||
| type MockService struct { | ||||
| 	SignIdentityFn  func(ctx context.Context, identity identity.Requester) (string, error) | ||||
| 	SignIdentityFn  func(ctx context.Context, identity identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) | ||||
| 	RemoveIDTokenFn func(ctx context.Context, identity identity.Requester) error | ||||
| } | ||||
| 
 | ||||
| func (m *MockService) SignIdentity(ctx context.Context, identity identity.Requester) (string, error) { | ||||
| func (m *MockService) SignIdentity(ctx context.Context, identity identity.Requester) (string, *authnlib.Claims[authnlib.IDTokenClaims], error) { | ||||
| 	if m.SignIdentityFn != nil { | ||||
| 		return m.SignIdentityFn(ctx, identity) | ||||
| 	} | ||||
| 	return "", nil | ||||
| 	return "", nil, nil | ||||
| } | ||||
| 
 | ||||
| func (m *MockService) RemoveIDToken(ctx context.Context, identity identity.Requester) error { | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ import ( | |||
| 	"fmt" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/grafana/authlib/authn" | ||||
| 	"golang.org/x/oauth2" | ||||
| 
 | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
|  | @ -70,6 +71,7 @@ type Identity struct { | |||
| 	// IDToken is a signed token representing the identity that can be forwarded to plugins and external services.
 | ||||
| 	// Will only be set when featuremgmt.FlagIdForwarding is enabled.
 | ||||
| 	IDToken       string | ||||
| 	IDTokenClaims *authn.Claims[authn.IDTokenClaims] | ||||
| } | ||||
| 
 | ||||
| // GetRawIdentifier implements Requester.
 | ||||
|  | @ -156,6 +158,10 @@ func (i *Identity) GetIDToken() string { | |||
| 	return i.IDToken | ||||
| } | ||||
| 
 | ||||
| func (i *Identity) GetIDClaims() *authn.Claims[authn.IDTokenClaims] { | ||||
| 	return i.IDTokenClaims | ||||
| } | ||||
| 
 | ||||
| func (i *Identity) GetIsGrafanaAdmin() bool { | ||||
| 	return i.IsGrafanaAdmin != nil && *i.IsGrafanaAdmin | ||||
| } | ||||
|  |  | |||
|  | @ -5,6 +5,8 @@ import ( | |||
| 	"strconv" | ||||
| 	"time" | ||||
| 
 | ||||
| 	authnlib "github.com/grafana/authlib/authn" | ||||
| 
 | ||||
| 	"github.com/grafana/grafana/pkg/apimachinery/identity" | ||||
| ) | ||||
| 
 | ||||
|  | @ -40,9 +42,11 @@ type SignedInUser struct { | |||
| 	Teams            []int64 | ||||
| 	// Permissions grouped by orgID and actions
 | ||||
| 	Permissions map[int64]map[string][]string `json:"-"` | ||||
| 
 | ||||
| 	// IDToken is a signed token representing the identity that can be forwarded to plugins and external services.
 | ||||
| 	// Will only be set when featuremgmt.FlagIdForwarding is enabled.
 | ||||
| 	IDToken       string                                   `json:"-" xorm:"-"` | ||||
| 	IDTokenClaims *authnlib.Claims[authnlib.IDTokenClaims] `json:"-" xorm:"-"` | ||||
| 
 | ||||
| 	// When other settings are not deterministic, this value is used
 | ||||
| 	FallbackType identity.IdentityType | ||||
|  | @ -309,3 +313,7 @@ func (u *SignedInUser) GetDisplayName() string { | |||
| func (u *SignedInUser) GetIDToken() string { | ||||
| 	return u.IDToken | ||||
| } | ||||
| 
 | ||||
| func (u *SignedInUser) GetIDClaims() *authnlib.Claims[authnlib.IDTokenClaims] { | ||||
| 	return u.IDTokenClaims | ||||
| } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue