Skip to content

Commit

Permalink
Return errors instead of panicing + refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
vasayxtx committed Oct 23, 2024
1 parent 113fd0b commit e99d274
Showing 21 changed files with 208 additions and 193 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Toolkit for authentication and authorization in Go services

## Installation

```
go get -u github.com/acronis/go-authkit
```

## Features

- Authenticate HTTP requests with JWT tokens via middleware that can be configured via YAML/JSON file or environment variables.
39 changes: 12 additions & 27 deletions auth.go
Original file line number Diff line number Diff line change
@@ -12,49 +12,41 @@ import (
"fmt"
"net/http"
"os"
"time"

"github.com/acronis/go-appkit/log"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"

"github.com/acronis/go-authkit/idptoken"
"github.com/acronis/go-authkit/internal/idputil"
"github.com/acronis/go-authkit/jwks"
"github.com/acronis/go-authkit/jwt"
)

// Default values.
const (
DefaultHTTPClientRequestTimeout = time.Second * 30
DefaultGRPCClientRequestTimeout = time.Second * 30
)

// NewJWTParser creates a new JWTParser with the given configuration.
// If cfg.JWT.ClaimsCache.Enabled is true, then jwt.CachingParser created, otherwise - jwt.Parser.
func NewJWTParser(cfg *Config, opts ...JWTParserOption) (JWTParser, error) {
var options jwtParserOptions
for _, opt := range opts {
opt(&options)
}
logger := options.logger
if logger == nil {
logger = log.NewDisabledLogger()
}

logger := idputil.PrepareLogger(options.logger)

// Make caching JWKS client.
jwksCacheUpdateMinInterval := cfg.JWKS.Cache.UpdateMinInterval
if jwksCacheUpdateMinInterval == 0 {
jwksCacheUpdateMinInterval = jwks.DefaultCacheUpdateMinInterval
}
httpClientRequestTimeout := cfg.HTTPClient.RequestTimeout
if httpClientRequestTimeout == 0 {
httpClientRequestTimeout = DefaultHTTPClientRequestTimeout
}
jwksClientOpts := jwks.CachingClientOpts{
ClientOpts: jwks.ClientOpts{PrometheusLibInstanceLabel: options.prometheusLibInstanceLabel},
ClientOpts: jwks.ClientOpts{
Logger: logger,
HTTPClient: idputil.MakeDefaultHTTPClient(cfg.HTTPClient.RequestTimeout, logger),
PrometheusLibInstanceLabel: options.prometheusLibInstanceLabel,
},
CacheUpdateMinInterval: jwksCacheUpdateMinInterval,
}
jwksClient := jwks.NewCachingClientWithOpts(&http.Client{Timeout: httpClientRequestTimeout}, logger, jwksClientOpts)
jwksClient := jwks.NewCachingClientWithOpts(jwksClientOpts)

// Make JWT parser.

@@ -134,10 +126,8 @@ func NewTokenIntrospector(
for _, opt := range opts {
opt(&options)
}
logger := options.logger
if logger == nil {
logger = log.NewDisabledLogger()
}

logger := idputil.PrepareLogger(options.logger)

if len(cfg.JWT.TrustedIssuers) == 0 && len(cfg.JWT.TrustedIssuerURLs) == 0 {
logger.Warn("list of trusted issuers is empty, jwt introspection may not work properly")
@@ -156,15 +146,10 @@ func NewTokenIntrospector(
}
}

httpClientRequestTimeout := cfg.HTTPClient.RequestTimeout
if httpClientRequestTimeout == 0 {
httpClientRequestTimeout = DefaultHTTPClientRequestTimeout
}

introspectorOpts := idptoken.IntrospectorOpts{
StaticHTTPEndpoint: cfg.Introspection.Endpoint,
GRPCClient: grpcClient,
HTTPClient: &http.Client{Timeout: httpClientRequestTimeout},
HTTPClient: idputil.MakeDefaultHTTPClient(cfg.HTTPClient.RequestTimeout, logger),
AccessTokenScope: cfg.Introspection.AccessTokenScope,
Logger: logger,
ScopeFilter: scopeFilter,
5 changes: 3 additions & 2 deletions config.go
Original file line number Diff line number Diff line change
@@ -14,6 +14,7 @@ import (
"github.com/acronis/go-appkit/config"

"github.com/acronis/go-authkit/idptoken"
"github.com/acronis/go-authkit/internal/idputil"
"github.com/acronis/go-authkit/jwks"
"github.com/acronis/go-authkit/jwt"
)
@@ -142,8 +143,8 @@ func (c *Config) KeyPrefix() string {

// SetProviderDefaults sets default configuration values for auth in config.DataProvider.
func (c *Config) SetProviderDefaults(dp config.DataProvider) {
dp.SetDefault(cfgKeyHTTPClientRequestTimeout, DefaultHTTPClientRequestTimeout.String())
dp.SetDefault(cfgKeyGRPCClientRequestTimeout, DefaultGRPCClientRequestTimeout.String())
dp.SetDefault(cfgKeyHTTPClientRequestTimeout, idputil.DefaultHTTPRequestTimeout.String())
dp.SetDefault(cfgKeyGRPCClientRequestTimeout, idptoken.DefaultGRPCClientRequestTimeout.String())
dp.SetDefault(cfgKeyJWTClaimsCacheMaxEntries, jwt.DefaultClaimsCacheMaxEntries)
dp.SetDefault(cfgKeyJWKSCacheUpdateMinInterval, jwks.DefaultCacheUpdateMinInterval.String())
dp.SetDefault(cfgKeyIntrospectionClaimsCacheMaxEntries, idptoken.DefaultIntrospectionClaimsCacheMaxEntries)
3 changes: 2 additions & 1 deletion examples/idp-test-server/main.go
Original file line number Diff line number Diff line change
@@ -33,7 +33,8 @@ func runApp() error {
logger, loggerClose := log.NewLogger(&log.Config{Output: log.OutputStdout, Level: log.LevelInfo, Format: log.FormatJSON})
defer loggerClose()

jwtParser := jwt.NewParser(jwks.NewCachingClient(&http.Client{Timeout: time.Second * 30}, logger), logger)
jwksClientOpts := jwks.CachingClientOpts{ClientOpts: jwks.ClientOpts{Logger: logger}}
jwtParser := jwt.NewParser(jwks.NewCachingClientWithOpts(jwksClientOpts), logger)
_ = jwtParser.AddTrustedIssuerURL("http://" + idpAddr)
idpSrv := idptest.NewHTTPServer(
idptest.WithHTTPAddress(idpAddr),
10 changes: 10 additions & 0 deletions idptest/jwt.go
Original file line number Diff line number Diff line change
@@ -21,6 +21,16 @@ func SignToken(token *jwtgo.Token, rsaPrivateKey interface{}) (string, error) {
return token.SignedString(rsaPrivateKey)
}

// MustSignToken signs token with key.
// It panics if error occurs.
func MustSignToken(token *jwtgo.Token, rsaPrivateKey interface{}) string {
s, err := SignToken(token, rsaPrivateKey)
if err != nil {
panic(err)
}
return s
}

// MakeTokenStringWithHeader create test signed token with claims and headers.
func MakeTokenStringWithHeader(
claims jwtgo.Claims, kid string, rsaPrivateKey interface{}, header map[string]interface{},
3 changes: 1 addition & 2 deletions idptest/jwt_test.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ package idptest

import (
"context"
"net/http"
"net/http/httptest"
"testing"
"time"
@@ -42,7 +41,7 @@ func TestMakeTokenStringWithHeader(t *testing.T) {
},
}

parser := jwt.NewParser(jwks.NewCachingClient(http.DefaultClient, logger), logger)
parser := jwt.NewParser(jwks.NewCachingClient(), logger)
parser.AddTrustedIssuer(testIss, issuerConfigServer.URL)
parsedClaims, err := parser.Parse(context.Background(), MustMakeTokenStringSignedWithTestKey(jwtClaims))
require.NoError(t, err)
3 changes: 1 addition & 2 deletions idptoken/caching_introspector_test.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ package idptoken_test

import (
"context"
"net/http"
"net/url"
gotesting "testing"
"time"
@@ -36,7 +35,7 @@ func TestCachingIntrospector_IntrospectToken(t *gotesting.T) {
tokenProvider := idptest.NewSimpleTokenProvider(accessToken)

logger := log.NewDisabledLogger()
jwtParser := jwt.NewParser(jwks.NewClient(http.DefaultClient, logger), logger)
jwtParser := jwt.NewParser(jwks.NewClient(), logger)
require.NoError(t, jwtParser.AddTrustedIssuerURL(idpSrv.URL()))
serverIntrospector.JWTParser = jwtParser

9 changes: 5 additions & 4 deletions idptoken/grpc_client.go
Original file line number Diff line number Diff line change
@@ -22,10 +22,13 @@ import (
grpcstatus "google.golang.org/grpc/status"

"github.com/acronis/go-authkit/idptoken/pb"
"github.com/acronis/go-authkit/internal/idputil"
"github.com/acronis/go-authkit/internal/metrics"
"github.com/acronis/go-authkit/jwt"
)

const DefaultGRPCClientRequestTimeout = time.Second * 30

// GRPCClientOpts contains options for the GRPCClient.
type GRPCClientOpts struct {
// Logger is a logger for the client.
@@ -61,11 +64,9 @@ func NewGRPCClient(
func NewGRPCClientWithOpts(
target string, transportCreds credentials.TransportCredentials, opts GRPCClientOpts,
) (*GRPCClient, error) {
if opts.Logger == nil {
opts.Logger = log.NewDisabledLogger()
}
opts.Logger = idputil.PrepareLogger(opts.Logger)
if opts.RequestTimeout == 0 {
opts.RequestTimeout = time.Second * 30
opts.RequestTimeout = DefaultGRPCClientRequestTimeout
}
conn, err := grpc.NewClient(target,
grpc.WithTransportCredentials(transportCreds),
8 changes: 2 additions & 6 deletions idptoken/introspector.go
Original file line number Diff line number Diff line change
@@ -27,8 +27,6 @@ import (
"github.com/acronis/go-authkit/jwt"
)

const DefaultRequestTimeout = 30 * time.Second

const JWTTypeAccessToken = "at+jwt"

const TokenTypeBearer = "bearer"
@@ -137,11 +135,9 @@ func NewIntrospector(tokenProvider IntrospectionTokenProvider) *Introspector {
// NewIntrospectorWithOpts creates a new Introspector with the given token provider and options.
// See IntrospectorOpts for more details.
func NewIntrospectorWithOpts(accessTokenProvider IntrospectionTokenProvider, opts IntrospectorOpts) *Introspector {
opts.Logger = idputil.PrepareLogger(opts.Logger)
if opts.HTTPClient == nil {
opts.HTTPClient = &http.Client{Timeout: DefaultRequestTimeout}
}
if opts.Logger == nil {
opts.Logger = log.NewDisabledLogger()
opts.HTTPClient = idputil.MakeDefaultHTTPClient(idputil.DefaultHTTPRequestTimeout, opts.Logger)
}

values := url.Values{}
4 changes: 1 addition & 3 deletions idptoken/introspector_test.go
Original file line number Diff line number Diff line change
@@ -8,7 +8,6 @@ package idptoken_test

import (
"context"
"net/http"
"net/url"
gotesting "testing"
"time"
@@ -42,8 +41,7 @@ func TestIntrospector_IntrospectToken(t *gotesting.T) {
const accessToken = "access-token-with-introspection-permission"
tokenProvider := idptest.NewSimpleTokenProvider(accessToken)

logger := log.NewDisabledLogger()
jwtParser := jwt.NewParser(jwks.NewClient(http.DefaultClient, logger), logger)
jwtParser := jwt.NewParser(jwks.NewClient(), log.NewDisabledLogger())
require.NoError(t, jwtParser.AddTrustedIssuerURL(httpIDPSrv.URL()))
httpServerIntrospector.JWTParser = jwtParser
grpcServerIntrospector.JWTParser = jwtParser
Loading

0 comments on commit e99d274

Please sign in to comment.