diff --git a/auth/auth.go b/auth/auth.go index fee5792a..0ba23eca 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -49,7 +49,7 @@ type Auth interface { // Account provided by an auth provider type Account struct { - // ID of the account e.g. email + // ID of the account e.g. UUID. Should not change ID string `json:"id"` // Type of the account, e.g. service Type string `json:"type"` @@ -61,6 +61,8 @@ type Account struct { Scopes []string `json:"scopes"` // Secret for the account, e.g. the password Secret string `json:"secret"` + // Name of the account. User friendly name that might change e.g. a username or email + Name string `json:"name"` } // Token can be short or long lived diff --git a/auth/jwt/jwt.go b/auth/jwt/jwt.go index ab14728f..f898f699 100644 --- a/auth/jwt/jwt.go +++ b/auth/jwt/jwt.go @@ -54,13 +54,17 @@ func (j *jwtAuth) Generate(id string, opts ...auth.GenerateOption) (*auth.Accoun if len(options.Issuer) == 0 { options.Issuer = j.Options().Issuer } - + name := options.Name + if name == "" { + name = id + } account := &auth.Account{ ID: id, Type: options.Type, Scopes: options.Scopes, Metadata: options.Metadata, Issuer: options.Issuer, + Name: name, } // generate a JWT secret which can be provided to the Token() method diff --git a/auth/noop/noop.go b/auth/noop/noop.go index 573f4ca7..62d5a79d 100644 --- a/auth/noop/noop.go +++ b/auth/noop/noop.go @@ -40,13 +40,17 @@ func (n *noop) Options() auth.Options { // Generate a new account func (n *noop) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) { options := auth.NewGenerateOptions(opts...) - + name := options.Name + if name == "" { + name = id + } return &auth.Account{ ID: id, Secret: options.Secret, Metadata: options.Metadata, Scopes: options.Scopes, Issuer: n.Options().Issuer, + Name: name, }, nil } diff --git a/auth/options.go b/auth/options.go index e1bda400..ccb049fe 100644 --- a/auth/options.go +++ b/auth/options.go @@ -110,6 +110,8 @@ type GenerateOptions struct { Secret string // Issuer of the account, e.g. micro Issuer string + // Name of the acouunt e.g. an email or username + Name string } type GenerateOption func(o *GenerateOptions) @@ -156,6 +158,13 @@ func WithIssuer(i string) GenerateOption { } } +// WithName for the generated account +func WithName(n string) GenerateOption { + return func(o *GenerateOptions) { + o.Name = n + } +} + // NewGenerateOptions from a slice of options func NewGenerateOptions(opts ...GenerateOption) GenerateOptions { var options GenerateOptions diff --git a/util/token/jwt/jwt.go b/util/token/jwt/jwt.go index 40673192..7834ef5a 100644 --- a/util/token/jwt/jwt.go +++ b/util/token/jwt/jwt.go @@ -14,6 +14,7 @@ type authClaims struct { Type string `json:"type"` Scopes []string `json:"scopes"` Metadata map[string]string `json:"metadata"` + Name string `json:"name"` jwt.StandardClaims } @@ -47,10 +48,17 @@ func (j *JWT) Generate(acc *auth.Account, opts ...token.GenerateOption) (*token. // parse the options options := token.NewGenerateOptions(opts...) + // backwards compatibility + name := acc.Name + if name == "" { + name = acc.ID + } + // generate the JWT expiry := time.Now().Add(options.Expiry) t := jwt.NewWithClaims(jwt.SigningMethodRS256, authClaims{ - acc.Type, acc.Scopes, acc.Metadata, jwt.StandardClaims{ + Type: acc.Type, Scopes: acc.Scopes, Metadata: acc.Metadata, Name: name, + StandardClaims: jwt.StandardClaims{ Subject: acc.ID, Issuer: acc.Issuer, ExpiresAt: expiry.Unix(), @@ -94,6 +102,12 @@ func (j *JWT) Inspect(t string) (*auth.Account, error) { return nil, token.ErrInvalidToken } + // backwards compatibility + name := claims.Name + if name == "" { + name = claims.Subject + } + // return the token return &auth.Account{ ID: claims.Subject, @@ -101,6 +115,7 @@ func (j *JWT) Inspect(t string) (*auth.Account, error) { Type: claims.Type, Scopes: claims.Scopes, Metadata: claims.Metadata, + Name: name, }, nil } diff --git a/util/token/jwt/jwt_test.go b/util/token/jwt/jwt_test.go index 2afd9f7d..7cb35445 100644 --- a/util/token/jwt/jwt_test.go +++ b/util/token/jwt/jwt_test.go @@ -44,8 +44,9 @@ func TestInspect(t *testing.T) { md := map[string]string{"foo": "bar"} scopes := []string{"admin"} subject := "test" + name := "testname" - acc := &auth.Account{ID: subject, Scopes: scopes, Metadata: md} + acc := &auth.Account{ID: subject, Scopes: scopes, Metadata: md, Name: name} tok, err := j.Generate(acc) if err != nil { t.Fatalf("Generate returned %v error, expected nil", err) @@ -64,6 +65,9 @@ func TestInspect(t *testing.T) { if len(tok2.Metadata) != len(md) { t.Errorf("Inspect returned %v as the token metadata, expected %v", tok2.Metadata, md) } + if tok2.Name != name { + t.Errorf("Inspect returned %v as the token name, expected %v", tok2.Name, name) + } }) t.Run("Expired token", func(t *testing.T) { @@ -84,4 +88,19 @@ func TestInspect(t *testing.T) { } }) + t.Run("Default name", func(t *testing.T) { + tok, err := j.Generate(&auth.Account{ID: "test"}) + 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.Name != "test" { + t.Fatalf("Inspect returned %v as the token name, expected test", tok2.Name) + } + }) + }