mirror of
				https://github.com/go-micro/go-micro.git
				synced 2025-10-30 23:27:41 +02:00 
			
		
		
		
	Service => Service Auth
This commit is contained in:
		| @@ -8,8 +8,12 @@ import ( | ||||
| ) | ||||
|  | ||||
| type Options struct { | ||||
| 	// Token is an auth token | ||||
| 	Token string | ||||
| 	// ID is the services auth ID | ||||
| 	ID string | ||||
| 	// Secret is used to generate new tokens | ||||
| 	Secret string | ||||
| 	// Token is the services token used to authenticate itself | ||||
| 	Token *Token | ||||
| 	// Public key base64 encoded | ||||
| 	PublicKey string | ||||
| 	// Private key base64 encoded | ||||
| @@ -45,10 +49,11 @@ func PrivateKey(key string) Option { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ServiceToken sets an auth token | ||||
| func ServiceToken(t string) Option { | ||||
| // Credentials sets the auth credentials | ||||
| func Credentials(id, secret string) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Token = t | ||||
| 		o.ID = id | ||||
| 		o.Secret = secret | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -55,13 +55,13 @@ func (s *svc) Init(opts ...auth.Option) { | ||||
| 	} | ||||
|  | ||||
| 	// load rules periodically from the auth service | ||||
| 	timer := time.NewTicker(time.Second * 30) | ||||
| 	ruleTimer := time.NewTicker(time.Second * 30) | ||||
| 	go func() { | ||||
| 		// load rules immediately on startup | ||||
| 		s.loadRules() | ||||
|  | ||||
| 		for { | ||||
| 			<-timer.C | ||||
| 			<-ruleTimer.C | ||||
|  | ||||
| 			// jitter for up to 5 seconds, this stops | ||||
| 			// all the services calling the auth service | ||||
| @@ -70,9 +70,39 @@ func (s *svc) Init(opts ...auth.Option) { | ||||
| 			s.loadRules() | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	// we have client credentials and must load a new token | ||||
| 	// periodically | ||||
| 	if len(s.options.ID) > 0 || len(s.options.Secret) > 0 { | ||||
| 		tokenTimer := time.NewTicker(time.Minute) | ||||
|  | ||||
| 		go func() { | ||||
| 			s.loadToken() | ||||
|  | ||||
| 			for { | ||||
| 				<-tokenTimer.C | ||||
|  | ||||
| 				// Do not get a new token if the current one has more than three | ||||
| 				// minutes remaining. We do 3 minutes to allow multiple retires in | ||||
| 				// the case one request fails | ||||
| 				t := s.Options().Token | ||||
| 				if t != nil && t.Expiry.Unix() > time.Now().Add(time.Minute*3).Unix() { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				// jitter for up to 5 seconds, this stops | ||||
| 				// all the services calling the auth service | ||||
| 				// at the exact same time | ||||
| 				time.Sleep(jitter.Do(time.Second * 5)) | ||||
| 				s.loadToken() | ||||
| 			} | ||||
| 		}() | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *svc) Options() auth.Options { | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
| 	return s.options | ||||
| } | ||||
|  | ||||
| @@ -256,6 +286,24 @@ func (s *svc) loadRules() { | ||||
| 	s.rules = rsp.Rules | ||||
| } | ||||
|  | ||||
| // loadToken generates a new token for the service to use when making calls | ||||
| func (s *svc) loadToken() { | ||||
| 	rsp, err := s.auth.Token(context.TODO(), &pb.TokenRequest{ | ||||
| 		Id:          s.Options().ID, | ||||
| 		Secret:      s.Options().Secret, | ||||
| 		TokenExpiry: int64((time.Minute * 15).Seconds()), | ||||
| 	}) | ||||
| 	s.Lock() | ||||
| 	defer s.Unlock() | ||||
|  | ||||
| 	if err != nil { | ||||
| 		log.Errorf("Error generating token: %v", err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	s.options.Token = serializeToken(rsp.Token) | ||||
| } | ||||
|  | ||||
| func serializeToken(t *pb.Token) *auth.Token { | ||||
| 	return &auth.Token{ | ||||
| 		Token:    t.Token, | ||||
|   | ||||
| @@ -131,7 +131,15 @@ func (g *grpcClient) call(ctx context.Context, node *registry.Node, req client.R | ||||
| 	// set the content type for the request | ||||
| 	header["x-content-type"] = req.ContentType() | ||||
|  | ||||
| 	// set the authorization token if one is saved locally | ||||
| 	// if the caller specifies using service privelages, and the client | ||||
| 	// has auth set, override the authorization header | ||||
| 	if opts.WithServicePrivileges && g.opts.Auth != nil && g.opts.Auth.Options().Token != nil { | ||||
| 		t := g.opts.Auth.Options().Token | ||||
| 		header["authorization"] = auth.BearerScheme + t.Token | ||||
| 	} | ||||
|  | ||||
| 	// fall back to using the authorization token set in config, | ||||
| 	// this enables the CLI to provide a token | ||||
| 	if len(header["authorization"]) == 0 { | ||||
| 		if token, err := config.Get("token"); err == nil && len(token) > 0 { | ||||
| 			header["authorization"] = auth.BearerScheme + token | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"context" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/micro/go-micro/v2/auth" | ||||
| 	"github.com/micro/go-micro/v2/broker" | ||||
| 	"github.com/micro/go-micro/v2/client/selector" | ||||
| 	"github.com/micro/go-micro/v2/codec" | ||||
| @@ -16,6 +17,7 @@ type Options struct { | ||||
| 	ContentType string | ||||
|  | ||||
| 	// Plugged interfaces | ||||
| 	Auth      auth.Auth | ||||
| 	Broker    broker.Broker | ||||
| 	Codecs    map[string]codec.NewCodec | ||||
| 	Registry  registry.Registry | ||||
| @@ -55,6 +57,8 @@ type CallOptions struct { | ||||
| 	Retries int | ||||
| 	// Request/Response timeout | ||||
| 	RequestTimeout time.Duration | ||||
| 	// Use the services own auth token | ||||
| 	WithServicePrivileges bool | ||||
|  | ||||
| 	// Middleware for low level call func | ||||
| 	CallWrappers []CallWrapper | ||||
| @@ -99,6 +103,7 @@ func NewOptions(options ...Option) Options { | ||||
| 		}, | ||||
| 		PoolSize:  DefaultPoolSize, | ||||
| 		PoolTTL:   DefaultPoolTTL, | ||||
| 		Auth:      auth.DefaultAuth, | ||||
| 		Broker:    broker.DefaultBroker, | ||||
| 		Selector:  selector.DefaultSelector, | ||||
| 		Registry:  registry.DefaultRegistry, | ||||
| @@ -119,6 +124,13 @@ func Broker(b broker.Broker) Option { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Auth to be used when making a request | ||||
| func Auth(a auth.Auth) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Auth = a | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // Codec to be used to encode/decode requests for a given content type | ||||
| func Codec(contentType string, c codec.NewCodec) Option { | ||||
| 	return func(o *Options) { | ||||
| @@ -291,6 +303,14 @@ func WithDialTimeout(d time.Duration) CallOption { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithServicePrivileges is a CallOption which overrides the | ||||
| // authorization header with the services own auth token | ||||
| func WithServicePrivileges() CallOption { | ||||
| 	return func(o *CallOptions) { | ||||
| 		o.WithServicePrivileges = true | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithMessageContentType(ct string) MessageOption { | ||||
| 	return func(o *MessageOptions) { | ||||
| 		o.ContentType = ct | ||||
|   | ||||
| @@ -255,9 +255,14 @@ var ( | ||||
| 			Usage:   "Auth for role based access control, e.g. service", | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:    "auth_token", | ||||
| 			EnvVars: []string{"MICRO_AUTH_TOKEN"}, | ||||
| 			Usage:   "Auth token used for client authentication", | ||||
| 			Name:    "auth_id", | ||||
| 			EnvVars: []string{"MICRO_AUTH_ID"}, | ||||
| 			Usage:   "Account ID used for client authentication", | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:    "auth_secret", | ||||
| 			EnvVars: []string{"MICRO_AUTH_SECRET"}, | ||||
| 			Usage:   "Account secret used for client authentication", | ||||
| 		}, | ||||
| 		&cli.StringFlag{ | ||||
| 			Name:    "auth_public_key", | ||||
| @@ -488,6 +493,7 @@ func (c *cmd) Before(ctx *cli.Context) error { | ||||
| 		} | ||||
|  | ||||
| 		*c.opts.Auth = a() | ||||
| 		clientOpts = append(clientOpts, client.Auth(*c.opts.Auth)) | ||||
| 	} | ||||
|  | ||||
| 	// Set the profile | ||||
| @@ -655,8 +661,10 @@ func (c *cmd) Before(ctx *cli.Context) error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(ctx.String("auth_token")) > 0 { | ||||
| 		authOpts = append(authOpts, auth.ServiceToken(ctx.String("auth_token"))) | ||||
| 	if len(ctx.String("auth_id")) > 0 || len(ctx.String("auth_secret")) > 0 { | ||||
| 		authOpts = append(authOpts, auth.Credentials( | ||||
| 			ctx.String("auth_id"), ctx.String("auth_secret"), | ||||
| 		)) | ||||
| 	} | ||||
|  | ||||
| 	if len(ctx.String("auth_public_key")) > 0 { | ||||
|   | ||||
| @@ -142,6 +142,7 @@ func Tracer(t trace.Tracer) Option { | ||||
| func Auth(a auth.Auth) Option { | ||||
| 	return func(o *Options) { | ||||
| 		o.Auth = a | ||||
| 		o.Client.Init(client.Auth(a)) | ||||
| 		o.Server.Init(server.Auth(a)) | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -18,7 +18,6 @@ import ( | ||||
| 	"github.com/micro/go-micro/v2/plugin" | ||||
| 	"github.com/micro/go-micro/v2/server" | ||||
| 	"github.com/micro/go-micro/v2/store" | ||||
| 	"github.com/micro/go-micro/v2/util/config" | ||||
| 	"github.com/micro/go-micro/v2/util/wrapper" | ||||
| ) | ||||
|  | ||||
| @@ -117,9 +116,9 @@ func (s *service) Init(opts ...Option) { | ||||
| 		// Right now we're just going to load a token | ||||
| 		// May need to re-read value on change | ||||
| 		// TODO: should be scoped to micro/auth/token | ||||
| 		if tk, _ := config.Get("token"); len(tk) > 0 { | ||||
| 			s.opts.Auth.Init(auth.ServiceToken(tk)) | ||||
| 		} | ||||
| 		// if tk, _ := config.Get("token"); len(tk) > 0 { | ||||
| 		// 	s.opts.Auth.Init(auth.ServiceToken(tk)) | ||||
| 		// } | ||||
| 	}) | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -38,15 +38,6 @@ func (c *clientWrapper) setHeaders(ctx context.Context) context.Context { | ||||
| 	mda, _ := metadata.FromContext(ctx) | ||||
| 	md := metadata.Copy(mda) | ||||
|  | ||||
| 	// get auth token | ||||
| 	if a := c.auth(); a != nil { | ||||
| 		tk := a.Options().Token | ||||
| 		// if the token if exists and auth header isn't set then set it | ||||
| 		if len(tk) > 0 && len(md["Authorization"]) == 0 { | ||||
| 			md["Authorization"] = auth.BearerScheme + tk | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// set headers | ||||
| 	for k, v := range c.headers { | ||||
| 		if _, ok := md[k]; !ok { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user