AuthN: Add oauth clients and perform oauth authentication with authn.Service (#62072)

* AuthN: Update signature of redirect client and RedirectURL function

* OAuth: use authn.Service to perform oauth authentication and login if feature toggle is enabled

* AuthN: register oauth clients

* AuthN: set auth module metadata

* AuthN: add logs for failed login attempts

* AuthN: Don't use enable disabled setting

* OAuth: only run hooks when authnService feature toggle is disabled

* OAuth: Add function to handle oauth errors from authn.Service
This commit is contained in:
Karl Persson
2023-01-30 12:45:04 +01:00
committed by GitHub
parent e3bfc67d7b
commit efeb0daec6
8 changed files with 681 additions and 39 deletions

View File

@@ -11,6 +11,7 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/infra/network"
"github.com/grafana/grafana/pkg/infra/tracing"
"github.com/grafana/grafana/pkg/login/social"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apikey"
"github.com/grafana/grafana/pkg/services/auth"
@@ -52,6 +53,7 @@ func ProvideService(
loginAttempts loginattempt.Service, quotaService quota.Service,
authInfoService login.AuthInfoService, renderService rendering.Service,
features *featuremgmt.FeatureManager, oauthTokenService oauthtoken.OAuthTokenService,
socialService social.Service,
) *Service {
s := &Service{
log: log.New("authn.service"),
@@ -116,6 +118,21 @@ func ProvideService(
s.RegisterClient(clients.ProvideJWT(jwtService, cfg))
}
for name := range socialService.GetOAuthProviders() {
oauthCfg := socialService.GetOAuthInfoProvider(name)
if oauthCfg != nil && oauthCfg.Enabled {
clientName := authn.ClientWithPrefix(name)
connector, errConnector := socialService.GetConnector(name)
httpClient, errHTTPClient := socialService.GetOAuthHttpClient(name)
if errConnector != nil || errHTTPClient != nil {
s.log.Error("failed to configure oauth client", "client", clientName, "err", multierror.Append(errConnector, errHTTPClient))
}
s.RegisterClient(clients.ProvideOAuth(clientName, cfg, oauthCfg, connector, httpClient))
}
}
// FIXME (jguer): move to User package
userSyncService := sync.ProvideUserSync(userService, userProtectionService, authInfoService, quotaService)
orgUserSyncService := sync.ProvideOrgSync(userService, orgService, accessControlService)
@@ -233,6 +250,7 @@ func (s *Service) Login(ctx context.Context, client string, r *authn.Request) (i
sessionToken, err := s.sessionService.CreateToken(ctx, &user.User{ID: id}, ip, r.HTTPRequest.UserAgent())
if err != nil {
s.log.FromContext(ctx).Error("failed to create session", "client", client, "userId", id, "err", err)
return nil, err
}
@@ -244,19 +262,19 @@ func (s *Service) RegisterPostLoginHook(hook authn.PostLoginHookFn, priority uin
s.postLoginHooks.insert(hook, priority)
}
func (s *Service) RedirectURL(ctx context.Context, client string, r *authn.Request) (string, error) {
func (s *Service) RedirectURL(ctx context.Context, client string, r *authn.Request) (*authn.Redirect, error) {
ctx, span := s.tracer.Start(ctx, "authn.RedirectURL")
defer span.End()
span.SetAttributes(attributeKeyClient, client, attribute.Key(attributeKeyClient).String(client))
c, ok := s.clients[client]
if !ok {
return "", authn.ErrClientNotConfigured.Errorf("client not configured: %s", client)
return nil, authn.ErrClientNotConfigured.Errorf("client not configured: %s", client)
}
redirectClient, ok := c.(authn.RedirectClient)
if !ok {
return "", authn.ErrUnsupportedClient.Errorf("client does not support generating redirect url: %s", client)
return nil, authn.ErrUnsupportedClient.Errorf("client does not support generating redirect url: %s", client)
}
return redirectClient.RedirectURL(ctx, r)

View File

@@ -243,16 +243,13 @@ func TestService_RedirectURL(t *testing.T) {
type testCase struct {
desc string
client string
expectedURL string
expectedErr error
}
tests := []testCase{
{
desc: "should generate url for valid redirect client",
client: "redirect",
expectedURL: "https://localhost/redirect",
expectedErr: nil,
desc: "should generate url for valid redirect client",
client: "redirect",
},
{
desc: "should return error on non existing client",
@@ -269,13 +266,12 @@ func TestService_RedirectURL(t *testing.T) {
for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
service := setupTests(t, func(svc *Service) {
svc.RegisterClient(authntest.FakeRedirectClient{ExpectedName: "redirect", ExpectedURL: tt.expectedURL})
svc.RegisterClient(authntest.FakeRedirectClient{ExpectedName: "redirect"})
svc.RegisterClient(&authntest.FakeClient{ExpectedName: "non-redirect"})
})
u, err := service.RedirectURL(context.Background(), tt.client, nil)
_, err := service.RedirectURL(context.Background(), tt.client, nil)
assert.ErrorIs(t, err, tt.expectedErr)
assert.Equal(t, tt.expectedURL, u)
})
}
}