190
									
								
								auth/jwt/jwt.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								auth/jwt/jwt.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,190 @@ | |||||||
|  | package jwt | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"github.com/micro/go-micro/v2/auth" | ||||||
|  | 	"github.com/micro/go-micro/v2/auth/token" | ||||||
|  | 	jwtToken "github.com/micro/go-micro/v2/auth/token/jwt" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // NewAuth returns a new instance of the Auth service | ||||||
|  | func NewAuth(opts ...auth.Option) auth.Auth { | ||||||
|  | 	j := new(jwt) | ||||||
|  | 	j.Init(opts...) | ||||||
|  | 	return j | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type rule struct { | ||||||
|  | 	role     string | ||||||
|  | 	resource *auth.Resource | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type jwt struct { | ||||||
|  | 	options auth.Options | ||||||
|  | 	jwt     token.Provider | ||||||
|  | 	rules   []*rule | ||||||
|  |  | ||||||
|  | 	sync.Mutex | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) String() string { | ||||||
|  | 	return "jwt" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Init(opts ...auth.Option) { | ||||||
|  | 	j.Lock() | ||||||
|  | 	defer j.Unlock() | ||||||
|  |  | ||||||
|  | 	for _, o := range opts { | ||||||
|  | 		o(&j.options) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(j.options.Namespace) == 0 { | ||||||
|  | 		j.options.Namespace = auth.DefaultNamespace | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	j.jwt = jwtToken.NewTokenProvider( | ||||||
|  | 		token.WithPrivateKey(j.options.PrivateKey), | ||||||
|  | 		token.WithPublicKey(j.options.PublicKey), | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Options() auth.Options { | ||||||
|  | 	j.Lock() | ||||||
|  | 	defer j.Unlock() | ||||||
|  | 	return j.options | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { | ||||||
|  | 	options := auth.NewGenerateOptions(opts...) | ||||||
|  | 	account := &auth.Account{ | ||||||
|  | 		ID:        id, | ||||||
|  | 		Type:      options.Type, | ||||||
|  | 		Roles:     options.Roles, | ||||||
|  | 		Provider:  options.Provider, | ||||||
|  | 		Metadata:  options.Metadata, | ||||||
|  | 		Namespace: options.Namespace, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// generate a JWT secret which can be provided to the Token() method | ||||||
|  | 	// and exchanged for an access token | ||||||
|  | 	secret, err := j.jwt.Generate(account) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	account.Secret = secret.Token | ||||||
|  |  | ||||||
|  | 	// return the account | ||||||
|  | 	return account, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Grant(role string, res *auth.Resource) error { | ||||||
|  | 	j.Lock() | ||||||
|  | 	defer j.Unlock() | ||||||
|  | 	j.rules = append(j.rules, &rule{role, res}) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Revoke(role string, res *auth.Resource) error { | ||||||
|  | 	j.Lock() | ||||||
|  | 	defer j.Unlock() | ||||||
|  |  | ||||||
|  | 	rules := make([]*rule, 0, len(j.rules)) | ||||||
|  |  | ||||||
|  | 	var ruleFound bool | ||||||
|  | 	for _, r := range rules { | ||||||
|  | 		if r.role == role && r.resource == res { | ||||||
|  | 			ruleFound = true | ||||||
|  | 		} else { | ||||||
|  | 			rules = append(rules, r) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if !ruleFound { | ||||||
|  | 		return auth.ErrNotFound | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	j.rules = rules | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Verify(acc *auth.Account, res *auth.Resource) error { | ||||||
|  | 	j.Lock() | ||||||
|  | 	if len(res.Namespace) == 0 { | ||||||
|  | 		res.Namespace = j.options.Namespace | ||||||
|  | 	} | ||||||
|  | 	rules := j.rules | ||||||
|  | 	j.Unlock() | ||||||
|  |  | ||||||
|  | 	for _, rule := range rules { | ||||||
|  | 		// validate the rule applies to the requested resource | ||||||
|  | 		if rule.resource.Namespace != "*" && rule.resource.Namespace != res.Namespace { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if rule.resource.Type != "*" && rule.resource.Type != res.Type { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if rule.resource.Name != "*" && rule.resource.Name != res.Name { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		if rule.resource.Endpoint != "*" && rule.resource.Endpoint != res.Endpoint { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// a blank role indicates anyone can access the resource, even without an account | ||||||
|  | 		if rule.role == "" { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// all furter checks require an account | ||||||
|  | 		if acc == nil { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// this rule allows any account access, allow the request | ||||||
|  | 		if rule.role == "*" { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// if the account has the necessary role, allow the request | ||||||
|  | 		for _, r := range acc.Roles { | ||||||
|  | 			if r == rule.role { | ||||||
|  | 				return nil | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// no rules matched, forbid the request | ||||||
|  | 	return auth.ErrForbidden | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Inspect(token string) (*auth.Account, error) { | ||||||
|  | 	return j.jwt.Inspect(token) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (j *jwt) Token(opts ...auth.TokenOption) (*auth.Token, error) { | ||||||
|  | 	options := auth.NewTokenOptions(opts...) | ||||||
|  |  | ||||||
|  | 	secret := options.RefreshToken | ||||||
|  | 	if len(options.Secret) > 0 { | ||||||
|  | 		secret = options.Secret | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	account, err := j.jwt.Inspect(secret) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	tok, err := j.jwt.Generate(account, token.WithExpiry(options.Expiry)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return &auth.Token{ | ||||||
|  | 		Created:      tok.Created, | ||||||
|  | 		Expiry:       tok.Expiry, | ||||||
|  | 		AccessToken:  tok.Token, | ||||||
|  | 		RefreshToken: tok.Token, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
| @@ -70,6 +70,7 @@ import ( | |||||||
| 	memTracer "github.com/micro/go-micro/v2/debug/trace/memory" | 	memTracer "github.com/micro/go-micro/v2/debug/trace/memory" | ||||||
|  |  | ||||||
| 	// auth | 	// auth | ||||||
|  | 	jwtAuth "github.com/micro/go-micro/v2/auth/jwt" | ||||||
| 	svcAuth "github.com/micro/go-micro/v2/auth/service" | 	svcAuth "github.com/micro/go-micro/v2/auth/service" | ||||||
|  |  | ||||||
| 	// auth providers | 	// auth providers | ||||||
| @@ -369,6 +370,7 @@ var ( | |||||||
|  |  | ||||||
| 	DefaultAuths = map[string]func(...auth.Option) auth.Auth{ | 	DefaultAuths = map[string]func(...auth.Option) auth.Auth{ | ||||||
| 		"service": svcAuth.NewAuth, | 		"service": svcAuth.NewAuth, | ||||||
|  | 		"jwt":     jwtAuth.NewAuth, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	DefaultAuthProviders = map[string]func(...provider.Option) provider.Provider{ | 	DefaultAuthProviders = map[string]func(...provider.Option) provider.Provider{ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user