mirror of
https://github.com/grafana/grafana.git
synced 2024-12-30 10:47:30 -06:00
IDForwarding: Add service and a local signer (#75423)
* IDForwarding: Add service for handling id token and create a local signer --------- Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
parent
5983bcec86
commit
b50f1e15a8
@ -43,6 +43,8 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/annotations/annotationsimpl"
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl/anonstore"
|
||||
"github.com/grafana/grafana/pkg/services/apikey/apikeyimpl"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/idimpl"
|
||||
"github.com/grafana/grafana/pkg/services/auth/jwt"
|
||||
"github.com/grafana/grafana/pkg/services/authn/authnimpl"
|
||||
"github.com/grafana/grafana/pkg/services/cleanup"
|
||||
@ -361,6 +363,8 @@ var wireBasicSet = wire.NewSet(
|
||||
loggermw.Provide,
|
||||
signingkeysimpl.ProvideEmbeddedSigningKeysService,
|
||||
wire.Bind(new(signingkeys.Service), new(*signingkeysimpl.Service)),
|
||||
idimpl.ProvideService,
|
||||
wire.Bind(new(auth.IDService), new(*idimpl.Service)),
|
||||
grafanaapiserver.WireSet,
|
||||
apiregistry.WireSet,
|
||||
)
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/anonymous/anonimpl"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/authimpl"
|
||||
"github.com/grafana/grafana/pkg/services/auth/idimpl"
|
||||
"github.com/grafana/grafana/pkg/services/caching"
|
||||
"github.com/grafana/grafana/pkg/services/datasources/guardian"
|
||||
"github.com/grafana/grafana/pkg/services/encryption"
|
||||
@ -91,6 +92,8 @@ var wireExtsBasicSet = wire.NewSet(
|
||||
wire.Bind(new(caching.CachingService), new(*caching.OSSCachingService)),
|
||||
secretsMigrator.ProvideSecretsMigrator,
|
||||
wire.Bind(new(secrets.Migrator), new(*secretsMigrator.SecretsMigrator)),
|
||||
idimpl.ProvideLocalSigner,
|
||||
wire.Bind(new(auth.IDSigner), new(*idimpl.LocalSigner)),
|
||||
)
|
||||
|
||||
var wireExtsSet = wire.NewSet(
|
||||
|
21
pkg/services/auth/id.go
Normal file
21
pkg/services/auth/id.go
Normal file
@ -0,0 +1,21 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
type IDSigner interface {
|
||||
SignIDToken(ctx context.Context, claims *IDClaims) (string, error)
|
||||
}
|
||||
|
||||
type IDClaims struct {
|
||||
jwt.Claims
|
||||
}
|
74
pkg/services/auth/idimpl/service.go
Normal file
74
pkg/services/auth/idimpl/service.go
Normal file
@ -0,0 +1,74 @@
|
||||
package idimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/infra/remotecache"
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/auth/identity"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
)
|
||||
|
||||
const (
|
||||
cachePrefix = "id-token"
|
||||
tokenTTL = 1 * time.Hour
|
||||
cacheTTL = 58 * time.Minute
|
||||
)
|
||||
|
||||
var _ auth.IDService = (*Service)(nil)
|
||||
|
||||
func ProvideService(cfg *setting.Cfg, signer auth.IDSigner, cache remotecache.CacheStorage) *Service {
|
||||
return &Service{cfg, log.New("id-service"), signer, cache}
|
||||
}
|
||||
|
||||
type Service struct {
|
||||
cfg *setting.Cfg
|
||||
logger log.Logger
|
||||
signer auth.IDSigner
|
||||
cache remotecache.CacheStorage
|
||||
}
|
||||
|
||||
func (s *Service) SignIdentity(ctx context.Context, id identity.Requester) (string, error) {
|
||||
namespace, identifier := id.GetNamespacedID()
|
||||
|
||||
cacheKey := prefixCacheKey(id.GetCacheKey())
|
||||
cachedToken, err := s.cache.Get(ctx, cacheKey)
|
||||
if err == nil {
|
||||
s.logger.Debug("Cached token found", "namespace", namespace, "id", identifier)
|
||||
return string(cachedToken), nil
|
||||
}
|
||||
|
||||
s.logger.Debug("Sign new id token", "namespace", namespace, "id", identifier)
|
||||
|
||||
now := time.Now()
|
||||
|
||||
token, err := s.signer.SignIDToken(ctx, &auth.IDClaims{
|
||||
Claims: jwt.Claims{
|
||||
ID: identifier,
|
||||
Issuer: s.cfg.AppURL,
|
||||
Audience: jwt.Audience{strconv.FormatInt(id.GetOrgID(), 10)},
|
||||
Subject: fmt.Sprintf("%s:%s", namespace, identifier),
|
||||
Expiry: jwt.NewNumericDate(now.Add(tokenTTL)),
|
||||
IssuedAt: jwt.NewNumericDate(now),
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err := s.cache.Set(ctx, cacheKey, []byte(token), cacheTTL); err != nil {
|
||||
s.logger.Error("failed to set cache", "error", err)
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func prefixCacheKey(key string) string {
|
||||
return fmt.Sprintf("%s-%s", cachePrefix, key)
|
||||
}
|
45
pkg/services/auth/idimpl/signer.go
Normal file
45
pkg/services/auth/idimpl/signer.go
Normal file
@ -0,0 +1,45 @@
|
||||
package idimpl
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/go-jose/go-jose/v3"
|
||||
"github.com/go-jose/go-jose/v3/jwt"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/auth"
|
||||
"github.com/grafana/grafana/pkg/services/signingkeys"
|
||||
)
|
||||
|
||||
var _ auth.IDSigner = (*LocalSigner)(nil)
|
||||
|
||||
func ProvideLocalSigner(keyService signingkeys.Service) (*LocalSigner, error) {
|
||||
key := keyService.GetServerPrivateKey() // FIXME: replace with signing specific key
|
||||
|
||||
signer, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.ES256, Key: key}, &jose.SignerOptions{
|
||||
ExtraHeaders: map[jose.HeaderKey]interface{}{
|
||||
"kid": "default", // FIXME: replace with specific key id
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &LocalSigner{
|
||||
signer: signer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type LocalSigner struct {
|
||||
signer jose.Signer
|
||||
}
|
||||
|
||||
func (s *LocalSigner) SignIDToken(ctx context.Context, claims *auth.IDClaims) (string, error) {
|
||||
builder := jwt.Signed(s.signer).Claims(claims.Claims)
|
||||
|
||||
token, err := builder.CompactSerialize()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user