Updated auth interface (#1384)

* Updated  auth interface

* Add Rule

* Remove Rule

* Return token from Renew

* Renew => Refresh

* Implement Tokens & Default Auth Implementation

* Change default auth to noop

* Change default auth to noop

* Move token.Token to auth.Token

* Remove Token from Account

* Auth service implementation

* Decode JWT locally

* Cookie for secret

* Move string to bottom of interface definition

* Depricate auth_exclude

* Update auth wrappers

* Update go.sum

Co-authored-by: Ben Toogood <ben@micro.mu>
This commit is contained in:
ben-toogood 2020-03-23 16:19:30 +00:00 committed by GitHub
parent 9826ddbd64
commit e0e77f3983
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1842 additions and 649 deletions

View File

@ -43,44 +43,42 @@ func (h authHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
} else {
// Get the token out the cookies if not provided in headers
if c, err := req.Cookie("micro-token"); err == nil && c != nil {
token = strings.TrimPrefix(c.Value, auth.CookieName+"=")
token = strings.TrimPrefix(c.Value, auth.TokenCookieName+"=")
req.Header.Set("Authorization", BearerScheme+token)
}
}
// Return if the user disabled auth on this endpoint
excludes := h.auth.Options().Exclude
excludes = append(excludes, DefaultExcludes...)
loginURL := h.auth.Options().LoginURL
if len(loginURL) > 0 {
excludes = append(excludes, loginURL)
// Get the account using the token, fallback to a blank account
// since some endpoints can be unauthenticated, so the lack of an
// account doesn't necesserially mean a forbidden request
acc, err := h.auth.Inspect(token)
if err != nil {
acc = &auth.Account{}
}
err = h.auth.Verify(acc, &auth.Resource{
Type: "service",
Name: "go.micro.web",
Endpoint: req.URL.Path,
})
for _, e := range excludes {
// is a standard exclude, e.g. /rpc
if e == req.URL.Path {
// The account has the necessary permissions to access the
// resource
if err == nil {
h.handler.ServeHTTP(w, req)
return
}
// is a wildcard exclude, e.g. /services/*
wildcard := strings.Replace(e, "*", "", 1)
if strings.HasSuffix(e, "*") && strings.HasPrefix(req.URL.Path, wildcard) {
h.handler.ServeHTTP(w, req)
return
}
}
// If the token is valid, allow the request
if _, err := h.auth.Verify(token); err == nil {
h.handler.ServeHTTP(w, req)
// The account is set, but they don't have enough permissions,
// hence we 403.
if len(acc.ID) > 0 {
w.WriteHeader(http.StatusForbidden)
return
}
// If there is no auth login url set, 401
loginURL := h.auth.Options().LoginURL
if loginURL == "" {
w.WriteHeader(401)
w.WriteHeader(http.StatusUnauthorized)
return
}

View File

@ -4,24 +4,44 @@ package auth
import (
"context"
"encoding/json"
"errors"
"time"
"github.com/micro/go-micro/v2/metadata"
)
var (
// ErrNotFound is returned when a resouce cannot be found
ErrNotFound = errors.New("not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
// ErrInvalidRole is returned when the role provided was invalid
ErrInvalidRole = errors.New("invalid role")
// ErrForbidden is returned when a user does not have the necessary roles to access a resource
ErrForbidden = errors.New("resource forbidden")
)
// Auth providers authentication and authorization
type Auth interface {
// Init the auth package
Init(opts ...Option) error
// Options returns the options set
// Init the auth
Init(opts ...Option)
// Options set for auth
Options() Options
// Generate a new auth Account
// Generate a new account
Generate(id string, opts ...GenerateOption) (*Account, error)
// Revoke an authorization Account
Revoke(token string) error
// Verify an account token
Verify(token string) (*Account, error)
// String returns the implementation
// Grant access to a resource
Grant(role string, res *Resource) error
// Revoke access to a resource
Revoke(role string, res *Resource) error
// Verify an account has access to a resource
Verify(acc *Account, res *Resource) error
// Inspect a token
Inspect(token string) (*Account, error)
// Refresh an account using a secret
Refresh(secret string, opts ...RefreshOption) (*Token, error)
// String returns the name of the implementation
String() string
}
@ -31,40 +51,47 @@ type Resource struct {
Name string
// Type of resource, e.g.
Type string
}
// Role an account has
type Role struct {
// Name of the role
Name string
// The resource it has access
// TODO: potentially remove
Resource *Resource
// Endpoint resource e.g NotesService.Create
Endpoint string
}
// Account provided by an auth provider
type Account struct {
// ID of the account (UUIDV4, email or username)
Id string `json:"id"`
// Token used to authenticate
Token string `json:"token"`
// Time of Account creation
Created time.Time `json:"created"`
// Time of Account expiry
Expiry time.Time `json:"expiry"`
ID string `json:"id"`
// Secret used to renew the account
Secret *Token `json:"secret"`
// Roles associated with the Account
Roles []*Role `json:"roles"`
Roles []string `json:"roles"`
// Any other associated metadata
Metadata map[string]string `json:"metadata"`
}
// Token can be short or long lived
type Token struct {
// The token itself
Token string `json:"token"`
// Type of token, e.g. JWT
Type string `json:"type"`
// Time of token creation
Created time.Time `json:"created"`
// Time of token expiry
Expiry time.Time `json:"expiry"`
// Subject of the token, e.g. the account ID
Subject string `json:"subject"`
// Roles granted to the token
Roles []string `json:"roles"`
// Metadata embedded in the token
Metadata map[string]string `json:"metadata"`
}
const (
// MetadataKey is the key used when storing the account
// in metadata
// MetadataKey is the key used when storing the account in metadata
MetadataKey = "auth-account"
// CookieName is the name of the cookie which stores the
// auth token
CookieName = "micro-token"
// TokenCookieName is the name of the cookie which stores the auth token
TokenCookieName = "micro-token"
// SecretCookieName is the name of the cookie which stores the auth secret
SecretCookieName = "micro-secret"
)
// AccountFromContext gets the account from the context, which

View File

@ -1,122 +1,70 @@
package auth
import (
"encoding/base32"
"sync"
"time"
"github.com/google/uuid"
)
var (
DefaultAuth = NewAuth()
)
func genAccount(id string) *Account {
// return a pseudo account
return &Account{
Id: id,
Token: base32.StdEncoding.EncodeToString([]byte(id)),
Created: time.Now(),
Expiry: time.Now().Add(time.Hour * 24),
Metadata: make(map[string]string),
}
}
// NewAuth returns a new default registry which is memory
func NewAuth(opts ...Option) Auth {
var options Options
for _, o := range opts {
o(&options)
return &noop{}
}
return &memory{
accounts: make(map[string]*Account),
opts: options,
}
}
// TODO: replace with https://github.com/nats-io/nkeys
// We'll then register public key in registry to use
type memory struct {
type noop struct {
opts Options
// accounts
sync.RWMutex
accounts map[string]*Account
}
func (n *memory) Init(opts ...Option) error {
// String returns the name of the implementation
func (n *noop) String() string {
return "noop"
}
// Init the auth
func (n *noop) Init(opts ...Option) {
for _, o := range opts {
o(&n.opts)
}
return nil
}
func (n *memory) Options() Options {
// Options set for auth
func (n *noop) Options() Options {
return n.opts
}
func (n *memory) Generate(id string, opts ...GenerateOption) (*Account, error) {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
// Generate a new account
func (n *noop) Generate(id string, opts ...GenerateOption) (*Account, error) {
options := NewGenerateOptions(opts...)
// return a pseudo account
acc := genAccount(id)
// set opts
if len(options.Roles) > 0 {
acc.Roles = options.Roles
}
if options.Metadata != nil {
acc.Metadata = options.Metadata
}
// TODO: don't overwrite
n.Lock()
// maybe save by account id?
n.accounts[acc.Token] = acc
n.Unlock()
return acc, nil
}
func (n *memory) Revoke(token string) error {
n.Lock()
delete(n.accounts, token)
n.Unlock()
return nil
}
func (n *memory) Verify(token string) (*Account, error) {
n.RLock()
defer n.RUnlock()
if len(token) == 0 {
// pseudo account?
return genAccount(""), nil
}
// try get the local account if it exists
if acc, ok := n.accounts[token]; ok {
return acc, nil
}
// decode the token otherwise
b, err := base32.StdEncoding.DecodeString(token)
if err != nil {
return genAccount(""), nil
}
// return a pseudo account based on token/id
return &Account{
Id: string(b),
Token: token,
Created: time.Now(),
Expiry: time.Now().Add(time.Hour * 24),
Metadata: make(map[string]string),
ID: id,
Roles: options.Roles,
Metadata: options.Metadata,
}, nil
}
func (n *memory) String() string {
return "memory"
// Grant access to a resource
func (n *noop) Grant(role string, res *Resource) error {
return nil
}
// Revoke access to a resource
func (n *noop) Revoke(role string, res *Resource) error {
return nil
}
// Verify an account has access to a resource
func (n *noop) Verify(acc *Account, res *Resource) error {
return nil
}
// Inspect a token
func (n *noop) Inspect(token string) (*Account, error) {
return &Account{ID: uuid.New().String()}, nil
}
// Refresh an account using a secret
func (n *noop) Refresh(secret string, opts ...RefreshOption) (*Token, error) {
return &Token{}, nil
}

View File

@ -1,133 +0,0 @@
package jwt
import (
"encoding/base64"
"errors"
"github.com/dgrijalva/jwt-go"
"github.com/micro/go-micro/v2/auth"
)
var (
// ErrInvalidPrivateKey is returned when the service provided an invalid private key
ErrInvalidPrivateKey = errors.New("An invalid private key was provided")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("An error occured while encoding the JWT")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("An invalid token was provided")
// ErrMissingToken is returned when no token is provided
ErrMissingToken = errors.New("A valid JWT is required")
)
// NewAuth returns a new instance of the Auth service
func NewAuth(opts ...auth.Option) auth.Auth {
svc := new(svc)
svc.Init(opts...)
return svc
}
// svc is the JWT implementation of the Auth interface
type svc struct {
options auth.Options
}
func (s *svc) String() string {
return "jwt"
}
func (s *svc) Options() auth.Options {
return s.options
}
func (s *svc) Init(opts ...auth.Option) error {
for _, o := range opts {
o(&s.options)
}
return nil
}
// AuthClaims to be encoded in the JWT
type AuthClaims struct {
Roles []*auth.Role `json:"roles"`
Metadata map[string]string `json:"metadata"`
jwt.StandardClaims
}
// Generate a new JWT
func (s *svc) Generate(id string, ops ...auth.GenerateOption) (*auth.Account, error) {
// decode the private key
priv, err := base64.StdEncoding.DecodeString(s.options.PrivateKey)
if err != nil {
return nil, err
}
key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
if err != nil {
return nil, ErrEncodingToken
}
options := auth.NewGenerateOptions(ops...)
account := jwt.NewWithClaims(jwt.SigningMethodRS256, AuthClaims{
options.Roles, options.Metadata, jwt.StandardClaims{
Subject: id,
ExpiresAt: options.Expiry.Unix(),
},
})
token, err := account.SignedString(key)
if err != nil {
return nil, err
}
return &auth.Account{
Id: id,
Token: token,
Roles: options.Roles,
Metadata: options.Metadata,
}, nil
}
// Revoke an authorization account
func (s *svc) Revoke(token string) error {
return nil
}
// Verify a JWT
func (s *svc) Verify(token string) (*auth.Account, error) {
if token == "" {
return nil, ErrMissingToken
}
// decode the public key
pub, err := base64.StdEncoding.DecodeString(s.options.PublicKey)
if err != nil {
return nil, err
}
res, err := jwt.ParseWithClaims(token, &AuthClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(pub)
})
if err != nil {
return nil, err
}
if !res.Valid {
return nil, ErrInvalidToken
}
claims, ok := res.Claims.(*AuthClaims)
if !ok {
return nil, ErrInvalidToken
}
return &auth.Account{
Id: claims.Subject,
Metadata: claims.Metadata,
Roles: claims.Roles,
}, nil
}

View File

@ -4,6 +4,7 @@ import (
"time"
"github.com/micro/go-micro/v2/auth/provider"
"github.com/micro/go-micro/v2/store"
)
type Options struct {
@ -13,20 +14,20 @@ type Options struct {
PublicKey string
// Private key base64 encoded
PrivateKey string
// Endpoints to exclude
Exclude []string
// Provider is an auth provider
Provider provider.Provider
// LoginURL is the relative url path where a user can login
LoginURL string
// Store to back auth
Store store.Store
}
type Option func(o *Options)
// Exclude ecludes a set of endpoints from authorization
func Exclude(e ...string) Option {
// Store to back auth
func Store(s store.Store) Option {
return func(o *Options) {
o.Exclude = e
o.Store = s
}
}
@ -44,8 +45,8 @@ func PrivateKey(key string) Option {
}
}
// Token sets an auth token
func Token(t string) Option {
// ServiceToken sets an auth token
func ServiceToken(t string) Option {
return func(o *Options) {
o.Token = t
}
@ -69,31 +70,31 @@ type GenerateOptions struct {
// Metadata associated with the account
Metadata map[string]string
// Roles/scopes associated with the account
Roles []*Role
//Expiry of the token
Expiry time.Time
Roles []string
// SecretExpiry is the time the secret should live for
SecretExpiry time.Duration
}
type GenerateOption func(o *GenerateOptions)
// Metadata for the generated account
func Metadata(md map[string]string) func(o *GenerateOptions) {
// WithMetadata for the generated account
func WithMetadata(md map[string]string) GenerateOption {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// Roles for the generated account
func Roles(rs []*Role) func(o *GenerateOptions) {
// WithRoles for the generated account
func WithRoles(rs []string) GenerateOption {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// Expiry for the generated account's token expires
func Expiry(ex time.Time) func(o *GenerateOptions) {
// WithSecretExpiry for the generated account's secret expires
func WithSecretExpiry(ex time.Duration) GenerateOption {
return func(o *GenerateOptions) {
o.Expiry = ex
o.SecretExpiry = ex
}
}
@ -103,9 +104,40 @@ func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
for _, o := range opts {
o(&options)
}
//set defualt expiry of token
if options.Expiry.IsZero() {
options.Expiry = time.Now().Add(time.Hour * 24)
// set defualt expiry of secret
if options.SecretExpiry == 0 {
options.SecretExpiry = time.Hour * 24 * 7
}
return options
}
type RefreshOptions struct {
// TokenExpiry is the time the token should live for
TokenExpiry time.Duration
}
type RefreshOption func(o *RefreshOptions)
// WithTokenExpiry for the token
func WithTokenExpiry(ex time.Duration) RefreshOption {
return func(o *RefreshOptions) {
o.TokenExpiry = ex
}
}
// NewRefreshOptions from a slice of options
func NewRefreshOptions(opts ...RefreshOption) RefreshOptions {
var options RefreshOptions
for _, o := range opts {
o(&options)
}
// set defualt expiry of token
if options.TokenExpiry == 0 {
options.TokenExpiry = time.Minute
}
return options
}

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// source: micro/go-micro/auth/service/proto/auth.proto
// source: auth/service/proto/auth.proto
package go_micro_auth
@ -20,13 +20,98 @@ var _ = math.Inf
// proto package needs to be updated.
const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
type Account struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Token string `protobuf:"bytes,2,opt,name=token,proto3" json:"token,omitempty"`
type Token struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Created int64 `protobuf:"varint,3,opt,name=created,proto3" json:"created,omitempty"`
Expiry int64 `protobuf:"varint,4,opt,name=expiry,proto3" json:"expiry,omitempty"`
Roles []*Role `protobuf:"bytes,5,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,6,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
Subject string `protobuf:"bytes,5,opt,name=subject,proto3" json:"subject,omitempty"`
Roles []string `protobuf:"bytes,6,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,7,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Token) Reset() { *m = Token{} }
func (m *Token) String() string { return proto.CompactTextString(m) }
func (*Token) ProtoMessage() {}
func (*Token) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{0}
}
func (m *Token) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Token.Unmarshal(m, b)
}
func (m *Token) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Token.Marshal(b, m, deterministic)
}
func (m *Token) XXX_Merge(src proto.Message) {
xxx_messageInfo_Token.Merge(m, src)
}
func (m *Token) XXX_Size() int {
return xxx_messageInfo_Token.Size(m)
}
func (m *Token) XXX_DiscardUnknown() {
xxx_messageInfo_Token.DiscardUnknown(m)
}
var xxx_messageInfo_Token proto.InternalMessageInfo
func (m *Token) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
func (m *Token) GetType() string {
if m != nil {
return m.Type
}
return ""
}
func (m *Token) GetCreated() int64 {
if m != nil {
return m.Created
}
return 0
}
func (m *Token) GetExpiry() int64 {
if m != nil {
return m.Expiry
}
return 0
}
func (m *Token) GetSubject() string {
if m != nil {
return m.Subject
}
return ""
}
func (m *Token) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
func (m *Token) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
type Account struct {
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Secret *Token `protobuf:"bytes,2,opt,name=secret,proto3" json:"secret,omitempty"`
Roles []string `protobuf:"bytes,3,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,4,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -36,7 +121,7 @@ func (m *Account) Reset() { *m = Account{} }
func (m *Account) String() string { return proto.CompactTextString(m) }
func (*Account) ProtoMessage() {}
func (*Account) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{0}
return fileDescriptor_21300bfacc51fc2a, []int{1}
}
func (m *Account) XXX_Unmarshal(b []byte) error {
@ -64,28 +149,14 @@ func (m *Account) GetId() string {
return ""
}
func (m *Account) GetToken() string {
func (m *Account) GetSecret() *Token {
if m != nil {
return m.Token
return m.Secret
}
return ""
return nil
}
func (m *Account) GetCreated() int64 {
if m != nil {
return m.Created
}
return 0
}
func (m *Account) GetExpiry() int64 {
if m != nil {
return m.Expiry
}
return 0
}
func (m *Account) GetRoles() []*Role {
func (m *Account) GetRoles() []string {
if m != nil {
return m.Roles
}
@ -99,56 +170,10 @@ func (m *Account) GetMetadata() map[string]string {
return nil
}
type Role struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *Role) Reset() { *m = Role{} }
func (m *Role) String() string { return proto.CompactTextString(m) }
func (*Role) ProtoMessage() {}
func (*Role) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{1}
}
func (m *Role) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_Role.Unmarshal(m, b)
}
func (m *Role) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_Role.Marshal(b, m, deterministic)
}
func (m *Role) XXX_Merge(src proto.Message) {
xxx_messageInfo_Role.Merge(m, src)
}
func (m *Role) XXX_Size() int {
return xxx_messageInfo_Role.Size(m)
}
func (m *Role) XXX_DiscardUnknown() {
xxx_messageInfo_Role.DiscardUnknown(m)
}
var xxx_messageInfo_Role proto.InternalMessageInfo
func (m *Role) GetName() string {
if m != nil {
return m.Name
}
return ""
}
func (m *Role) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
type Resource struct {
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
Type string `protobuf:"bytes,2,opt,name=type,proto3" json:"type,omitempty"`
Endpoint string `protobuf:"bytes,3,opt,name=endpoint,proto3" json:"endpoint,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -158,7 +183,7 @@ func (m *Resource) Reset() { *m = Resource{} }
func (m *Resource) String() string { return proto.CompactTextString(m) }
func (*Resource) ProtoMessage() {}
func (*Resource) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{2}
return fileDescriptor_21300bfacc51fc2a, []int{2}
}
func (m *Resource) XXX_Unmarshal(b []byte) error {
@ -193,8 +218,18 @@ func (m *Resource) GetType() string {
return ""
}
func (m *Resource) GetEndpoint() string {
if m != nil {
return m.Endpoint
}
return ""
}
type GenerateRequest struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
Roles []string `protobuf:"bytes,2,rep,name=roles,proto3" json:"roles,omitempty"`
Metadata map[string]string `protobuf:"bytes,3,rep,name=metadata,proto3" json:"metadata,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
SecretExpiry int64 `protobuf:"varint,4,opt,name=secret_expiry,json=secretExpiry,proto3" json:"secret_expiry,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -204,7 +239,7 @@ func (m *GenerateRequest) Reset() { *m = GenerateRequest{} }
func (m *GenerateRequest) String() string { return proto.CompactTextString(m) }
func (*GenerateRequest) ProtoMessage() {}
func (*GenerateRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{3}
return fileDescriptor_21300bfacc51fc2a, []int{3}
}
func (m *GenerateRequest) XXX_Unmarshal(b []byte) error {
@ -225,13 +260,34 @@ func (m *GenerateRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_GenerateRequest proto.InternalMessageInfo
func (m *GenerateRequest) GetAccount() *Account {
func (m *GenerateRequest) GetId() string {
if m != nil {
return m.Account
return m.Id
}
return ""
}
func (m *GenerateRequest) GetRoles() []string {
if m != nil {
return m.Roles
}
return nil
}
func (m *GenerateRequest) GetMetadata() map[string]string {
if m != nil {
return m.Metadata
}
return nil
}
func (m *GenerateRequest) GetSecretExpiry() int64 {
if m != nil {
return m.SecretExpiry
}
return 0
}
type GenerateResponse struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
@ -243,7 +299,7 @@ func (m *GenerateResponse) Reset() { *m = GenerateResponse{} }
func (m *GenerateResponse) String() string { return proto.CompactTextString(m) }
func (*GenerateResponse) ProtoMessage() {}
func (*GenerateResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{4}
return fileDescriptor_21300bfacc51fc2a, []int{4}
}
func (m *GenerateResponse) XXX_Unmarshal(b []byte) error {
@ -271,8 +327,87 @@ func (m *GenerateResponse) GetAccount() *Account {
return nil
}
type GrantRequest struct {
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GrantRequest) Reset() { *m = GrantRequest{} }
func (m *GrantRequest) String() string { return proto.CompactTextString(m) }
func (*GrantRequest) ProtoMessage() {}
func (*GrantRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{5}
}
func (m *GrantRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GrantRequest.Unmarshal(m, b)
}
func (m *GrantRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GrantRequest.Marshal(b, m, deterministic)
}
func (m *GrantRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_GrantRequest.Merge(m, src)
}
func (m *GrantRequest) XXX_Size() int {
return xxx_messageInfo_GrantRequest.Size(m)
}
func (m *GrantRequest) XXX_DiscardUnknown() {
xxx_messageInfo_GrantRequest.DiscardUnknown(m)
}
var xxx_messageInfo_GrantRequest proto.InternalMessageInfo
func (m *GrantRequest) GetRole() string {
if m != nil {
return m.Role
}
return ""
}
func (m *GrantRequest) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
type GrantResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *GrantResponse) Reset() { *m = GrantResponse{} }
func (m *GrantResponse) String() string { return proto.CompactTextString(m) }
func (*GrantResponse) ProtoMessage() {}
func (*GrantResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{6}
}
func (m *GrantResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_GrantResponse.Unmarshal(m, b)
}
func (m *GrantResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_GrantResponse.Marshal(b, m, deterministic)
}
func (m *GrantResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_GrantResponse.Merge(m, src)
}
func (m *GrantResponse) XXX_Size() int {
return xxx_messageInfo_GrantResponse.Size(m)
}
func (m *GrantResponse) XXX_DiscardUnknown() {
xxx_messageInfo_GrantResponse.DiscardUnknown(m)
}
var xxx_messageInfo_GrantResponse proto.InternalMessageInfo
type VerifyRequest struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -282,7 +417,7 @@ func (m *VerifyRequest) Reset() { *m = VerifyRequest{} }
func (m *VerifyRequest) String() string { return proto.CompactTextString(m) }
func (*VerifyRequest) ProtoMessage() {}
func (*VerifyRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{5}
return fileDescriptor_21300bfacc51fc2a, []int{7}
}
func (m *VerifyRequest) XXX_Unmarshal(b []byte) error {
@ -303,15 +438,21 @@ func (m *VerifyRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_VerifyRequest proto.InternalMessageInfo
func (m *VerifyRequest) GetToken() string {
func (m *VerifyRequest) GetAccount() *Account {
if m != nil {
return m.Token
return m.Account
}
return ""
return nil
}
func (m *VerifyRequest) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
type VerifyResponse struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -321,7 +462,7 @@ func (m *VerifyResponse) Reset() { *m = VerifyResponse{} }
func (m *VerifyResponse) String() string { return proto.CompactTextString(m) }
func (*VerifyResponse) ProtoMessage() {}
func (*VerifyResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{6}
return fileDescriptor_21300bfacc51fc2a, []int{8}
}
func (m *VerifyResponse) XXX_Unmarshal(b []byte) error {
@ -342,15 +483,9 @@ func (m *VerifyResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_VerifyResponse proto.InternalMessageInfo
func (m *VerifyResponse) GetAccount() *Account {
if m != nil {
return m.Account
}
return nil
}
type RevokeRequest struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
Role string `protobuf:"bytes,1,opt,name=role,proto3" json:"role,omitempty"`
Resource *Resource `protobuf:"bytes,2,opt,name=resource,proto3" json:"resource,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
@ -360,7 +495,7 @@ func (m *RevokeRequest) Reset() { *m = RevokeRequest{} }
func (m *RevokeRequest) String() string { return proto.CompactTextString(m) }
func (*RevokeRequest) ProtoMessage() {}
func (*RevokeRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{7}
return fileDescriptor_21300bfacc51fc2a, []int{9}
}
func (m *RevokeRequest) XXX_Unmarshal(b []byte) error {
@ -381,13 +516,20 @@ func (m *RevokeRequest) XXX_DiscardUnknown() {
var xxx_messageInfo_RevokeRequest proto.InternalMessageInfo
func (m *RevokeRequest) GetToken() string {
func (m *RevokeRequest) GetRole() string {
if m != nil {
return m.Token
return m.Role
}
return ""
}
func (m *RevokeRequest) GetResource() *Resource {
if m != nil {
return m.Resource
}
return nil
}
type RevokeResponse struct {
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
@ -398,7 +540,7 @@ func (m *RevokeResponse) Reset() { *m = RevokeResponse{} }
func (m *RevokeResponse) String() string { return proto.CompactTextString(m) }
func (*RevokeResponse) ProtoMessage() {}
func (*RevokeResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_de609d4872dacc78, []int{8}
return fileDescriptor_21300bfacc51fc2a, []int{10}
}
func (m *RevokeResponse) XXX_Unmarshal(b []byte) error {
@ -419,50 +561,235 @@ func (m *RevokeResponse) XXX_DiscardUnknown() {
var xxx_messageInfo_RevokeResponse proto.InternalMessageInfo
type InspectRequest struct {
Token string `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *InspectRequest) Reset() { *m = InspectRequest{} }
func (m *InspectRequest) String() string { return proto.CompactTextString(m) }
func (*InspectRequest) ProtoMessage() {}
func (*InspectRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{11}
}
func (m *InspectRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InspectRequest.Unmarshal(m, b)
}
func (m *InspectRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_InspectRequest.Marshal(b, m, deterministic)
}
func (m *InspectRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_InspectRequest.Merge(m, src)
}
func (m *InspectRequest) XXX_Size() int {
return xxx_messageInfo_InspectRequest.Size(m)
}
func (m *InspectRequest) XXX_DiscardUnknown() {
xxx_messageInfo_InspectRequest.DiscardUnknown(m)
}
var xxx_messageInfo_InspectRequest proto.InternalMessageInfo
func (m *InspectRequest) GetToken() string {
if m != nil {
return m.Token
}
return ""
}
type InspectResponse struct {
Account *Account `protobuf:"bytes,1,opt,name=account,proto3" json:"account,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *InspectResponse) Reset() { *m = InspectResponse{} }
func (m *InspectResponse) String() string { return proto.CompactTextString(m) }
func (*InspectResponse) ProtoMessage() {}
func (*InspectResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{12}
}
func (m *InspectResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_InspectResponse.Unmarshal(m, b)
}
func (m *InspectResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_InspectResponse.Marshal(b, m, deterministic)
}
func (m *InspectResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_InspectResponse.Merge(m, src)
}
func (m *InspectResponse) XXX_Size() int {
return xxx_messageInfo_InspectResponse.Size(m)
}
func (m *InspectResponse) XXX_DiscardUnknown() {
xxx_messageInfo_InspectResponse.DiscardUnknown(m)
}
var xxx_messageInfo_InspectResponse proto.InternalMessageInfo
func (m *InspectResponse) GetAccount() *Account {
if m != nil {
return m.Account
}
return nil
}
type RefreshRequest struct {
Secret string `protobuf:"bytes,1,opt,name=secret,proto3" json:"secret,omitempty"`
TokenExpiry int64 `protobuf:"varint,2,opt,name=token_expiry,json=tokenExpiry,proto3" json:"token_expiry,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RefreshRequest) Reset() { *m = RefreshRequest{} }
func (m *RefreshRequest) String() string { return proto.CompactTextString(m) }
func (*RefreshRequest) ProtoMessage() {}
func (*RefreshRequest) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{13}
}
func (m *RefreshRequest) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RefreshRequest.Unmarshal(m, b)
}
func (m *RefreshRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RefreshRequest.Marshal(b, m, deterministic)
}
func (m *RefreshRequest) XXX_Merge(src proto.Message) {
xxx_messageInfo_RefreshRequest.Merge(m, src)
}
func (m *RefreshRequest) XXX_Size() int {
return xxx_messageInfo_RefreshRequest.Size(m)
}
func (m *RefreshRequest) XXX_DiscardUnknown() {
xxx_messageInfo_RefreshRequest.DiscardUnknown(m)
}
var xxx_messageInfo_RefreshRequest proto.InternalMessageInfo
func (m *RefreshRequest) GetSecret() string {
if m != nil {
return m.Secret
}
return ""
}
func (m *RefreshRequest) GetTokenExpiry() int64 {
if m != nil {
return m.TokenExpiry
}
return 0
}
type RefreshResponse struct {
Token *Token `protobuf:"bytes,1,opt,name=token,proto3" json:"token,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"`
}
func (m *RefreshResponse) Reset() { *m = RefreshResponse{} }
func (m *RefreshResponse) String() string { return proto.CompactTextString(m) }
func (*RefreshResponse) ProtoMessage() {}
func (*RefreshResponse) Descriptor() ([]byte, []int) {
return fileDescriptor_21300bfacc51fc2a, []int{14}
}
func (m *RefreshResponse) XXX_Unmarshal(b []byte) error {
return xxx_messageInfo_RefreshResponse.Unmarshal(m, b)
}
func (m *RefreshResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
return xxx_messageInfo_RefreshResponse.Marshal(b, m, deterministic)
}
func (m *RefreshResponse) XXX_Merge(src proto.Message) {
xxx_messageInfo_RefreshResponse.Merge(m, src)
}
func (m *RefreshResponse) XXX_Size() int {
return xxx_messageInfo_RefreshResponse.Size(m)
}
func (m *RefreshResponse) XXX_DiscardUnknown() {
xxx_messageInfo_RefreshResponse.DiscardUnknown(m)
}
var xxx_messageInfo_RefreshResponse proto.InternalMessageInfo
func (m *RefreshResponse) GetToken() *Token {
if m != nil {
return m.Token
}
return nil
}
func init() {
proto.RegisterType((*Token)(nil), "go.micro.auth.Token")
proto.RegisterMapType((map[string]string)(nil), "go.micro.auth.Token.MetadataEntry")
proto.RegisterType((*Account)(nil), "go.micro.auth.Account")
proto.RegisterMapType((map[string]string)(nil), "go.micro.auth.Account.MetadataEntry")
proto.RegisterType((*Role)(nil), "go.micro.auth.Role")
proto.RegisterType((*Resource)(nil), "go.micro.auth.Resource")
proto.RegisterType((*GenerateRequest)(nil), "go.micro.auth.GenerateRequest")
proto.RegisterMapType((map[string]string)(nil), "go.micro.auth.GenerateRequest.MetadataEntry")
proto.RegisterType((*GenerateResponse)(nil), "go.micro.auth.GenerateResponse")
proto.RegisterType((*GrantRequest)(nil), "go.micro.auth.GrantRequest")
proto.RegisterType((*GrantResponse)(nil), "go.micro.auth.GrantResponse")
proto.RegisterType((*VerifyRequest)(nil), "go.micro.auth.VerifyRequest")
proto.RegisterType((*VerifyResponse)(nil), "go.micro.auth.VerifyResponse")
proto.RegisterType((*RevokeRequest)(nil), "go.micro.auth.RevokeRequest")
proto.RegisterType((*RevokeResponse)(nil), "go.micro.auth.RevokeResponse")
proto.RegisterType((*InspectRequest)(nil), "go.micro.auth.InspectRequest")
proto.RegisterType((*InspectResponse)(nil), "go.micro.auth.InspectResponse")
proto.RegisterType((*RefreshRequest)(nil), "go.micro.auth.RefreshRequest")
proto.RegisterType((*RefreshResponse)(nil), "go.micro.auth.RefreshResponse")
}
func init() {
proto.RegisterFile("micro/go-micro/auth/service/proto/auth.proto", fileDescriptor_de609d4872dacc78)
}
func init() { proto.RegisterFile("auth/service/proto/auth.proto", fileDescriptor_21300bfacc51fc2a) }
var fileDescriptor_de609d4872dacc78 = []byte{
// 432 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x53, 0x4b, 0x6f, 0xd3, 0x40,
0x10, 0xae, 0x1d, 0xe7, 0xc1, 0x44, 0x09, 0xd1, 0x80, 0x8a, 0x15, 0xf1, 0x88, 0x56, 0x20, 0x05,
0x09, 0x1c, 0xe4, 0x5e, 0x10, 0x5c, 0x28, 0x0f, 0xf5, 0x54, 0x21, 0xed, 0x81, 0xfb, 0xe2, 0x0c,
0xad, 0x95, 0xc4, 0x6b, 0xd6, 0xeb, 0x08, 0xff, 0x06, 0x7e, 0x28, 0x7f, 0x03, 0x79, 0xd7, 0x1b,
0x6a, 0xb7, 0xe5, 0x00, 0xb7, 0x79, 0x7c, 0xf3, 0xcd, 0xf7, 0x8d, 0x76, 0xe1, 0xc5, 0x2e, 0x4d,
0x94, 0x5c, 0x5d, 0xc8, 0x97, 0x36, 0x10, 0xa5, 0xbe, 0x5c, 0x15, 0xa4, 0xf6, 0x69, 0x42, 0xab,
0x5c, 0x49, 0x6d, 0x4b, 0x91, 0x09, 0x71, 0x72, 0x21, 0x23, 0x83, 0x8b, 0xea, 0x22, 0xfb, 0xe9,
0xc3, 0xf0, 0x34, 0x49, 0x64, 0x99, 0x69, 0x9c, 0x82, 0x9f, 0xae, 0x43, 0x6f, 0xe1, 0x2d, 0xef,
0x70, 0x3f, 0x5d, 0xe3, 0x7d, 0xe8, 0x6b, 0xb9, 0xa1, 0x2c, 0xf4, 0x4d, 0xc9, 0x26, 0x18, 0xc2,
0x30, 0x51, 0x24, 0x34, 0xad, 0xc3, 0xde, 0xc2, 0x5b, 0xf6, 0xb8, 0x4b, 0xf1, 0x18, 0x06, 0xf4,
0x23, 0x4f, 0x55, 0x15, 0x06, 0xa6, 0xd1, 0x64, 0xf8, 0x1c, 0xfa, 0x4a, 0x6e, 0xa9, 0x08, 0xfb,
0x8b, 0xde, 0x72, 0x1c, 0xdf, 0x8b, 0x5a, 0x12, 0x22, 0x2e, 0xb7, 0xc4, 0x2d, 0x02, 0xdf, 0xc1,
0x68, 0x47, 0x5a, 0xac, 0x85, 0x16, 0xe1, 0xc0, 0xa0, 0x9f, 0x76, 0xd0, 0x8d, 0xd8, 0xe8, 0xbc,
0x81, 0x7d, 0xca, 0xb4, 0xaa, 0xf8, 0x61, 0x6a, 0xfe, 0x16, 0x26, 0xad, 0x16, 0xce, 0xa0, 0xb7,
0xa1, 0xaa, 0xb1, 0x55, 0x87, 0xb5, 0xaf, 0xbd, 0xd8, 0x96, 0xe4, 0x7c, 0x99, 0xe4, 0x8d, 0xff,
0xda, 0x63, 0x9f, 0x21, 0xa8, 0xd5, 0x20, 0x42, 0x90, 0x89, 0x1d, 0x35, 0x43, 0x26, 0xc6, 0x13,
0x18, 0x29, 0x2a, 0x64, 0xa9, 0x12, 0x3b, 0x38, 0x8e, 0x1f, 0x74, 0x8d, 0x34, 0x6d, 0x7e, 0x00,
0xb2, 0x18, 0x46, 0xae, 0x7a, 0x23, 0x29, 0x42, 0xa0, 0xab, 0xdc, 0x29, 0x31, 0x31, 0xfb, 0x00,
0x77, 0xcf, 0x28, 0x23, 0x25, 0x34, 0x71, 0xfa, 0x5e, 0x52, 0xa1, 0xf1, 0x15, 0x0c, 0x85, 0xf5,
0x6d, 0xa6, 0xc7, 0xf1, 0xf1, 0xcd, 0x57, 0xe1, 0x0e, 0xc6, 0x3e, 0xc2, 0xec, 0x0f, 0x49, 0x91,
0xcb, 0xac, 0xa0, 0x7f, 0x60, 0x79, 0x06, 0x93, 0x2f, 0xa4, 0xd2, 0x6f, 0x95, 0x13, 0x72, 0x78,
0x12, 0xde, 0x95, 0x27, 0xc1, 0xde, 0xc3, 0xd4, 0xc1, 0xfe, 0x67, 0x15, 0xa7, 0xbd, 0xdc, 0xd0,
0xdf, 0x57, 0xcd, 0x60, 0xea, 0x60, 0x76, 0x55, 0xfc, 0xcb, 0x83, 0xe0, 0xb4, 0xd4, 0x97, 0x78,
0x0e, 0x23, 0x67, 0x19, 0x1f, 0x77, 0xd6, 0x75, 0x0e, 0x3a, 0x7f, 0x72, 0x6b, 0xdf, 0xb2, 0xb2,
0x23, 0x3c, 0x83, 0x81, 0x35, 0x85, 0x0f, 0x3b, 0xe0, 0xd6, 0x49, 0xe6, 0x8f, 0x6e, 0xe9, 0x5e,
0x25, 0xb2, 0x92, 0xaf, 0x11, 0xb5, 0x0c, 0x5f, 0x23, 0x6a, 0xfb, 0x64, 0x47, 0x5f, 0x07, 0xe6,
0x07, 0x9f, 0xfc, 0x0e, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x34, 0xce, 0x17, 0xf1, 0x03, 0x00, 0x00,
var fileDescriptor_21300bfacc51fc2a = []byte{
// 663 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x55, 0xdd, 0x6e, 0xd3, 0x4c,
0x10, 0xad, 0xed, 0xfc, 0x75, 0x52, 0x27, 0xd1, 0xaa, 0xea, 0x67, 0xf9, 0xa3, 0x25, 0x18, 0x84,
0x2a, 0x54, 0xb9, 0x28, 0xbd, 0x41, 0x20, 0x10, 0x15, 0xad, 0xca, 0x8f, 0xca, 0x85, 0x85, 0x80,
0x3b, 0xe4, 0x3a, 0x53, 0x6a, 0xd2, 0xd8, 0x66, 0xbd, 0x8e, 0xc8, 0x5b, 0xf0, 0x52, 0xbc, 0x05,
0x77, 0xbc, 0x08, 0xda, 0xf5, 0xae, 0x6b, 0x3b, 0x09, 0x12, 0x05, 0xee, 0x76, 0x76, 0x66, 0xcf,
0x99, 0x39, 0x73, 0xe2, 0xc0, 0xb6, 0x9f, 0xb1, 0x8b, 0xfd, 0x14, 0xe9, 0x2c, 0x0c, 0x70, 0x3f,
0xa1, 0x31, 0x8b, 0xf7, 0xf9, 0x95, 0x2b, 0x8e, 0xc4, 0xfc, 0x18, 0xbb, 0xd3, 0x30, 0xa0, 0xb1,
0xcb, 0x2f, 0x9d, 0xaf, 0x3a, 0x34, 0xdf, 0xc4, 0x13, 0x8c, 0xc8, 0x26, 0x34, 0x19, 0x3f, 0x58,
0xda, 0x50, 0xdb, 0x5d, 0xf7, 0xf2, 0x80, 0x10, 0x68, 0xb0, 0x79, 0x82, 0x96, 0x2e, 0x2e, 0xc5,
0x99, 0x58, 0xd0, 0x0e, 0x28, 0xfa, 0x0c, 0xc7, 0x96, 0x31, 0xd4, 0x76, 0x0d, 0x4f, 0x85, 0x64,
0x0b, 0x5a, 0xf8, 0x25, 0x09, 0xe9, 0xdc, 0x6a, 0x88, 0x84, 0x8c, 0xf8, 0x8b, 0x34, 0x3b, 0xfb,
0x84, 0x01, 0xb3, 0x9a, 0x02, 0x48, 0x85, 0x9c, 0x95, 0xc6, 0x97, 0x98, 0x5a, 0xad, 0xa1, 0xc1,
0x59, 0x45, 0x40, 0x9e, 0x40, 0x67, 0x8a, 0xcc, 0x1f, 0xfb, 0xcc, 0xb7, 0xda, 0x43, 0x63, 0xb7,
0x3b, 0x72, 0xdc, 0x4a, 0xdf, 0xae, 0xe8, 0xd9, 0x3d, 0x95, 0x45, 0xc7, 0x11, 0xa3, 0x73, 0xaf,
0x78, 0x63, 0x3f, 0x02, 0xb3, 0x92, 0x22, 0x03, 0x30, 0x26, 0x38, 0x97, 0xa3, 0xf1, 0x23, 0x27,
0x9e, 0xf9, 0x97, 0x99, 0x9a, 0x2c, 0x0f, 0x1e, 0xea, 0x0f, 0x34, 0xe7, 0xbb, 0x06, 0xed, 0xc3,
0x20, 0x88, 0xb3, 0x88, 0x91, 0x1e, 0xe8, 0xe1, 0x58, 0x3e, 0xd3, 0xc3, 0x31, 0xd9, 0x83, 0x56,
0x8a, 0x01, 0x45, 0x26, 0x9e, 0x75, 0x47, 0x9b, 0xcb, 0xda, 0xf2, 0x64, 0xcd, 0xd5, 0x70, 0x46,
0x79, 0xb8, 0xa7, 0xa5, 0xe1, 0x1a, 0x62, 0xb8, 0x3b, 0x35, 0x14, 0xc9, 0xfe, 0x6f, 0xc6, 0x7b,
0x0d, 0x1d, 0x0f, 0xd3, 0x38, 0xa3, 0x01, 0xf2, 0xed, 0x46, 0xfe, 0x14, 0xe5, 0x43, 0x71, 0x5e,
0xba, 0x71, 0x1b, 0x3a, 0x18, 0x8d, 0x93, 0x38, 0x8c, 0x98, 0x58, 0xf9, 0xba, 0x57, 0xc4, 0xce,
0x0f, 0x0d, 0xfa, 0x27, 0x18, 0x21, 0xf5, 0x19, 0x7a, 0xf8, 0x39, 0xc3, 0x74, 0x51, 0xb6, 0x42,
0x08, 0xbd, 0x2c, 0xc4, 0xf3, 0x92, 0x10, 0x86, 0x10, 0x62, 0xaf, 0x26, 0x44, 0x0d, 0x77, 0x95,
0x20, 0xe4, 0x36, 0x98, 0xb9, 0xe4, 0x1f, 0x2a, 0xf6, 0xdb, 0xc8, 0x2f, 0x8f, 0xc5, 0xdd, 0x9f,
0xa9, 0x76, 0x04, 0x83, 0xab, 0x66, 0xd2, 0x24, 0x8e, 0x52, 0x24, 0xf7, 0xa1, 0xed, 0xe7, 0x9b,
0x12, 0x18, 0xdd, 0xd1, 0xd6, 0xf2, 0x3d, 0x7a, 0xaa, 0xcc, 0x79, 0x07, 0x1b, 0x27, 0xd4, 0x8f,
0x98, 0xd2, 0x89, 0x40, 0x83, 0x4b, 0xa1, 0xf4, 0xe7, 0x67, 0x72, 0x00, 0x1d, 0x2a, 0xf7, 0x23,
0x4d, 0xf6, 0x5f, 0x0d, 0x56, 0xad, 0xcf, 0x2b, 0x0a, 0x9d, 0x3e, 0x98, 0x12, 0x38, 0xef, 0xcd,
0x99, 0x81, 0xf9, 0x16, 0x69, 0x78, 0x3e, 0x57, 0x54, 0xbf, 0xdd, 0xec, 0xf5, 0x1a, 0x19, 0x40,
0x4f, 0xf1, 0xca, 0x4e, 0xde, 0x83, 0xe9, 0xe1, 0x2c, 0x9e, 0xe0, 0x5f, 0x1f, 0x7a, 0x00, 0x3d,
0x85, 0x2c, 0xb9, 0xee, 0x42, 0xef, 0x45, 0x94, 0x26, 0x18, 0x14, 0x0a, 0x2f, 0xfd, 0xaa, 0x39,
0xcf, 0xa0, 0x5f, 0xd4, 0x5d, 0x7b, 0x99, 0xaf, 0x38, 0xfd, 0x39, 0xc5, 0xf4, 0x42, 0x91, 0x6d,
0x15, 0x5f, 0x87, 0x9c, 0x4d, 0x7d, 0x07, 0x6e, 0xc1, 0x86, 0xe0, 0x55, 0xee, 0xd4, 0x85, 0x3b,
0xbb, 0xe2, 0x2e, 0x37, 0xa7, 0xf3, 0x18, 0xfa, 0x05, 0x98, 0xec, 0xe8, 0x5e, 0xb9, 0xf5, 0x55,
0x9f, 0x9a, 0xbc, 0x64, 0xf4, 0xcd, 0x80, 0xc6, 0x61, 0xc6, 0x2e, 0xc8, 0x29, 0x74, 0x94, 0x4f,
0xc9, 0xce, 0xaf, 0x7f, 0x4d, 0xf6, 0xcd, 0x95, 0x79, 0x29, 0xe7, 0x1a, 0x39, 0x82, 0xa6, 0xf0,
0x15, 0xf9, 0xbf, 0x5e, 0x5b, 0xb2, 0xb1, 0x7d, 0x63, 0x79, 0xb2, 0x40, 0x39, 0x81, 0x56, 0x6e,
0x0a, 0x52, 0xaf, 0xac, 0x78, 0xd4, 0xde, 0x5e, 0x91, 0x2d, 0x03, 0xe5, 0x1b, 0x5f, 0x00, 0xaa,
0x58, 0x6c, 0x01, 0xa8, 0x66, 0x93, 0x35, 0xf2, 0x12, 0xda, 0xd2, 0x00, 0xa4, 0x5e, 0x5b, 0x35,
0x90, 0xbd, 0xb3, 0x2a, 0x5d, 0xc6, 0x92, 0xab, 0x23, 0x8b, 0xbc, 0x65, 0x7f, 0x2c, 0x60, 0xd5,
0x36, 0xee, 0xac, 0x9d, 0xb5, 0xc4, 0x9f, 0xf4, 0xc1, 0xcf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb7,
0xf8, 0x55, 0xb6, 0xc5, 0x07, 0x00, 0x00,
}

View File

@ -1,5 +1,5 @@
// Code generated by protoc-gen-micro. DO NOT EDIT.
// source: micro/go-micro/auth/service/proto/auth.proto
// source: auth/service/proto/auth.proto
package go_micro_auth
@ -35,8 +35,11 @@ var _ server.Option
type AuthService interface {
Generate(ctx context.Context, in *GenerateRequest, opts ...client.CallOption) (*GenerateResponse, error)
Grant(ctx context.Context, in *GrantRequest, opts ...client.CallOption) (*GrantResponse, error)
Verify(ctx context.Context, in *VerifyRequest, opts ...client.CallOption) (*VerifyResponse, error)
Revoke(ctx context.Context, in *RevokeRequest, opts ...client.CallOption) (*RevokeResponse, error)
Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error)
Refresh(ctx context.Context, in *RefreshRequest, opts ...client.CallOption) (*RefreshResponse, error)
}
type authService struct {
@ -61,6 +64,16 @@ func (c *authService) Generate(ctx context.Context, in *GenerateRequest, opts ..
return out, nil
}
func (c *authService) Grant(ctx context.Context, in *GrantRequest, opts ...client.CallOption) (*GrantResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Grant", in)
out := new(GrantResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Verify(ctx context.Context, in *VerifyRequest, opts ...client.CallOption) (*VerifyResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Verify", in)
out := new(VerifyResponse)
@ -81,19 +94,45 @@ func (c *authService) Revoke(ctx context.Context, in *RevokeRequest, opts ...cli
return out, nil
}
func (c *authService) Inspect(ctx context.Context, in *InspectRequest, opts ...client.CallOption) (*InspectResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Inspect", in)
out := new(InspectResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *authService) Refresh(ctx context.Context, in *RefreshRequest, opts ...client.CallOption) (*RefreshResponse, error) {
req := c.c.NewRequest(c.name, "Auth.Refresh", in)
out := new(RefreshResponse)
err := c.c.Call(ctx, req, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// Server API for Auth service
type AuthHandler interface {
Generate(context.Context, *GenerateRequest, *GenerateResponse) error
Grant(context.Context, *GrantRequest, *GrantResponse) error
Verify(context.Context, *VerifyRequest, *VerifyResponse) error
Revoke(context.Context, *RevokeRequest, *RevokeResponse) error
Inspect(context.Context, *InspectRequest, *InspectResponse) error
Refresh(context.Context, *RefreshRequest, *RefreshResponse) error
}
func RegisterAuthHandler(s server.Server, hdlr AuthHandler, opts ...server.HandlerOption) error {
type auth interface {
Generate(ctx context.Context, in *GenerateRequest, out *GenerateResponse) error
Grant(ctx context.Context, in *GrantRequest, out *GrantResponse) error
Verify(ctx context.Context, in *VerifyRequest, out *VerifyResponse) error
Revoke(ctx context.Context, in *RevokeRequest, out *RevokeResponse) error
Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error
Refresh(ctx context.Context, in *RefreshRequest, out *RefreshResponse) error
}
type Auth struct {
auth
@ -110,6 +149,10 @@ func (h *authHandler) Generate(ctx context.Context, in *GenerateRequest, out *Ge
return h.AuthHandler.Generate(ctx, in, out)
}
func (h *authHandler) Grant(ctx context.Context, in *GrantRequest, out *GrantResponse) error {
return h.AuthHandler.Grant(ctx, in, out)
}
func (h *authHandler) Verify(ctx context.Context, in *VerifyRequest, out *VerifyResponse) error {
return h.AuthHandler.Verify(ctx, in, out)
}
@ -117,3 +160,11 @@ func (h *authHandler) Verify(ctx context.Context, in *VerifyRequest, out *Verify
func (h *authHandler) Revoke(ctx context.Context, in *RevokeRequest, out *RevokeResponse) error {
return h.AuthHandler.Revoke(ctx, in, out)
}
func (h *authHandler) Inspect(ctx context.Context, in *InspectRequest, out *InspectResponse) error {
return h.AuthHandler.Inspect(ctx, in, out)
}
func (h *authHandler) Refresh(ctx context.Context, in *RefreshRequest, out *RefreshResponse) error {
return h.AuthHandler.Refresh(ctx, in, out)
}

View File

@ -4,47 +4,81 @@ package go.micro.auth;
service Auth {
rpc Generate(GenerateRequest) returns (GenerateResponse) {};
rpc Grant(GrantRequest) returns (GrantResponse) {};
rpc Verify(VerifyRequest) returns (VerifyResponse) {};
rpc Revoke(RevokeRequest) returns (RevokeResponse) {};
rpc Inspect(InspectRequest) returns (InspectResponse) {};
rpc Refresh(RefreshRequest) returns (RefreshResponse) {};
}
message Token {
string token = 1;
string type = 2;
int64 created = 3;
int64 expiry = 4;
string subject = 5;
repeated string roles = 6;
map<string, string> metadata = 7;
}
message Account {
string id = 1;
string token = 2;
int64 created = 3;
int64 expiry = 4;
repeated Role roles = 5;
map<string, string> metadata = 6;
}
message Role {
string name = 1;
Resource resource = 2;
Token secret = 2;
repeated string roles = 3;
map<string, string> metadata = 4;
}
message Resource{
string name = 1;
string type = 2;
string endpoint = 3;
}
message GenerateRequest {
Account account = 1;
string id = 1;
repeated string roles = 2;
map<string, string> metadata = 3;
int64 secret_expiry = 4;
}
message GenerateResponse {
Account account = 1;
}
message VerifyRequest {
string token = 1;
message GrantRequest {
string role = 1;
Resource resource = 2;
}
message VerifyResponse {
message GrantResponse {}
message VerifyRequest {
Account account = 1;
Resource resource = 2;
}
message VerifyResponse {}
message RevokeRequest {
string token = 1;
string role = 1;
Resource resource = 2;
}
message RevokeResponse {}
message InspectRequest {
string token = 1;
}
message InspectResponse {
Account account = 1;
}
message RefreshRequest {
string secret = 1;
int64 token_expiry = 2;
}
message RefreshResponse {
Token token = 1;
}

View File

@ -2,10 +2,13 @@ package service
import (
"context"
"strings"
"time"
"github.com/micro/go-micro/v2/auth"
pb "github.com/micro/go-micro/v2/auth/service/proto"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/jwt"
"github.com/micro/go-micro/v2/client"
)
@ -20,13 +23,14 @@ func NewAuth(opts ...auth.Option) auth.Auth {
type svc struct {
options auth.Options
auth pb.AuthService
jwt token.Provider
}
func (s *svc) String() string {
return "service"
}
func (s *svc) Init(opts ...auth.Option) error {
func (s *svc) Init(opts ...auth.Option) {
for _, o := range opts {
o(&s.options)
}
@ -34,99 +38,140 @@ func (s *svc) Init(opts ...auth.Option) error {
dc := client.DefaultClient
s.auth = pb.NewAuthService("go.micro.auth", dc)
return nil
// if we have a JWT public key passed as an option,
// we can decode tokens with the type "JWT" locally
// and not have to make an RPC call
if key := s.options.PublicKey; len(key) > 0 {
s.jwt = jwt.NewTokenProvider(token.WithPublicKey(key))
}
}
func (s *svc) Options() auth.Options {
return s.options
}
// Generate a new auth account
// Generate a new account
func (s *svc) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
// construct the request
options := auth.NewGenerateOptions(opts...)
sa := &auth.Account{
rsp, err := s.auth.Generate(context.TODO(), &pb.GenerateRequest{
Id: id,
Roles: options.Roles,
Metadata: options.Metadata,
}
req := &pb.GenerateRequest{Account: serializeAccount(sa)}
// execute the request
resp, err := s.auth.Generate(context.Background(), req)
SecretExpiry: int64(options.SecretExpiry.Seconds()),
})
if err != nil {
return nil, err
}
// format the response
return deserializeAccount(resp.Account), nil
return serializeAccount(rsp.Account), nil
}
// Revoke an authorization account
func (s *svc) Revoke(token string) error {
// contruct the request
req := &pb.RevokeRequest{Token: token}
// execute the request
_, err := s.auth.Revoke(context.Background(), req)
// Grant access to a resource
func (s *svc) Grant(role string, res *auth.Resource) error {
_, err := s.auth.Grant(context.TODO(), &pb.GrantRequest{
Role: role,
Resource: &pb.Resource{
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
})
return err
}
// Verify an account token
func (s *svc) Verify(token string) (*auth.Account, error) {
resp, err := s.auth.Verify(context.Background(), &pb.VerifyRequest{Token: token})
// Revoke access to a resource
func (s *svc) Revoke(role string, res *auth.Resource) error {
_, err := s.auth.Revoke(context.TODO(), &pb.RevokeRequest{
Role: role,
Resource: &pb.Resource{
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
})
return err
}
// Verify an account has access to a resource
func (s *svc) Verify(acc *auth.Account, res *auth.Resource) error {
_, err := s.auth.Verify(context.TODO(), &pb.VerifyRequest{
Account: &pb.Account{
Id: acc.ID,
Roles: acc.Roles,
},
Resource: &pb.Resource{
Type: res.Type,
Name: res.Name,
Endpoint: res.Endpoint,
},
})
return err
}
// Inspect a token
func (s *svc) Inspect(token string) (*auth.Account, error) {
// try to decode JWT locally and fall back to srv if an error
// occurs, TODO: find a better way of determining if the token
// is a JWT, possibly update the interface to take an auth.Token
// and not just the string
if len(strings.Split(token, ".")) == 3 && s.jwt != nil {
if tok, err := s.jwt.Inspect(token); err == nil {
return &auth.Account{
ID: tok.Subject,
Roles: tok.Roles,
Metadata: tok.Metadata,
}, nil
}
}
rsp, err := s.auth.Inspect(context.TODO(), &pb.InspectRequest{
Token: token,
})
if err != nil {
return nil, err
}
return deserializeAccount(resp.Account), nil
return serializeAccount(rsp.Account), nil
}
func serializeAccount(sa *auth.Account) *pb.Account {
roles := make([]*pb.Role, len(sa.Roles))
for i, r := range sa.Roles {
roles[i] = &pb.Role{
Name: r.Name,
// Refresh an account using a secret
func (s *svc) Refresh(secret string, opts ...auth.RefreshOption) (*auth.Token, error) {
options := auth.NewRefreshOptions(opts...)
rsp, err := s.auth.Refresh(context.Background(), &pb.RefreshRequest{
Secret: secret,
TokenExpiry: int64(options.TokenExpiry.Seconds()),
})
if err != nil {
return nil, err
}
if r.Resource != nil {
roles[i].Resource = &pb.Resource{
Name: r.Resource.Name,
Type: r.Resource.Type,
return serializeToken(rsp.Token), nil
}
func serializeToken(t *pb.Token) *auth.Token {
return &auth.Token{
Token: t.Token,
Type: t.Type,
Created: time.Unix(t.Created, 0),
Expiry: time.Unix(t.Expiry, 0),
Subject: t.Subject,
Roles: t.Roles,
Metadata: t.Metadata,
}
}
return &pb.Account{
Id: sa.Id,
Roles: roles,
Metadata: sa.Metadata,
}
func serializeAccount(a *pb.Account) *auth.Account {
var secret *auth.Token
if a.Secret != nil {
secret = serializeToken(a.Secret)
}
func deserializeAccount(a *pb.Account) *auth.Account {
// format the response
sa := &auth.Account{
Id: a.Id,
Token: a.Token,
Created: time.Unix(a.Created, 0),
Expiry: time.Unix(a.Expiry, 0),
return &auth.Account{
ID: a.Id,
Roles: a.Roles,
Metadata: a.Metadata,
}
sa.Roles = make([]*auth.Role, len(a.Roles))
for i, r := range a.Roles {
sa.Roles[i] = &auth.Role{
Name: r.Name,
}
if r.Resource != nil {
sa.Roles[i].Resource = &auth.Resource{
Name: r.Resource.Name,
Type: r.Resource.Type,
Secret: secret,
}
}
}
return sa
}

72
auth/store/rules.go Normal file
View File

@ -0,0 +1,72 @@
package store
import (
"encoding/json"
"strings"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/store"
)
// Rule is an access control rule
type Rule struct {
Role string `json:"rule"`
Resource *auth.Resource `json:"resource"`
}
// Key to be used when written to the store
func (r *Rule) Key() string {
comps := []string{r.Resource.Type, r.Resource.Name, r.Resource.Endpoint, r.Role}
return strings.Join(comps, "/")
}
// Bytes returns json encoded bytes
func (r *Rule) Bytes() []byte {
bytes, _ := json.Marshal(r)
return bytes
}
// isValidRule returns a bool, indicating if a rule permits access to a
// resource for a given account
func isValidRule(rule Rule, acc *auth.Account, res *auth.Resource) bool {
if rule.Role == "*" {
return true
}
for _, role := range acc.Roles {
if rule.Role == role {
return true
}
// allow user.anything if role is user.*
if strings.HasSuffix(rule.Role, ".*") && strings.HasPrefix(rule.Role, role+".") {
return true
}
}
return false
}
// listRules gets all the rules from the store which have a key
// prefix matching the filters
func (s *Store) listRules(filters ...string) ([]Rule, error) {
// get the records from the store
prefix := strings.Join(filters, "/")
recs, err := s.opts.Store.Read(prefix, store.ReadPrefix())
if err != nil {
return nil, err
}
// unmarshal the records
rules := make([]Rule, 0, len(recs))
for _, rec := range recs {
var r Rule
if err := json.Unmarshal(rec.Value, &r); err != nil {
return nil, err
}
rules = append(rules, r)
}
// return the rules
return rules, nil
}

View File

@ -1,130 +1,159 @@
package store
import (
"bytes"
"encoding/gob"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/errors"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/auth/token/basic"
"github.com/micro/go-micro/v2/store"
memStore "github.com/micro/go-micro/v2/store/memory"
)
type Auth struct {
store store.Store
// NewAuth returns a new default registry which is store
func NewAuth(opts ...auth.Option) auth.Auth {
var s Store
s.Init(opts...)
return &s
}
// Store implementation of auth
type Store struct {
secretProvider token.Provider
tokenProvider token.Provider
opts auth.Options
}
// NewAuth returns an instance of store auth
func NewAuth(opts ...auth.Option) auth.Auth {
var options auth.Options
// String returns store
func (s *Store) String() string {
return "store"
}
// Init the auth
func (s *Store) Init(opts ...auth.Option) {
for _, o := range opts {
o(&options)
o(&s.opts)
}
return &Auth{
store: store.DefaultStore,
opts: options,
// use the default store as a fallback
if s.opts.Store == nil {
s.opts.Store = store.DefaultStore
}
// noop will not work for auth
if s.opts.Store.String() == "noop" {
s.opts.Store = memStore.NewStore()
}
if s.tokenProvider == nil {
s.tokenProvider = basic.NewTokenProvider(token.WithStore(s.opts.Store))
}
if s.secretProvider == nil {
s.secretProvider = basic.NewTokenProvider(token.WithStore(s.opts.Store))
}
}
// Init the auth package
func (a *Auth) Init(opts ...auth.Option) error {
for _, o := range opts {
o(&a.opts)
}
return nil
}
// Options returns the options set
func (a *Auth) Options() auth.Options {
return a.opts
}
// Generate a new auth Account
func (a *Auth) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
// generate the token
token, err := uuid.NewUUID()
if err != nil {
return nil, err
// Options returns the options
func (s *Store) Options() auth.Options {
return s.opts
}
// Generate a new account
func (s *Store) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
// parse the options
options := auth.NewGenerateOptions(opts...)
// construct the account
sa := auth.Account{
Id: id,
Token: token.String(),
Created: time.Now(),
Metadata: options.Metadata,
Roles: options.Roles,
// Generate a long-lived secret
secretOpts := []token.GenerateOption{
token.WithExpiry(options.SecretExpiry),
token.WithMetadata(options.Metadata),
token.WithRoles(options.Roles),
}
// encode the data to bytes
// TODO: replace with json
buf := &bytes.Buffer{}
e := gob.NewEncoder(buf)
if err := e.Encode(sa); err != nil {
return nil, err
}
// write to the store
err = a.store.Write(&store.Record{
Key: token.String(),
Value: buf.Bytes(),
})
secret, err := s.secretProvider.Generate(id, secretOpts...)
if err != nil {
return nil, err
}
// return the result
return &sa, nil
// return the account
return &auth.Account{
ID: id,
Roles: options.Roles,
Metadata: options.Metadata,
Secret: secret,
}, nil
}
// Revoke an authorization Account
func (a *Auth) Revoke(token string) error {
records, err := a.store.Read(token, store.ReadSuffix())
// Grant access to a resource
func (s *Store) Grant(role string, res *auth.Resource) error {
r := Rule{role, res}
return s.opts.Store.Write(&store.Record{Key: r.Key(), Value: r.Bytes()})
}
// Revoke access to a resource
func (s *Store) Revoke(role string, res *auth.Resource) error {
r := Rule{role, res}
err := s.opts.Store.Delete(r.Key())
if err == store.ErrNotFound {
return auth.ErrNotFound
}
return err
}
// Verify an account has access to a resource
func (s *Store) Verify(acc *auth.Account, res *auth.Resource) error {
queries := [][]string{
{res.Type, "*"}, // check for wildcard resource type, e.g. service.*
{res.Type, res.Name, "*"}, // check for wildcard name, e.g. service.foo*
{res.Type, res.Name, res.Endpoint, "*"}, // check for wildcard endpoints, e.g. service.foo.ListFoo:*
{res.Type, res.Name, res.Endpoint}, // check for specific role, e.g. service.foo.ListFoo:admin
}
for _, q := range queries {
rules, err := s.listRules(q...)
if err != nil {
return err
}
if len(records) == 0 {
return errors.BadRequest("go.micro.auth", "token not found")
}
for _, r := range records {
if err := a.store.Delete(r.Key); err != nil {
return errors.InternalServerError("go.micro.auth", "error deleting from store")
}
}
for _, rule := range rules {
if isValidRule(rule, acc, res) {
return nil
}
}
}
// Verify an account token
func (a *Auth) Verify(token string) (*auth.Account, error) {
// lookup the record by token
records, err := a.store.Read(token, store.ReadSuffix())
if err == store.ErrNotFound || len(records) == 0 {
return nil, errors.Unauthorized("go.micro.auth", "invalid token")
return auth.ErrForbidden
}
// Inspect a token
func (s *Store) Inspect(t string) (*auth.Account, error) {
tok, err := s.tokenProvider.Inspect(t)
if err == token.ErrInvalidToken || err == token.ErrNotFound {
return nil, auth.ErrInvalidToken
} else if err != nil {
return nil, errors.InternalServerError("go.micro.auth", "error reading store")
return nil, err
}
// decode the result
// TODO: replace with json
b := bytes.NewBuffer(records[0].Value)
decoder := gob.NewDecoder(b)
var sa auth.Account
err = decoder.Decode(&sa)
// return the result
return &sa, err
return &auth.Account{
ID: tok.Subject,
Roles: tok.Roles,
Metadata: tok.Metadata,
}, nil
}
// String returns the implementation
func (a *Auth) String() string {
return "store"
// Refresh an account using a secret
func (s *Store) Refresh(secret string, opts ...auth.RefreshOption) (*auth.Token, error) {
sec, err := s.secretProvider.Inspect(secret)
if err == token.ErrInvalidToken || err == token.ErrNotFound {
return nil, auth.ErrInvalidToken
} else if err != nil {
return nil, err
}
options := auth.NewRefreshOptions(opts...)
return s.tokenProvider.Generate(sec.Subject,
token.WithExpiry(options.TokenExpiry),
token.WithMetadata(sec.Metadata),
token.WithRoles(sec.Roles),
)
}

287
auth/store/store_test.go Normal file
View File

@ -0,0 +1,287 @@
package store
import (
"log"
"testing"
"github.com/micro/go-micro/v2/auth"
memStore "github.com/micro/go-micro/v2/store/memory"
)
func TestGenerate(t *testing.T) {
s := memStore.NewStore()
a := NewAuth(auth.Store(s))
id := "test"
roles := []string{"admin"}
metadata := map[string]string{"foo": "bar"}
opts := []auth.GenerateOption{
auth.WithRoles(roles),
auth.WithMetadata(metadata),
}
// generate the account
acc, err := a.Generate(id, opts...)
if err != nil {
t.Fatalf("Generate returned an error: %v, expected nil", err)
}
// validate the account attributes were set correctly
if acc.ID != id {
t.Errorf("Generate returned %v as the ID, expected %v", acc.ID, id)
}
if len(acc.Roles) != len(roles) {
t.Errorf("Generate returned %v as the roles, expected %v", acc.Roles, roles)
}
if len(acc.Metadata) != len(metadata) {
t.Errorf("Generate returned %v as the metadata, expected %v", acc.Metadata, metadata)
}
// validate the secret is valid
if _, err := a.Refresh(acc.Secret.Token); err != nil {
t.Errorf("Generate returned an invalid secret, error: %v", err)
}
}
func TestGrant(t *testing.T) {
s := memStore.NewStore()
a := NewAuth(auth.Store(s))
res := &auth.Resource{Type: "service", Name: "Test", Endpoint: "Foo.Bar"}
if err := a.Grant("users.*", res); err != nil {
t.Fatalf("Grant returned an error: %v, expected nil", err)
}
recs, err := s.List()
if err != nil {
t.Fatalf("Could not read from the store: %v", err)
}
if len(recs) != 1 {
t.Errorf("Expected Grant to write 1 record, actually wrote %v", len(recs))
}
}
func TestRevoke(t *testing.T) {
s := memStore.NewStore()
a := NewAuth(auth.Store(s))
res := &auth.Resource{Type: "service", Name: "Test", Endpoint: "Foo.Bar"}
if err := a.Grant("users.*", res); err != nil {
t.Fatalf("Grant returned an error: %v, expected nil", err)
}
recs, err := s.List()
if err != nil {
t.Fatalf("Could not read from the store: %v", err)
}
if len(recs) != 1 {
t.Fatalf("Expected Grant to write 1 record, actually wrote %v", len(recs))
}
if err := a.Revoke("users.*", res); err != nil {
t.Fatalf("Revoke returned an error: %v, expected nil", err)
}
recs, err = s.List()
if err != nil {
t.Fatalf("Could not read from the store: %v", err)
}
if len(recs) != 0 {
t.Fatalf("Expected Revoke to delete 1 record, actually deleted %v", 1-len(recs))
}
}
func TestInspect(t *testing.T) {
a := NewAuth()
t.Run("Valid Token", func(t *testing.T) {
id := "test"
roles := []string{"admin"}
metadata := map[string]string{"foo": "bar"}
opts := []auth.GenerateOption{
auth.WithRoles(roles),
auth.WithMetadata(metadata),
}
// generate and inspect the token
acc, err := a.Generate("test", opts...)
if err != nil {
log.Fatalf("Generate returned an error: %v, expected nil", err)
}
tok, err := a.Refresh(acc.Secret.Token)
if err != nil {
log.Fatalf("Refresh returned an error: %v, expected nil", err)
}
acc2, err := a.Inspect(tok.Token)
if err != nil {
log.Fatalf("Inspect returned an error: %v, expected nil", err)
}
// validate the account attributes were retrieved correctly
if acc2.ID != id {
t.Errorf("Generate returned %v as the ID, expected %v", acc.ID, id)
}
if len(acc2.Roles) != len(roles) {
t.Errorf("Generate returned %v as the roles, expected %v", acc.Roles, roles)
}
if len(acc2.Metadata) != len(metadata) {
t.Errorf("Generate returned %v as the metadata, expected %v", acc.Metadata, metadata)
}
})
t.Run("Invalid Token", func(t *testing.T) {
_, err := a.Inspect("invalid token")
if err != auth.ErrInvalidToken {
t.Errorf("Inspect returned %v error, expected %v", err, auth.ErrInvalidToken)
}
})
}
func TestRefresh(t *testing.T) {
a := NewAuth()
t.Run("Valid Secret", func(t *testing.T) {
roles := []string{"admin"}
metadata := map[string]string{"foo": "bar"}
opts := []auth.GenerateOption{
auth.WithRoles(roles),
auth.WithMetadata(metadata),
}
// generate the account
acc, err := a.Generate("test", opts...)
if err != nil {
log.Fatalf("Generate returned an error: %v, expected nil", err)
}
// refresh the token
tok, err := a.Refresh(acc.Secret.Token)
if err != nil {
log.Fatalf("Refresh returned an error: %v, expected nil", err)
}
// validate the account attributes were set correctly
if acc.ID != tok.Subject {
t.Errorf("Refresh returned %v as the ID, expected %v", acc.ID, tok.Subject)
}
if len(acc.Roles) != len(tok.Roles) {
t.Errorf("Refresh returned %v as the roles, expected %v", acc.Roles, tok.Subject)
}
if len(acc.Metadata) != len(tok.Metadata) {
t.Errorf("Refresh returned %v as the metadata, expected %v", acc.Metadata, tok.Metadata)
}
})
t.Run("Invalid Secret", func(t *testing.T) {
_, err := a.Refresh("invalid secret")
if err != auth.ErrInvalidToken {
t.Errorf("Inspect returned %v error, expected %v", err, auth.ErrInvalidToken)
}
})
}
func TestVerify(t *testing.T) {
testRules := []struct {
Role string
Resource *auth.Resource
}{
{
Role: "*",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.PublicList"},
},
{
Role: "user.*",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.List"},
},
{
Role: "user.developer",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Update"},
},
{
Role: "admin",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Delete"},
},
{
Role: "admin",
Resource: &auth.Resource{Type: "service", Name: "*", Endpoint: "*"},
},
}
a := NewAuth()
for _, r := range testRules {
if err := a.Grant(r.Role, r.Resource); err != nil {
t.Fatalf("Grant returned an error: %v, expected nil", err)
}
}
testTable := []struct {
Name string
Roles []string
Resource *auth.Resource
Error error
}{
{
Name: "An account with no roles accessing a public endpoint",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.PublicList"},
},
{
Name: "An account with no roles accessing a private endpoint",
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Update"},
Error: auth.ErrForbidden,
},
{
Name: "An account with the user role accessing a user* endpoint",
Roles: []string{"user"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.List"},
},
{
Name: "An account with the user role accessing a user.admin endpoint",
Roles: []string{"user"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Delete"},
Error: auth.ErrForbidden,
},
{
Name: "An account with the developer role accessing a user.developer endpoint",
Roles: []string{"user.developer"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Update"},
},
{
Name: "An account with the developer role accessing an admin endpoint",
Roles: []string{"user.developer"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Delete"},
Error: auth.ErrForbidden,
},
{
Name: "An admin account accessing an admin endpoint",
Roles: []string{"admin"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.apps", Endpoint: "Apps.Delete"},
},
{
Name: "An admin account accessing a generic service endpoint",
Roles: []string{"admin"},
Resource: &auth.Resource{Type: "service", Name: "go.micro.foo", Endpoint: "Foo.Bar"},
},
{
Name: "An admin account accessing an unauthorised endpoint",
Roles: []string{"admin"},
Resource: &auth.Resource{Type: "infra", Name: "go.micro.foo", Endpoint: "Foo.Bar"},
Error: auth.ErrForbidden,
},
{
Name: "A account with no roles accessing an unauthorised endpoint",
Resource: &auth.Resource{Type: "infra", Name: "go.micro.foo", Endpoint: "Foo.Bar"},
Error: auth.ErrForbidden,
},
}
for _, tc := range testTable {
t.Run(tc.Name, func(t *testing.T) {
acc := &auth.Account{Roles: tc.Roles}
if err := a.Verify(acc, tc.Resource); err != tc.Error {
t.Errorf("Verify returned %v error, expected %v", err, tc.Error)
}
})
}
}

95
auth/token/basic/basic.go Normal file
View File

@ -0,0 +1,95 @@
package basic
import (
"encoding/json"
"time"
"github.com/google/uuid"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/store"
)
// Basic implementation of token provider, backed by the store
type Basic struct {
store store.Store
}
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
options := token.NewOptions(opts...)
if options.Store == nil {
options.Store = store.DefaultStore
}
return &Basic{
store: options.Store,
}
}
// Generate a token for an account
func (b *Basic) Generate(subject string, opts ...token.GenerateOption) (*auth.Token, error) {
options := token.NewGenerateOptions(opts...)
// construct the token
token := auth.Token{
Subject: subject,
Type: b.String(),
Token: uuid.New().String(),
Created: time.Now(),
Expiry: time.Now().Add(options.Expiry),
Metadata: options.Metadata,
Roles: options.Roles,
}
// marshal the account to bytes
bytes, err := json.Marshal(token)
if err != nil {
return nil, err
}
// write to the store
err = b.store.Write(&store.Record{
Key: token.Token,
Value: bytes,
Expiry: options.Expiry,
})
if err != nil {
return nil, err
}
// return the token
return &token, nil
}
// Inspect a token
func (b *Basic) Inspect(t string) (*auth.Token, error) {
// lookup the token in the store
recs, err := b.store.Read(t)
if err == store.ErrNotFound {
return nil, token.ErrInvalidToken
} else if err != nil {
return nil, err
}
bytes := recs[0].Value
// unmarshal the bytes
var tok *auth.Token
if err := json.Unmarshal(bytes, &tok); err != nil {
return nil, err
}
// ensure the token hasn't expired, the store should
// expire the token but we're checking again
if tok.Expiry.Unix() < time.Now().Unix() {
return nil, token.ErrInvalidToken
}
return tok, err
}
// String returns basic
func (b *Basic) String() string {
return "basic"
}

View File

@ -0,0 +1,80 @@
package basic
import (
"testing"
"time"
"github.com/micro/go-micro/v2/auth/token"
"github.com/micro/go-micro/v2/store/memory"
)
func TestGenerate(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
_, err := b.Generate("test")
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
recs, err := store.List()
if err != nil {
t.Fatalf("Unable to read from store: %v", err)
}
if len(recs) != 1 {
t.Errorf("Generate didn't write to the store, expected 1 record, got %v", len(recs))
}
}
func TestInspect(t *testing.T) {
store := memory.NewStore()
b := NewTokenProvider(token.WithStore(store))
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
roles := []string{"admin"}
subject := "test"
opts := []token.GenerateOption{
token.WithMetadata(md),
token.WithRoles(roles),
}
tok, err := b.Generate(subject, opts...)
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := b.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if tok2.Subject != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.Subject, subject)
}
if len(tok2.Roles) != len(roles) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Expired token", func(t *testing.T) {
tok, err := b.Generate("foo", token.WithExpiry(-10*time.Second))
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
if _, err = b.Inspect(tok.Token); err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := b.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

111
auth/token/jwt/jwt.go Normal file
View File

@ -0,0 +1,111 @@
package jwt
import (
"encoding/base64"
"time"
"github.com/dgrijalva/jwt-go"
"github.com/micro/go-micro/v2/auth"
"github.com/micro/go-micro/v2/auth/token"
)
// authClaims to be encoded in the JWT
type authClaims struct {
Roles []string `json:"roles"`
Metadata map[string]string `json:"metadata"`
jwt.StandardClaims
}
// JWT implementation of token provider
type JWT struct {
opts token.Options
}
// NewTokenProvider returns an initialized basic provider
func NewTokenProvider(opts ...token.Option) token.Provider {
return &JWT{
opts: token.NewOptions(opts...),
}
}
// Generate a new JWT
func (j *JWT) Generate(subject string, opts ...token.GenerateOption) (*auth.Token, error) {
// decode the private key
priv, err := base64.StdEncoding.DecodeString(j.opts.PrivateKey)
if err != nil {
return nil, err
}
// parse the private key
key, err := jwt.ParseRSAPrivateKeyFromPEM(priv)
if err != nil {
return nil, token.ErrEncodingToken
}
// parse the options
options := token.NewGenerateOptions(opts...)
// generate the JWT
expiry := time.Now().Add(options.Expiry)
t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{
options.Roles, options.Metadata, jwt.StandardClaims{
Subject: subject,
ExpiresAt: expiry.Unix(),
},
})
tok, err := t.SignedString(key)
if err != nil {
return nil, err
}
// return the token
return &auth.Token{
Subject: subject,
Token: tok,
Type: j.String(),
Created: time.Now(),
Expiry: expiry,
Roles: options.Roles,
Metadata: options.Metadata,
}, nil
}
// Inspect a JWT
func (j *JWT) Inspect(t string) (*auth.Token, error) {
// decode the public key
pub, err := base64.StdEncoding.DecodeString(j.opts.PublicKey)
if err != nil {
return nil, err
}
// parse the public key
res, err := jwt.ParseWithClaims(t, &authClaims{}, func(token *jwt.Token) (interface{}, error) {
return jwt.ParseRSAPublicKeyFromPEM(pub)
})
if err != nil {
return nil, token.ErrInvalidToken
}
// validate the token
if !res.Valid {
return nil, token.ErrInvalidToken
}
claims, ok := res.Claims.(*authClaims)
if !ok {
return nil, token.ErrInvalidToken
}
// return the token
return &auth.Token{
Token: t,
Subject: claims.Subject,
Metadata: claims.Metadata,
Roles: claims.Roles,
}, nil
}
// String returns JWT
func (j *JWT) String() string {
return "jwt"
}

View File

@ -0,0 +1,90 @@
package jwt
import (
"io/ioutil"
"testing"
"time"
"github.com/micro/go-micro/v2/auth/token"
)
func TestGenerate(t *testing.T) {
privKey, err := ioutil.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPrivateKey(string(privKey)),
)
_, err = j.Generate("test")
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
}
func TestInspect(t *testing.T) {
pubKey, err := ioutil.ReadFile("test/sample_key.pub")
if err != nil {
t.Fatalf("Unable to read public key: %v", err)
}
privKey, err := ioutil.ReadFile("test/sample_key")
if err != nil {
t.Fatalf("Unable to read private key: %v", err)
}
j := NewTokenProvider(
token.WithPublicKey(string(pubKey)),
token.WithPrivateKey(string(privKey)),
)
t.Run("Valid token", func(t *testing.T) {
md := map[string]string{"foo": "bar"}
roles := []string{"admin"}
subject := "test"
opts := []token.GenerateOption{
token.WithMetadata(md),
token.WithRoles(roles),
}
tok, err := j.Generate(subject, opts...)
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
tok2, err := j.Inspect(tok.Token)
if err != nil {
t.Fatalf("Inspect returned %v error, expected nil", err)
}
if tok2.Subject != subject {
t.Errorf("Inspect returned %v as the token subject, expected %v", tok2.Subject, subject)
}
if len(tok2.Roles) != len(roles) {
t.Errorf("Inspect returned %v roles, expected %v", len(tok2.Roles), len(roles))
}
if len(tok2.Metadata) != len(md) {
t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md)
}
})
t.Run("Expired token", func(t *testing.T) {
tok, err := j.Generate("foo", token.WithExpiry(-10*time.Second))
if err != nil {
t.Fatalf("Generate returned %v error, expected nil", err)
}
if _, err = j.Inspect(tok.Token); err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
t.Run("Invalid token", func(t *testing.T) {
_, err := j.Inspect("Invalid token")
if err != token.ErrInvalidToken {
t.Fatalf("Inspect returned %v error, expected %v", err, token.ErrInvalidToken)
}
})
}

View File

@ -0,0 +1 @@
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS3dJQkFBS0NBZ0VBOFNiSlA1WGJFaWRSbTViMnNOcExHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkCi9SbDkvMXBNVjdNaU8zTEh3dGhIQzJCUllxcisxd0Zkb1pDR0JZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUKMEJIL2xYUU1xeUVxRjVNSTJ6ZWpDNHpNenIxNU9OK2dFNEpuaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtLwptVWRJVC9MYUY3a1F4eVlLNVZLbitOZ09Xek1sektBQXBDbjdUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsCm85akRqbFk1b0JPY3pmcWVOV0hLNUdYQjdRd3BMTmg5NDZQelpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDUKd2xFcThoTmhtaG01Tk5lL08rR2dqQkROU2ZVaDA2K3E0bmdtYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1bwpSdFFoZ2lZOTEwcFBmOWJhdVhXcXdVQ1VhNHFzSHpqS1IwTC9OMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVCnJnTHJQYkVCOWVnY0drMzgrYnBLczNaNlJyNSt0bkQxQklQSUZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVUKVEdEeFV4OG9qOFZJZVJuV0RxNk1jMWlKcDhVeWNpQklUUnR3NGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMApsYVF6QXVQM2FpV1hJTXAyc2M4U2MrQmwrTGpYbUJveEJyYUJIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YCmdGS1NzSW5IRHJIVk95V1BCZTNmYWRFYzc3YituYi9leE96cjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUEKQVFLQ0FnRUFqUzc1Q2VvUlRRcUtBNzZaaFNiNGEzNVlKRENtcEpSazFsRTNKYnFzNFYxRnhXaDBjZmJYeG9VMgpSdTRRYjUrZWhsdWJGSFQ2a1BxdG9uRWhRVExjMUNmVE9WbHJOb3hocDVZM2ZyUmlQcnNnNXcwK1R3RUtrcFJUCnltanJQTXdQbGxCM2U0NmVaYmVXWGc3R3FFVmptMGcxVFRRK0tocVM4R0w3VGJlTFhRN1ZTem9ydTNCNVRKMVEKeEN6TVB0dnQ2eDYrU3JrcmhvZG1iT3VNRkpDam1TbWxmck9pZzQ4Zkc3NUpERHRObXpLWHBEUVJpYUNodFJhVQpQRHpmUTlTamhYdFFqdkZvWFFFT3BqdkZVRjR2WldNUWNQNUw1VklDM3JRSWp4MFNzQTN6S0FwakVUbjJHNjN2CktZby8zVWttbzhkUCtGRHA3NCs5a3pLNHFFaFJycEl3bEtiN0VOZWtDUXZqUFl1K3pyKzMyUXdQNTJ2L2FveWQKdjJJaUY3M2laTU1vZDhhYjJuQStyVEI2T0cvOVlSYk5kV21tay9VTi9jUHYrN214TmZ6Y1d1ZU1XcThxMXh4eAptNTNpR0NSQ29PQ1lDQk4zcUFkb1JwYW5xd3lCOUxrLzFCQjBHUld3MjgxK3VhNXNYRnZBVDBKeTVURnduMncvClU1MlJKWFlNOXVhMFBvd214b0RDUWRuNFZYVkdNZGdXaHN4aXhHRlYwOUZObWJJQWJaN0xaWGtkS1gzc1ZVbTcKWU1WYWIzVVo2bEhtdXYzT1NzcHNVUlRqN1hiRzZpaVVlaDU1aW91OENWbnRndWtFcnEzQTQwT05FVzhjNDBzOQphVTBGaSs4eWZpQTViaVZHLzF0bWlucUVERkhuQStnWk1xNEhlSkZxcWZxaEZKa1JwRGtDZ2dFQkFQeGR1NGNKCm5Da1duZDdPWFlHMVM3UDdkVWhRUzgwSDlteW9uZFc5bGFCQm84RWRPeTVTZzNOUmsxQ2pNZFZ1a3FMcjhJSnkKeStLWk15SVpvSlJvbllaMEtIUUVMR3ZLbzFOS2NLQ1FJbnYvWHVCdFJpRzBVb1pQNVkwN0RpRFBRQWpYUjlXUwpBc0EzMmQ1eEtFOC91Y3h0MjVQVzJFakNBUmtVeHQ5d0tKazN3bC9JdXVYRlExTDdDWjJsOVlFUjlHeWxUbzhNCmxXUEY3YndtUFV4UVNKaTNVS0FjTzZweTVUU1lkdWQ2aGpQeXJwSXByNU42VGpmTlRFWkVBeU9LbXVpOHVkUkoKMUg3T3RQVEhGZElKQjNrNEJnRDZtRE1HbjB2SXBLaDhZN3NtRUZBbFkvaXlCZjMvOHk5VHVMb1BycEdqR3RHbgp4Y2RpMHFud2p0SGFNbFVDZ2dFQkFQU2Z0dVFCQ2dTU2JLUSswUEFSR2VVeEQyTmlvZk1teENNTmdHUzJ5Ull3CjRGaGV4ZWkwMVJoaFk1NjE3UjduR1dzb0czd1RQa3dvRTJtbE1aQkoxeWEvUU9RRnQ3WG02OVl0RGh0T2FWbDgKL0o4dlVuSTBtWmxtT2pjTlRoYnVPZDlNSDlRdGxIRUMxMlhYdHJNb3Fsb0U2a05TT0pJalNxYm9wcDRXc1BqcApvZTZ0Nkdyd1RhOHBHeUJWWS90Mi85Ym5ORHVPVlpjODBaODdtY2gzcDNQclBqU3h5di9saGxYMFMwYUdHTkhTCk1XVjdUa25OaGo1TWlIRXFnZ1pZemtBWTkyd1JoVENnU1A2M0VNcitUWXFudXVuMXJHbndPYm95TDR2aFRpV0UKcU42UDNCTFlCZ1FpMllDTDludEJrOEl6RHZyd096dW5GVnhhZ0g5SVVoY0NnZ0VCQUwzQXlLa1BlOENWUmR6cQpzL284VkJDZmFSOFhhUGRnSGxTek1BSXZpNXEwNENqckRyMlV3MHZwTVdnM1hOZ0xUT3g5bFJpd3NrYk9SRmxHCmhhd3hRUWlBdkk0SE9WTlBTU0R1WHVNTG5USTQ0S0RFNlMrY2cxU0VMS2pWbDVqcDNFOEpkL1RJMVpLc0xBQUsKZTNHakM5UC9ZbE8xL21ndW4xNjVkWk01cFAwWHBPb2FaeFV2RHFFTktyekR0V1g0RngyOTZlUzdaSFJodFpCNwovQ2t1VUhlcmxrN2RDNnZzdWhTaTh2eTM3c0tPbmQ0K3c4cVM4czhZYVZxSDl3ZzVScUxxakp0bmJBUnc3alVDCm9KQ053M1hNdnc3clhaYzRTbnhVQUNMRGJNV2lLQy9xL1ZGWW9oTEs2WkpUVkJscWd5cjBSYzBRWmpDMlNJb0kKMjRwRWt3VUNnZ0VCQUpqb0FJVVNsVFY0WlVwaExXN3g4WkxPa01UWjBVdFFyd2NPR0hSYndPUUxGeUNGMVFWNQppejNiR2s4SmZyZHpVdk1sTmREZm9uQXVHTHhQa3VTVEUxWlg4L0xVRkJveXhyV3dvZ0cxaUtwME11QTV6em90CjROai9DbUtCQVkvWnh2anA5M2RFS21aZGxWQkdmeUFMeWpmTW5MWUovZXh5L09YSnhPUktZTUttSHg4M08zRWsKMWhvb0FwbTZabTIzMjRGME1iVU1ham5Idld2ZjhHZGJTNk5zcHd4L0dkbk1tYVMrdUJMVUhVMkNLbmc1bEIwVAp4OWJITmY0dXlPbTR0dXRmNzhCd1R5V3UreEdrVW0zZ2VZMnkvR1hqdDZyY2l1ajFGNzFDenZzcXFmZThTcDdJCnd6SHdxcTNzVHR5S2lCYTZuYUdEYWpNR1pKYSt4MVZJV204Q2dnRUJBT001ajFZR25Ba0pxR0czQWJSVDIvNUMKaVVxN0loYkswOGZsSGs5a2YwUlVjZWc0ZVlKY3dIRXJVaE4rdWQyLzE3MC81dDYra0JUdTVZOUg3bkpLREtESQpoeEg5SStyamNlVkR0RVNTRkluSXdDQ1lrOHhOUzZ0cHZMV1U5b0pibGFKMlZsalV2NGRFWGVQb0hkREh1Zk9ZClVLa0lsV2E3Uit1QzNEOHF5U1JrQnFLa3ZXZ1RxcFNmTVNkc1ZTeFIzU2Q4SVhFSHFjTDNUNEtMWGtYNEdEamYKMmZOSTFpZkx6ekhJMTN3Tk5IUTVRNU9SUC9pell2QzVzZkx4U2ZIUXJiMXJZVkpKWkI5ZjVBUjRmWFpHSVFsbApjMG8xd0JmZFlqMnZxVDlpR09IQnNSSTlSL2M2RzJQcUt3aFRpSzJVR2lmVFNEUVFuUkF6b2tpQVkrbE8vUjQ9Ci0tLS0tRU5EIFJTQSBQUklWQVRFIEtFWS0tLS0tCg==

View File

@ -0,0 +1 @@
LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUE4U2JKUDVYYkVpZFJtNWIyc05wTApHbzJlV2ZVNU9KZTBpemdySHdEOEg3RjZQa1BkL1JsOS8xcE1WN01pTzNMSHd0aEhDMkJSWXFyKzF3RmRvWkNHCkJZckxhWHVYRnFLMHZ1WmhQcUUzYXpqdUlIUXUwQkgvbFhRTXF5RXFGNU1JMnplakM0ek16cjE1T04rZ0U0Sm4KaXBqcC9DZGpPUEFEbUpHK0JKOXFlRS9RUGVtL21VZElUL0xhRjdrUXh5WUs1VktuK05nT1d6TWx6S0FBcENuNwpUVEtCVWU4RlpHNldTWDdMVjBlTEdIc29pYnhsbzlqRGpsWTVvQk9jemZxZU5XSEs1R1hCN1F3cExOaDk0NlB6ClpucW9hcFdVZStZL1JPaUhpekpUY3I1Wk1TTDV3bEVxOGhOaG1obTVOTmUvTytHZ2pCRE5TZlVoMDYrcTRuZ20KYm1OWDVoODM4QmJqUmN5YzM2ZHd6NkpVK2R1b1J0UWhnaVk5MTBwUGY5YmF1WFdxd1VDVWE0cXNIempLUjBMLwpOMVhYQXlsQ0RqeWVnWnp6Y093MkNIOFNrZkZVcmdMclBiRUI5ZWdjR2szOCticEtzM1o2UnI1K3RuRDFCSVBJCkZHTGVJMFVPQzAreGlCdjBvenhJRE9GbldhOVVUR0R4VXg4b2o4VkllUm5XRHE2TWMxaUpwOFV5Y2lCSVRSdHcKNGRabzcweG1mbmVJV3pyM0tTTmFoU29nSmRSMGxhUXpBdVAzYWlXWElNcDJzYzhTYytCbCtMalhtQm94QnJhQgpIaDlLa0pKRWNnQUZ3czJib2pDbEpPWXhvRi9YZ0ZLU3NJbkhEckhWT3lXUEJlM2ZhZEVjNzdiK25iL2V4T3pyCjFFcnhoR2c5akZtcmtPK3M0eEdodjZNQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=

96
auth/token/options.go Normal file
View File

@ -0,0 +1,96 @@
package token
import (
"time"
"github.com/micro/go-micro/v2/store"
)
type Options struct {
// Store to persist the tokens
Store store.Store
// PublicKey base64 encoded, used by JWT
PublicKey string
// PrivateKey base64 encoded, used by JWT
PrivateKey string
}
type Option func(o *Options)
// WithStore sets the token providers store
func WithStore(s store.Store) Option {
return func(o *Options) {
o.Store = s
}
}
// WithPublicKey sets the JWT public key
func WithPublicKey(key string) Option {
return func(o *Options) {
o.PublicKey = key
}
}
// WithPrivateKey sets the JWT private key
func WithPrivateKey(key string) Option {
return func(o *Options) {
o.PrivateKey = key
}
}
func NewOptions(opts ...Option) Options {
var options Options
for _, o := range opts {
o(&options)
}
//set default store
if options.Store == nil {
options.Store = store.DefaultStore
}
return options
}
type GenerateOptions struct {
// Expiry for the token
Expiry time.Duration
// Metadata associated with the account
Metadata map[string]string
// Roles/scopes associated with the account
Roles []string
}
type GenerateOption func(o *GenerateOptions)
// WithExpiry for the generated account's token expires
func WithExpiry(d time.Duration) GenerateOption {
return func(o *GenerateOptions) {
o.Expiry = d
}
}
// WithMetadata for the token
func WithMetadata(md map[string]string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Metadata = md
}
}
// WithRoles for the token
func WithRoles(rs []string) func(o *GenerateOptions) {
return func(o *GenerateOptions) {
o.Roles = rs
}
}
// NewGenerateOptions from a slice of options
func NewGenerateOptions(opts ...GenerateOption) GenerateOptions {
var options GenerateOptions
for _, o := range opts {
o(&options)
}
//set default Expiry of token
if options.Expiry == 0 {
options.Expiry = time.Minute * 15
}
return options
}

23
auth/token/token.go Normal file
View File

@ -0,0 +1,23 @@
package token
import (
"errors"
"github.com/micro/go-micro/v2/auth"
)
var (
// ErrNotFound is returned when a token cannot be found
ErrNotFound = errors.New("token not found")
// ErrEncodingToken is returned when the service encounters an error during encoding
ErrEncodingToken = errors.New("error encoding the token")
// ErrInvalidToken is returned when the token provided is not valid
ErrInvalidToken = errors.New("invalid token provided")
)
// Provider generates and inspects tokens
type Provider interface {
Generate(subject string, opts ...GenerateOption) (*auth.Token, error)
Inspect(token string) (*auth.Token, error)
String() string
}

View File

@ -70,8 +70,7 @@ import (
memTracer "github.com/micro/go-micro/v2/debug/trace/memory"
// auth
jwtAuth "github.com/micro/go-micro/v2/auth/jwt"
sAuth "github.com/micro/go-micro/v2/auth/service"
svcAuth "github.com/micro/go-micro/v2/auth/service"
storeAuth "github.com/micro/go-micro/v2/auth/store"
// auth providers
@ -271,11 +270,6 @@ var (
EnvVars: []string{"MICRO_AUTH_PRIVATE_KEY"},
Usage: "Private key for JWT auth (base64 encoded PEM)",
},
&cli.StringSliceFlag{
Name: "auth_exclude",
EnvVars: []string{"MICRO_AUTH_EXCLUDE"},
Usage: "Comma-separated list of endpoints excluded from authentication, e.g. Users.ListUsers",
},
&cli.StringFlag{
Name: "auth_provider",
EnvVars: []string{"MICRO_AUTH_PROVIDER"},
@ -365,9 +359,8 @@ var (
}
DefaultAuths = map[string]func(...auth.Option) auth.Auth{
"service": sAuth.NewAuth,
"service": svcAuth.NewAuth,
"store": storeAuth.NewAuth,
"jwt": jwtAuth.NewAuth,
}
DefaultAuthProviders = map[string]func(...provider.Option) provider.Provider{
@ -665,7 +658,7 @@ func (c *cmd) Before(ctx *cli.Context) error {
}
if len(ctx.String("auth_token")) > 0 {
authOpts = append(authOpts, auth.Token(ctx.String("auth_token")))
authOpts = append(authOpts, auth.ServiceToken(ctx.String("auth_token")))
}
if len(ctx.String("auth_public_key")) > 0 {
@ -676,10 +669,6 @@ func (c *cmd) Before(ctx *cli.Context) error {
authOpts = append(authOpts, auth.PrivateKey(ctx.String("auth_private_key")))
}
if len(ctx.StringSlice("auth_exclude")) > 0 {
authOpts = append(authOpts, auth.Exclude(ctx.StringSlice("auth_exclude")...))
}
if name := ctx.String("auth_provider"); len(name) > 0 {
p, ok := DefaultAuthProviders[name]
if !ok {
@ -707,9 +696,7 @@ func (c *cmd) Before(ctx *cli.Context) error {
}
if len(authOpts) > 0 {
if err := (*c.opts.Auth).Init(authOpts...); err != nil {
logger.Fatalf("Error configuring auth: %v", err)
}
(*c.opts.Auth).Init(authOpts...)
}
if ctx.String("config") == "service" {

View File

@ -118,7 +118,7 @@ func (s *service) Init(opts ...Option) {
// 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.Token(tk))
s.opts.Auth.Init(auth.ServiceToken(tk))
}
})
}

View File

@ -164,6 +164,11 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
return h(ctx, req, rsp)
}
// Check for auth service endpoints which should be excluded from auth
if strings.HasPrefix(req.Endpoint(), "Auth.") {
return h(ctx, req, rsp)
}
// Extract the token if present. Note: if noop is being used
// then the token can be blank without erroring
var token string
@ -177,29 +182,16 @@ func AuthHandler(fn func() auth.Auth) server.HandlerWrapper {
}
// Verify the token
account, authErr := a.Verify(token)
account, err := a.Inspect(token)
if err != nil {
return errors.Unauthorized("go.micro.auth", err.Error())
}
// If there is an account, set it in the context
if authErr == nil {
var err error
// There is an account, set it in the context
ctx, err = auth.ContextWithAccount(ctx, account)
if err != nil {
return err
}
}
// Return if the user disabled auth on this endpoint
for _, e := range a.Options().Exclude {
if e == req.Endpoint() {
return h(ctx, req, rsp)
}
}
// If the authErr is set, prevent the user from calling the endpoint
if authErr != nil {
return errors.Unauthorized("go.micro.auth", authErr.Error())
}
// The user is authorised, allow the call
return h(ctx, req, rsp)