auth

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 3, 2024 License: MIT Imports: 19 Imported by: 0

README

Simple library in Go with primitives for performing authentication and authorization

The library includes the following packages:

  • auth (root directory) - provides authentication and authorization primitives for using on the server side.
  • jwt - provides parser for JSON Web Tokens (JWT).
  • jwks - provides a client for fetching and caching JSON Web Key Sets (JWKS).
  • idptoken - provides a client for fetching and caching Access Tokens from Identity Providers (IDP).
  • idptest - provides primitives for testing IDP clients.

Examples

Authenticating requests with JWT tokens

The JWTAuthMiddleware function creates a middleware that authenticates requests with JWT tokens.

It uses the JWTParser to parse and validate JWT. JWTParser can verify JWT tokens signed with RSA (RS256, RS384, RS512) algorithms for now. It performs <issuer_url>/.well-known/openid-configuration request to get the JWKS URL ("jwks_uri" field) and fetches JWKS from there. For other algorithms jwt.SignAlgUnknownError error will be returned. The JWTParser can be created with the NewJWTParser function or with the NewJWTParserWithCachingJWKS function. The last one is recommended for production use because it caches public keys (JWKS) that are used for verifying JWT tokens.

See Config struct for more customization options.

Example:

package main

import (
	"net/http"

	"github.com/acronis/go-appkit/log"
	"github.com/acronis/go-authkit"
)

func main() {
	jwtConfig := auth.JWTConfig{
		TrustedIssuerURLs: []string{"https://my-idp.com"},
		//TrustedIssuers: map[string]string{"my-idp": "https://my-idp.com"}, // Use TrustedIssuers if you have a custom issuer name.
	}
	jwtParser, _ := auth.NewJWTParserWithCachingJWKS(&auth.Config{JWT: jwtConfig}, log.NewDisabledLogger())
	authN := auth.JWTAuthMiddleware("MyService", jwtParser)

	srvMux := http.NewServeMux()
	srvMux.Handle("/", http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
		_, _ = rw.Write([]byte("Hello, World!"))
	}))
	srvMux.Handle("/admin", authN(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		//jwtClaims := GetJWTClaimsFromContext(r.Context()) // GetJWTClaimsFromContext is a helper function to get JWT claims from context.
		_, _ = rw.Write([]byte("Hello, admin!"))
	})))
	
	_ = http.ListenAndServe(":8080", srvMux)
}	
$ curl -w "\nHTTP code: %{http_code}\n" localhost:8080
Hello, World!
HTTP code: 200

$ curl -w "\nHTTP code: %{http_code}\n" localhost:8080/admin
{"error":{"domain":"MyService","code":"bearerTokenMissing","message":"Authorization bearer token is missing."}}
HTTP code: 401
Authorizing requests with JWT tokens
package main

import (
	"net/http"

	"github.com/acronis/go-appkit/log"
	"github.com/acronis/go-authkit"
)

func main() {
	jwtConfig := auth.JWTConfig{TrustedIssuers: map[string]string{"my-idp": idpURL}}
	jwtParser, _ := auth.NewJWTParserWithCachingJWKS(&auth.Config{JWT: jwtConfig}, log.NewDisabledLogger())
	authOnlyAdmin := auth.JWTAuthMiddlewareWithVerifyAccess("MyService", jwtParser,
		auth.NewVerifyAccessByRolesInJWT(Role{Namespace: "my-service", Name: "admin"}))

	srvMux := http.NewServeMux()
	srvMux.Handle("/", http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
		_, _ = rw.Write([]byte("Hello, World!"))
	}))
	srvMux.Handle("/admin", authOnlyAdmin(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
		_, _ = rw.Write([]byte("Hello, admin!"))
	})))
	
	_ = http.ListenAndServe(":8080", srvMux)
}

Please see example_test.go for a full version of the example.

Fetching and caching Access Tokens from Identity Providers

The idptoken.Provider object is used to fetch and cache Access Tokens from Identity Providers (IDP).

Example:

package main

import (
	"log"
	"net/http"
	
    "github.com/acronis/go-authkit/idptoken"
)

func main() {
	// ...
	httpClient := &http.Client{Timeout: 30 * time.Second}
	source := idptoken.Source{
		URL:          idpURL,
		ClientID:     clientID,
		ClientSecret: clientSecret,
	}
	provider := idptoken.NewProvider(httpClient, source)
	accessToken, err := provider.GetToken()
	if err != nil {
		log.Fatalf("failed to get access token: %v", err)
    }
	// ...
}

License

Copyright © 2024 Acronis International GmbH.

Licensed under MIT License.

Documentation

Overview

Package auth provides high-level helpers and basic objects for authN/authZ.

Index

Examples

Constants

View Source
const (
	DefaultHTTPClientRequestTimeout = time.Second * 30
	DefaultGRPCClientRequestTimeout = time.Second * 30
)

Default values.

View Source
const HeaderAuthorization = "Authorization"

HeaderAuthorization contains the name of HTTP header with data that is used for authentication and authorization.

Variables

View Source
var (
	ErrCodeBearerTokenMissing   = "bearerTokenMissing"
	ErrCodeAuthenticationFailed = "authenticationFailed"
	ErrCodeAuthorizationFailed  = "authorizationFailed"
)

Authentication and authorization error codes. We are using "var" here because some services may want to use different error codes.

View Source
var (
	ErrMessageBearerTokenMissing   = "Authorization bearer token is missing."
	ErrMessageAuthenticationFailed = "Authentication is failed."
	ErrMessageAuthorizationFailed  = "Authorization is failed."
)

Authentication error messages. We are using "var" here because some services may want to use different error messages.

Functions

func GetBearerTokenFromContext

func GetBearerTokenFromContext(ctx context.Context) string

GetBearerTokenFromContext extracts token from the context.

func GetBearerTokenFromRequest

func GetBearerTokenFromRequest(r *http.Request) string

GetBearerTokenFromRequest extracts jwt token from request headers.

func GetJWTClaimsFromContext

func GetJWTClaimsFromContext(ctx context.Context) *jwt.Claims

GetJWTClaimsFromContext extracts JWT claims from the context.

func JWTAuthMiddleware

func JWTAuthMiddleware(errorDomain string, jwtParser JWTParser, opts ...JWTAuthMiddlewareOption) func(next http.Handler) http.Handler

JWTAuthMiddleware is a middleware that does authentication by Access Token from the "Authorization" HTTP header of incoming request.

Example
jwtConfig := JWTConfig{
	TrustedIssuerURLs: []string{"https://my-idp.com"},
	//TrustedIssuers: map[string]string{"my-idp": "https://my-idp.com"}, // Use TrustedIssuers if you have a custom issuer name.
}
jwtParser, _ := NewJWTParser(&Config{JWT: jwtConfig})
authN := JWTAuthMiddleware("MyService", jwtParser)

srvMux := http.NewServeMux()
srvMux.Handle("/", http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) {
	_, _ = rw.Write([]byte("Hello, World!"))
}))
srvMux.Handle("/admin", authN(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
	//jwtClaims := GetJWTClaimsFromContext(r.Context()) // GetJWTClaimsFromContext is a helper function to get JWT claims from context.
	_, _ = rw.Write([]byte("Hello, admin!"))
})))

done := make(chan struct{})
server := &http.Server{Addr: ":8080", Handler: srvMux}
go func() {
	defer close(done)
	_ = server.ListenAndServe()
}()

time.Sleep(time.Second) // Wait for the server to start.

client := &http.Client{Timeout: time.Second * 30}

fmt.Println("GET http://localhost:8080/")
resp, _ := client.Get("http://localhost:8080/")
fmt.Println("Status code:", resp.StatusCode)
respBody, _ := io.ReadAll(resp.Body)
_ = resp.Body.Close()
fmt.Println("Body:", string(respBody))

fmt.Println("------")
fmt.Println("GET http://localhost:8080/admin without token")
resp, _ = client.Get("http://localhost:8080/admin")
fmt.Println("Status code:", resp.StatusCode)
respBody, _ = io.ReadAll(resp.Body)
_ = resp.Body.Close()
fmt.Println("Body:", string(respBody))

fmt.Println("------")
fmt.Println("GET http://localhost:8080/admin with invalid token")
req, _ := http.NewRequest(http.MethodGet, "http://localhost:8080/admin", http.NoBody)
req.Header["Authorization"] = []string{"Bearer invalid-token"}
resp, _ = client.Do(req)
fmt.Println("Status code:", resp.StatusCode)
respBody, _ = io.ReadAll(resp.Body)
_ = resp.Body.Close()
fmt.Println("Body:", string(respBody))

_ = server.Shutdown(context.Background())
<-done
Output:
GET http://localhost:8080/
Status code: 200
Body: Hello, World!
------
GET http://localhost:8080/admin without token
Status code: 401
Body: {"error":{"domain":"MyService","code":"bearerTokenMissing","message":"Authorization bearer token is missing."}}
------
GET http://localhost:8080/admin with invalid token
Status code: 401
Body: {"error":{"domain":"MyService","code":"authenticationFailed","message":"Authentication is failed."}}

func NewContextWithBearerToken

func NewContextWithBearerToken(ctx context.Context, token string) context.Context

NewContextWithBearerToken creates a new context with token.

func NewContextWithJWTClaims

func NewContextWithJWTClaims(ctx context.Context, jwtClaims *jwt.Claims) context.Context

NewContextWithJWTClaims creates a new context with JWT claims.

func NewVerifyAccessByRolesInJWT

func NewVerifyAccessByRolesInJWT(roles ...Role) func(r *http.Request, claims *jwt.Claims) bool

NewVerifyAccessByRolesInJWT creates a new function which may be used for verifying access by roles in JWT scope.

func NewVerifyAccessByRolesInJWTMaker

func NewVerifyAccessByRolesInJWTMaker(namespace string) func(roleNames ...string) func(r *http.Request, claims *jwt.Claims) bool

NewVerifyAccessByRolesInJWTMaker creates a new function which may be used for verifying access by roles in JWT scope given a namespace.

Types

type CachingJWTParser

type CachingJWTParser interface {
	JWTParser
	InvalidateCache(ctx context.Context)
}

CachingJWTParser does the same as JWTParser but stores parsed JWT claims in cache.

type ClaimsCacheConfig

type ClaimsCacheConfig struct {
	Enabled    bool
	MaxEntries int
}

ClaimsCacheConfig is a configuration of how claims cache will be used.

type Config

type Config struct {
	HTTPClient HTTPClientConfig
	GRPCClient GRPCClientConfig

	JWT           JWTConfig
	JWKS          JWKSConfig
	Introspection IntrospectionConfig
	// contains filtered or unexported fields
}

Config represents a set of configuration parameters for authentication and authorization.

func NewConfig

func NewConfig() *Config

NewConfig creates a new instance of the Config.

func NewConfigWithKeyPrefix

func NewConfigWithKeyPrefix(keyPrefix string) *Config

NewConfigWithKeyPrefix creates a new instance of the Config. Allows specifying key prefix which will be used for parsing configuration parameters.

func (*Config) KeyPrefix

func (c *Config) KeyPrefix() string

KeyPrefix returns a key prefix with which all configuration parameters should be presented.

func (*Config) Set

func (c *Config) Set(dp config.DataProvider) error

Set sets auth configuration values from config.DataProvider.

func (*Config) SetProviderDefaults

func (c *Config) SetProviderDefaults(dp config.DataProvider)

SetProviderDefaults sets default configuration values for auth in config.DataProvider.

type GRPCClientConfig

type GRPCClientConfig struct {
	RequestTimeout time.Duration
}

type GRPCTLSConfig

type GRPCTLSConfig struct {
	Enabled    bool
	CACert     string
	ClientCert string
	ClientKey  string
}

GRPCTLSConfig is a configuration of how gRPC connection will be secured.

type HTTPClientConfig

type HTTPClientConfig struct {
	RequestTimeout time.Duration
}

type IntrospectionCacheConfig

type IntrospectionCacheConfig struct {
	Enabled    bool
	MaxEntries int
	TTL        time.Duration
}

IntrospectionCacheConfig is a configuration of how claims cache will be used for introspection.

type IntrospectionConfig

type IntrospectionConfig struct {
	Enabled bool

	Endpoint         string
	AccessTokenScope []string

	ClaimsCache   IntrospectionCacheConfig
	NegativeCache IntrospectionCacheConfig

	GRPC IntrospectionGRPCConfig
}

IntrospectionConfig is a configuration of how token introspection will be used.

type IntrospectionGRPCConfig

type IntrospectionGRPCConfig struct {
	Target         string
	RequestTimeout time.Duration
	TLS            GRPCTLSConfig
}

IntrospectionGRPCConfig is a configuration of how token will be introspected via gRPC.

type JWKSConfig

type JWKSConfig struct {
	Cache struct {
		UpdateMinInterval time.Duration
	}
}

JWKSConfig is configuration of how JWKS will be used.

type JWTAuthMiddlewareOption

type JWTAuthMiddlewareOption func(options *jwtAuthMiddlewareOpts)

JWTAuthMiddlewareOption is an option for JWTAuthMiddleware.

func WithJWTAuthMiddlewareTokenIntrospector

func WithJWTAuthMiddlewareTokenIntrospector(tokenIntrospector TokenIntrospector) JWTAuthMiddlewareOption

WithJWTAuthMiddlewareTokenIntrospector is an option to set a token introspector for JWTAuthMiddleware.

func WithJWTAuthMiddlewareVerifyAccess

func WithJWTAuthMiddlewareVerifyAccess(verifyAccess func(r *http.Request, claims *jwt.Claims) bool) JWTAuthMiddlewareOption

WithJWTAuthMiddlewareVerifyAccess is an option to set a function that verifies access for JWTAuthMiddleware.

type JWTConfig

type JWTConfig struct {
	TrustedIssuers    map[string]string
	TrustedIssuerURLs []string
	RequireAudience   bool
	ExpectedAudience  []string
	ClaimsCache       ClaimsCacheConfig
}

JWTConfig is configuration of how JWT will be verified.

type JWTParser

type JWTParser interface {
	Parse(ctx context.Context, token string) (*jwt.Claims, error)
}

JWTParser is an interface for parsing string representation of JWT.

func NewJWTParser

func NewJWTParser(cfg *Config, opts ...JWTParserOption) (JWTParser, error)

NewJWTParser creates a new JWTParser with the given configuration. If cfg.JWT.ClaimsCache.Enabled is true, then jwt.CachingParser created, otherwise - jwt.Parser.

type JWTParserOption

type JWTParserOption func(options *jwtParserOptions)

JWTParserOption is an option for creating JWTParser.

func WithJWTParserLogger

func WithJWTParserLogger(logger log.FieldLogger) JWTParserOption

WithJWTParserLogger sets the logger for JWTParser.

func WithJWTParserPrometheusLibInstanceLabel

func WithJWTParserPrometheusLibInstanceLabel(label string) JWTParserOption

WithJWTParserPrometheusLibInstanceLabel sets the Prometheus lib instance label for JWTParser.

func WithJWTParserTrustedIssuerNotFoundFallback

func WithJWTParserTrustedIssuerNotFoundFallback(fallback jwt.TrustedIssNotFoundFallback) JWTParserOption

WithJWTParserTrustedIssuerNotFoundFallback sets the fallback for JWTParser when trusted issuer is not found.

type Role

type Role struct {
	Namespace string
	Name      string
}

Role is a representation of role which may be used for verifying access.

type TokenIntrospector

type TokenIntrospector interface {
	IntrospectToken(ctx context.Context, token string) (idptoken.IntrospectionResult, error)
}

TokenIntrospector is an interface for introspecting tokens.

func NewTokenIntrospector

func NewTokenIntrospector(
	cfg *Config,
	tokenProvider idptoken.IntrospectionTokenProvider,
	scopeFilter []idptoken.IntrospectionScopeFilterAccessPolicy,
	opts ...TokenIntrospectorOption,
) (TokenIntrospector, error)

NewTokenIntrospector creates a new TokenIntrospector with the given configuration, token provider and scope filter. If cfg.Introspection.ClaimsCache.Enabled or cfg.Introspection.NegativeCache.Enabled is true, then idptoken.CachingIntrospector created, otherwise - idptoken.Introspector. Please note that the tokenProvider should be able to provide access token with the policy for introspection. scopeFilter is a list of filters that will be applied to the introspected token.

type TokenIntrospectorOption

type TokenIntrospectorOption func(options *tokenIntrospectorOptions)

TokenIntrospectorOption is an option for creating TokenIntrospector.

func WithTokenIntrospectorLogger

func WithTokenIntrospectorLogger(logger log.FieldLogger) TokenIntrospectorOption

WithTokenIntrospectorLogger sets the logger for TokenIntrospector.

func WithTokenIntrospectorPrometheusLibInstanceLabel

func WithTokenIntrospectorPrometheusLibInstanceLabel(label string) TokenIntrospectorOption

WithTokenIntrospectorPrometheusLibInstanceLabel sets the Prometheus lib instance label for TokenIntrospector.

func WithTokenIntrospectorTrustedIssuerNotFoundFallback

func WithTokenIntrospectorTrustedIssuerNotFoundFallback(
	fallback idptoken.TrustedIssNotFoundFallback,
) TokenIntrospectorOption

WithTokenIntrospectorTrustedIssuerNotFoundFallback sets the fallback for TokenIntrospector when trusted issuer is not found.

Directories

Path Synopsis
Package idptest provides helper primitives and functions required for testing signing and key generation and a simple HTTP server with JWKS, issuer and IDP configuration endpoints.
Package idptest provides helper primitives and functions required for testing signing and key generation and a simple HTTP server with JWKS, issuer and IDP configuration endpoints.
Package idptoken provides a robust way to request access tokens from IDP.
Package idptoken provides a robust way to request access tokens from IDP.
pb
internal
idputil
Package idputil provides utilities for working with identity providers.
Package idputil provides utilities for working with identity providers.
libinfo
Package libinfo provides helpers for working with the library information.
Package libinfo provides helpers for working with the library information.
metrics
Package metrics provides helpers for working with the library metrics.
Package metrics provides helpers for working with the library metrics.
testing
Package testing provides internal testing utilities.
Package testing provides internal testing utilities.
Package jwks contains clients for getting public keys from JWKS.
Package jwks contains clients for getting public keys from JWKS.
Package jwt provides primitives for working with JWT (Parser, Claims, and so on).
Package jwt provides primitives for working with JWT (Parser, Claims, and so on).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL