add context partial user helper and accept a generic interface on SetUser - the same method now returns an error if the given value does not complete at least one method of the User interface

This commit is contained in:
Gerasimos (Makis) Maropoulos 2020-10-31 15:47:28 +02:00
parent 8eea0296a7
commit 3d59d19de6
No known key found for this signature in database
GPG Key ID: 5DBE766BD26A54E7
8 changed files with 356 additions and 191 deletions

View File

@ -56,7 +56,9 @@ func h(ctx iris.Context) {
// makes sure for that, otherwise this handler will not be executed. // makes sure for that, otherwise this handler will not be executed.
// OR: // OR:
user := ctx.User() user := ctx.User()
ctx.Writef("%s %s:%s", ctx.Path(), user.GetUsername(), user.GetPassword()) username, _ := user.GetUsername()
password, _ := user.GetPassword
ctx.Writef("%s %s:%s", ctx.Path(), username, password)
} }
func logout(ctx iris.Context) { func logout(ctx iris.Context) {

View File

@ -26,14 +26,14 @@ func main() {
signer := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute) signer := jwt.NewSigner(jwt.HS256, sigKey, 10*time.Minute)
// Enable payload encryption with: // Enable payload encryption with:
// signer.WithGCM(encKey, nil) // signer.WithEncryption(encKey, nil)
app.Get("/", generateToken(signer)) app.Get("/", generateToken(signer))
verifier := jwt.NewVerifier(jwt.HS256, sigKey) verifier := jwt.NewVerifier(jwt.HS256, sigKey)
// Enable server-side token block feature (even before its expiration time): // Enable server-side token block feature (even before its expiration time):
verifier.WithDefaultBlocklist() verifier.WithDefaultBlocklist()
// Enable payload decryption with: // Enable payload decryption with:
// verifier.WithGCM(encKey, nil) // verifier.WithDecryption(encKey, nil)
verifyMiddleware := verifier.Verify(func() interface{} { verifyMiddleware := verifier.Verify(func() interface{} {
return new(fooClaims) return new(fooClaims)
}) })

View File

@ -28,6 +28,21 @@ type UserClaims struct {
Username string `json:"username"` Username string `json:"username"`
} }
// GetID implements the partial context user's ID interface.
// Note that if claims were a map then the claims value converted to UserClaims
// and no need to implement any method.
//
// This is useful when multiple auth methods are used (e.g. basic auth, jwt)
// but they all share a couple of methods.
func (u *UserClaims) GetID() string {
return u.ID
}
// GetUsername implements the partial context user's Username interface.
func (u *UserClaims) GetUsername() string {
return u.Username
}
// Validate completes the middleware's custom ClaimsValidator. // Validate completes the middleware's custom ClaimsValidator.
// It will not accept a token which its claims missing the username field // It will not accept a token which its claims missing the username field
// (useful to not accept refresh tokens generated by the same algorithm). // (useful to not accept refresh tokens generated by the same algorithm).
@ -58,8 +73,15 @@ func main() {
protectedAPI.Use(verifyMiddleware) protectedAPI.Use(verifyMiddleware)
protectedAPI.Get("/", func(ctx iris.Context) { protectedAPI.Get("/", func(ctx iris.Context) {
claims := jwt.Get(ctx).(*UserClaims) // Access the claims through: jwt.Get:
ctx.Writef("Username: %s\n", claims.Username) // claims := jwt.Get(ctx).(*UserClaims)
// ctx.Writef("Username: %s\n", claims.Username)
//
// OR through context's user (if at least one method was implement by our UserClaims):
user := ctx.User()
id, _ := user.GetID()
username, _ := user.GetUsername()
ctx.Writef("ID: %s\nUsername: %s\n", id, username)
}) })
} }

View File

@ -5340,12 +5340,34 @@ func (ctx *Context) Logout(args ...interface{}) error {
const userContextKey = "iris.user" const userContextKey = "iris.user"
// SetUser sets a User for this request. // SetUser sets a value as a User for this request.
// It's used by auth middlewares as a common // It's used by auth middlewares as a common
// method to provide user information to the // method to provide user information to the
// next handlers in the chain. // next handlers in the chain
func (ctx *Context) SetUser(u User) { // Look the `User` method to retrieve it.
func (ctx *Context) SetUser(i interface{}) error {
if i == nil {
ctx.values.Remove(userContextKey)
return nil
}
u, ok := i.(User)
if !ok {
if m, ok := i.(Map); ok { // it's a map, convert it to a User.
u = UserMap(m)
} else {
// It's a structure, wrap it and let
// runtime decide the features.
p := newUserPartial(i)
if p == nil {
return ErrNotSupported
}
u = p
}
}
ctx.values.Set(userContextKey, u) ctx.values.Set(userContextKey, u)
return nil
} }
// User returns the registered User of this request. // User returns the registered User of this request.

View File

@ -1,6 +1,7 @@
package context package context
import ( import (
"encoding/json"
"errors" "errors"
"strings" "strings"
"time" "time"
@ -27,30 +28,33 @@ var ErrNotSupported = errors.New("not supported")
// To make optional some of the fields you can just embed the User interface // To make optional some of the fields you can just embed the User interface
// and implement whatever methods you want to support. // and implement whatever methods you want to support.
// //
// There are two builtin implementations of the User interface: // There are three builtin implementations of the User interface:
// - SimpleUser (type-safe) // - SimpleUser
// - UserMap (wraps a map[string]interface{}) // - UserMap (a wrapper by SetUser)
// - UserPartial (a wrapper by SetUser)
type User interface { type User interface {
// GetAuthorization should return the authorization method, // GetAuthorization should return the authorization method,
// e.g. Basic Authentication. // e.g. Basic Authentication.
GetAuthorization() string GetAuthorization() (string, error)
// GetAuthorizedAt should return the exact time the // GetAuthorizedAt should return the exact time the
// client has been authorized for the "first" time. // client has been authorized for the "first" time.
GetAuthorizedAt() time.Time GetAuthorizedAt() (time.Time, error)
// GetID should return the ID of the User.
GetID() (string, error)
// GetUsername should return the name of the User. // GetUsername should return the name of the User.
GetUsername() string GetUsername() (string, error)
// GetPassword should return the encoded or raw password // GetPassword should return the encoded or raw password
// (depends on the implementation) of the User. // (depends on the implementation) of the User.
GetPassword() string GetPassword() (string, error)
// GetEmail should return the e-mail of the User. // GetEmail should return the e-mail of the User.
GetEmail() string GetEmail() (string, error)
// GetRoles should optionally return the specific user's roles. // GetRoles should optionally return the specific user's roles.
// Returns `ErrNotSupported` if this method is not // Returns `ErrNotSupported` if this method is not
// implemented by the User implementation. // implemented by the User implementation.
GetRoles() ([]string, error) GetRoles() ([]string, error)
// GetToken should optionally return a token used // GetToken should optionally return a token used
// to authorize this User. // to authorize this User.
GetToken() (string, error) GetToken() ([]byte, error)
// GetField should optionally return a dynamic field // GetField should optionally return a dynamic field
// based on its key. Useful for custom user fields. // based on its key. Useful for custom user fields.
// Keep in mind that these fields are encoded as a separate JSON key. // Keep in mind that these fields are encoded as a separate JSON key.
@ -63,6 +67,7 @@ add a Raw() interface{} to return the underline User implementation too.
The advandages of the above idea is that we don't have to add new methods The advandages of the above idea is that we don't have to add new methods
for each of the builtin features and we can keep the (assumed) struct small. for each of the builtin features and we can keep the (assumed) struct small.
But we dont as it has many disadvantages, unless is requested. But we dont as it has many disadvantages, unless is requested.
^ UPDATE: this is done through UserPartial.
The disadvantage of the current implementation is that the developer MUST The disadvantage of the current implementation is that the developer MUST
complete the whole interface in order to be a valid User and if we add complete the whole interface in order to be a valid User and if we add
@ -72,88 +77,51 @@ We kind of by-pass this disadvantage by providing a SimpleUser which can be embe
to the end-developer's custom implementations. to the end-developer's custom implementations.
*/ */
// FeaturedUser optional interface that a User can implement.
type FeaturedUser interface {
User
// GetFeatures should optionally return a list of features
// the User implementation offers.
GetFeatures() []UserFeature
}
// UserFeature a type which represents a user's optional feature.
// See `HasUserFeature` function for more.
type UserFeature uint32
// The list of standard UserFeatures.
const (
AuthorizedAtFeature UserFeature = iota
UsernameFeature
PasswordFeature
EmailFeature
RolesFeature
TokenFeature
FieldsFeature
)
// HasUserFeature reports whether the "u" User
// implements a specific "feature" User Feature.
//
// It returns ErrNotSupported if a user does not implement
// the FeaturedUser interface.
func HasUserFeature(user User, feature UserFeature) (bool, error) {
if u, ok := user.(FeaturedUser); ok {
for _, f := range u.GetFeatures() {
if f == feature {
return true, nil
}
}
return false, nil
}
return false, ErrNotSupported
}
// SimpleUser is a simple implementation of the User interface. // SimpleUser is a simple implementation of the User interface.
type SimpleUser struct { type SimpleUser struct {
Authorization string `json:"authorization"` Authorization string `json:"authorization,omitempty"`
AuthorizedAt time.Time `json:"authorized_at"` AuthorizedAt time.Time `json:"authorized_at,omitempty"`
Username string `json:"username,omitempty"` ID string `json:"id,omitempty"`
Password string `json:"-"` Username string `json:"username,omitempty"`
Email string `json:"email,omitempty"` Password string `json:"-"`
Roles []string `json:"roles,omitempty"` Email string `json:"email,omitempty"`
Features []UserFeature `json:"features,omitempty"` Roles []string `json:"roles,omitempty"`
Token string `json:"token,omitempty"` Token json.RawMessage `json:"token,omitempty"`
Fields Map `json:"fields,omitempty"` Fields Map `json:"fields,omitempty"`
} }
var _ FeaturedUser = (*SimpleUser)(nil) var _ User = (*SimpleUser)(nil)
// GetAuthorization returns the authorization method, // GetAuthorization returns the authorization method,
// e.g. Basic Authentication. // e.g. Basic Authentication.
func (u *SimpleUser) GetAuthorization() string { func (u *SimpleUser) GetAuthorization() (string, error) {
return u.Authorization return u.Authorization, nil
} }
// GetAuthorizedAt returns the exact time the // GetAuthorizedAt returns the exact time the
// client has been authorized for the "first" time. // client has been authorized for the "first" time.
func (u *SimpleUser) GetAuthorizedAt() time.Time { func (u *SimpleUser) GetAuthorizedAt() (time.Time, error) {
return u.AuthorizedAt return u.AuthorizedAt, nil
}
// GetID returns the ID of the User.
func (u *SimpleUser) GetID() (string, error) {
return u.ID, nil
} }
// GetUsername returns the name of the User. // GetUsername returns the name of the User.
func (u *SimpleUser) GetUsername() string { func (u *SimpleUser) GetUsername() (string, error) {
return u.Username return u.Username, nil
} }
// GetPassword returns the raw password of the User. // GetPassword returns the raw password of the User.
func (u *SimpleUser) GetPassword() string { func (u *SimpleUser) GetPassword() (string, error) {
return u.Password return u.Password, nil
} }
// GetEmail returns the e-mail of the User. // GetEmail returns the e-mail of (string,error) User.
func (u *SimpleUser) GetEmail() string { func (u *SimpleUser) GetEmail() (string, error) {
return u.Email return u.Email, nil
} }
// GetRoles returns the specific user's roles. // GetRoles returns the specific user's roles.
@ -171,9 +139,9 @@ func (u *SimpleUser) GetRoles() ([]string, error) {
// //
// The implementation can change that behavior. // The implementation can change that behavior.
// Returns with `ErrNotSupported` if the Token field is empty. // Returns with `ErrNotSupported` if the Token field is empty.
func (u *SimpleUser) GetToken() (string, error) { func (u *SimpleUser) GetToken() ([]byte, error) {
if u.Token == "" { if len(u.Token) == 0 {
return "", ErrNotSupported return nil, ErrNotSupported
} }
return u.Token, nil return u.Token, nil
@ -189,104 +157,66 @@ func (u *SimpleUser) GetField(key string) (interface{}, error) {
return u.Fields[key], nil return u.Fields[key], nil
} }
// GetFeatures returns a list of features
// this User implementation offers.
func (u *SimpleUser) GetFeatures() []UserFeature {
if u.Features != nil {
return u.Features
}
var features []UserFeature
if !u.AuthorizedAt.IsZero() {
features = append(features, AuthorizedAtFeature)
}
if u.Username != "" {
features = append(features, UsernameFeature)
}
if u.Password != "" {
features = append(features, PasswordFeature)
}
if u.Email != "" {
features = append(features, EmailFeature)
}
if u.Roles != nil {
features = append(features, RolesFeature)
}
if u.Fields != nil {
features = append(features, FieldsFeature)
}
return features
}
// UserMap can be used to convert a common map[string]interface{} to a User. // UserMap can be used to convert a common map[string]interface{} to a User.
// Usage: // Usage:
// user := map[string]interface{}{ // user := map[string]interface{}{
// "username": "kataras", // "username": "kataras",
// "age" : 27, // "age" : 27,
// } // }
// ctx.SetUser(UserMap(user)) // ctx.SetUser(user)
// OR // OR
// user := UserMap{"key": "value",...} // user := UserStruct{....}
// ctx.SetUser(user) // ctx.SetUser(user)
// [...] // [...]
// username := ctx.User().GetUsername() // username, err := ctx.User().GetUsername()
// age := ctx.User().GetField("age").(int) // field,err := ctx.User().GetField("age")
// age := field.(int)
// OR cast it: // OR cast it:
// user := ctx.User().(UserMap) // user := ctx.User().(UserMap)
// username := user["username"].(string) // username := user["username"].(string)
// age := user["age"].(int) // age := user["age"].(int)
type UserMap Map type UserMap Map
var _ FeaturedUser = UserMap{} var _ User = UserMap{}
// GetAuthorization returns the authorization or Authorization value of the map. // GetAuthorization returns the authorization or Authorization value of the map.
func (u UserMap) GetAuthorization() string { func (u UserMap) GetAuthorization() (string, error) {
return u.str("authorization") return u.str("authorization")
} }
// GetAuthorizedAt returns the authorized_at or Authorized_At value of the map. // GetAuthorizedAt returns the authorized_at or Authorized_At value of the map.
func (u UserMap) GetAuthorizedAt() time.Time { func (u UserMap) GetAuthorizedAt() (time.Time, error) {
return u.time("authorized_at") return u.time("authorized_at")
} }
// GetID returns the id or Id or ID value of the map.
func (u UserMap) GetID() (string, error) {
return u.str("id")
}
// GetUsername returns the username or Username value of the map. // GetUsername returns the username or Username value of the map.
func (u UserMap) GetUsername() string { func (u UserMap) GetUsername() (string, error) {
return u.str("username") return u.str("username")
} }
// GetPassword returns the password or Password value of the map. // GetPassword returns the password or Password value of the map.
func (u UserMap) GetPassword() string { func (u UserMap) GetPassword() (string, error) {
return u.str("password") return u.str("password")
} }
// GetEmail returns the email or Email value of the map. // GetEmail returns the email or Email value of the map.
func (u UserMap) GetEmail() string { func (u UserMap) GetEmail() (string, error) {
return u.str("email") return u.str("email")
} }
// GetRoles returns the roles or Roles value of the map. // GetRoles returns the roles or Roles value of the map.
func (u UserMap) GetRoles() ([]string, error) { func (u UserMap) GetRoles() ([]string, error) {
if s := u.strSlice("roles"); s != nil { return u.strSlice("roles")
return s, nil
}
return nil, ErrNotSupported
} }
// GetToken returns the roles or Roles value of the map. // GetToken returns the roles or Roles value of the map.
func (u UserMap) GetToken() (string, error) { func (u UserMap) GetToken() ([]byte, error) {
if s := u.str("token"); s != "" { return u.bytes("token")
return s, nil
}
return "", ErrNotSupported
} }
// GetField returns the raw map's value based on its "key". // GetField returns the raw map's value based on its "key".
@ -295,41 +225,6 @@ func (u UserMap) GetField(key string) (interface{}, error) {
return u[key], nil return u[key], nil
} }
// GetFeatures returns a list of features
// this map offers.
func (u UserMap) GetFeatures() []UserFeature {
if v := u.val("features"); v != nil { // if already contain features.
if features, ok := v.([]UserFeature); ok {
return features
}
}
// else try to resolve from map values.
features := []UserFeature{FieldsFeature}
if !u.GetAuthorizedAt().IsZero() {
features = append(features, AuthorizedAtFeature)
}
if u.GetUsername() != "" {
features = append(features, UsernameFeature)
}
if u.GetPassword() != "" {
features = append(features, PasswordFeature)
}
if u.GetEmail() != "" {
features = append(features, EmailFeature)
}
if roles, err := u.GetRoles(); err == nil && roles != nil {
features = append(features, RolesFeature)
}
return features
}
func (u UserMap) val(key string) interface{} { func (u UserMap) val(key string) interface{} {
isTitle := unicode.IsTitle(rune(key[0])) // if starts with uppercase. isTitle := unicode.IsTitle(rune(key[0])) // if starts with uppercase.
if isTitle { if isTitle {
@ -339,34 +234,248 @@ func (u UserMap) val(key string) interface{} {
return u[key] return u[key]
} }
func (u UserMap) str(key string) string { func (u UserMap) bytes(key string) ([]byte, error) {
if v := u.val(key); v != nil {
switch s := v.(type) {
case []byte:
return s, nil
case string:
return []byte(s), nil
}
}
return nil, ErrNotSupported
}
func (u UserMap) str(key string) (string, error) {
if v := u.val(key); v != nil { if v := u.val(key); v != nil {
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
return s return s, nil
} }
// exists or not we don't care, if it's invalid type we don't fill it. // exists or not we don't care, if it's invalid type we don't fill it.
} }
return "" return "", ErrNotSupported
} }
func (u UserMap) strSlice(key string) []string { func (u UserMap) strSlice(key string) ([]string, error) {
if v := u.val(key); v != nil { if v := u.val(key); v != nil {
if s, ok := v.([]string); ok { if s, ok := v.([]string); ok {
return s return s, nil
} }
} }
return nil return nil, ErrNotSupported
} }
func (u UserMap) time(key string) time.Time { func (u UserMap) time(key string) (time.Time, error) {
if v := u.val(key); v != nil { if v := u.val(key); v != nil {
if t, ok := v.(time.Time); ok { if t, ok := v.(time.Time); ok {
return t return t, nil
} }
} }
return time.Time{} return time.Time{}, ErrNotSupported
}
type (
userGetAuthorization interface {
GetAuthorization() string
}
userGetAuthorizedAt interface {
GetAuthorizedAt() time.Time
}
userGetID interface {
GetID() string
}
userGetUsername interface {
GetUsername() string
}
userGetPassword interface {
GetPassword() string
}
userGetEmail interface {
GetEmail() string
}
userGetRoles interface {
GetRoles() []string
}
userGetToken interface {
GetToken() []byte
}
userGetField interface {
GetField(string) interface{}
}
// UserPartial is a User.
// It's a helper which wraps a struct value that
// may or may not complete the whole User interface.
UserPartial struct {
Raw interface{}
userGetAuthorization
userGetAuthorizedAt
userGetID
userGetUsername
userGetPassword
userGetEmail
userGetRoles
userGetToken
userGetField
}
)
var _ User = (*UserPartial)(nil)
func newUserPartial(i interface{}) *UserPartial {
containsAtLeastOneMethod := false
p := &UserPartial{Raw: i}
if u, ok := i.(userGetAuthorization); ok {
p.userGetAuthorization = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetAuthorizedAt); ok {
p.userGetAuthorizedAt = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetID); ok {
p.userGetID = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetUsername); ok {
p.userGetUsername = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetPassword); ok {
p.userGetPassword = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetEmail); ok {
p.userGetEmail = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetRoles); ok {
p.userGetRoles = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetToken); ok {
p.userGetToken = u
containsAtLeastOneMethod = true
}
if u, ok := i.(userGetField); ok {
p.userGetField = u
containsAtLeastOneMethod = true
}
if !containsAtLeastOneMethod {
return nil
}
return p
}
// GetAuthorization should return the authorization method,
// e.g. Basic Authentication.
func (u *UserPartial) GetAuthorization() (string, error) {
if v := u.userGetAuthorization; v != nil {
return v.GetAuthorization(), nil
}
return "", ErrNotSupported
}
// GetAuthorizedAt should return the exact time the
// client has been authorized for the "first" time.
func (u *UserPartial) GetAuthorizedAt() (time.Time, error) {
if v := u.userGetAuthorizedAt; v != nil {
return v.GetAuthorizedAt(), nil
}
return time.Time{}, ErrNotSupported
}
// GetID should return the ID of the User.
func (u *UserPartial) GetID() (string, error) {
if v := u.userGetID; v != nil {
return v.GetID(), nil
}
return "", ErrNotSupported
}
// GetUsername should return the name of the User.
func (u *UserPartial) GetUsername() (string, error) {
if v := u.userGetUsername; v != nil {
return v.GetUsername(), nil
}
return "", ErrNotSupported
}
// GetPassword should return the encoded or raw password
// (depends on the implementation) of the User.
func (u *UserPartial) GetPassword() (string, error) {
if v := u.userGetPassword; v != nil {
return v.GetPassword(), nil
}
return "", ErrNotSupported
}
// GetEmail should return the e-mail of the User.
func (u *UserPartial) GetEmail() (string, error) {
if v := u.userGetEmail; v != nil {
return v.GetEmail(), nil
}
return "", ErrNotSupported
}
// GetRoles should optionally return the specific user's roles.
// Returns `ErrNotSupported` if this method is not
// implemented by the User implementation.
func (u *UserPartial) GetRoles() ([]string, error) {
if v := u.userGetRoles; v != nil {
return v.GetRoles(), nil
}
return nil, ErrNotSupported
}
// GetToken should optionally return a token used
// to authorize this User.
func (u *UserPartial) GetToken() ([]byte, error) {
if v := u.userGetToken; v != nil {
return v.GetToken(), nil
}
return nil, ErrNotSupported
}
// GetField should optionally return a dynamic field
// based on its key. Useful for custom user fields.
// Keep in mind that these fields are encoded as a separate JSON key.
func (u *UserPartial) GetField(key string) (interface{}, error) {
if v := u.userGetField; v != nil {
return v.GetField(key), nil
}
return nil, ErrNotSupported
} }

View File

@ -26,7 +26,11 @@ func TestBasicAuthUseRouter(t *testing.T) {
app.Get("/user_string", func(ctx iris.Context) { app.Get("/user_string", func(ctx iris.Context) {
user := ctx.User() user := ctx.User()
ctx.Writef("%s\n%s\n%s", user.GetAuthorization(), user.GetUsername(), user.GetPassword())
authorization, _ := user.GetAuthorization()
username, _ := user.GetUsername()
password, _ := user.GetPassword()
ctx.Writef("%s\n%s\n%s", authorization, username, password)
}) })
app.Get("/", func(ctx iris.Context) { app.Get("/", func(ctx iris.Context) {

View File

@ -24,8 +24,8 @@ func NewSigner(signatureAlg Alg, signatureKey interface{}, maxAge time.Duration)
} }
} }
// WithGCM enables AES-GCM payload decryption. // WithEncryption enables AES-GCM payload decryption.
func (s *Signer) WithGCM(key, additionalData []byte) *Signer { func (s *Signer) WithEncryption(key, additionalData []byte) *Signer {
encrypt, _, err := jwt.GCM(key, additionalData) encrypt, _, err := jwt.GCM(key, additionalData)
if err != nil { if err != nil {
panic(err) // important error before serve, stop everything. panic(err) // important error before serve, stop everything.
@ -35,10 +35,14 @@ func (s *Signer) WithGCM(key, additionalData []byte) *Signer {
return s return s
} }
// Sign generates a new token based on the given "claims" which is valid up to "s.MaxAge".
func (s *Signer) Sign(claims interface{}, opts ...SignOption) ([]byte, error) { func (s *Signer) Sign(claims interface{}, opts ...SignOption) ([]byte, error) {
return SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, append([]SignOption{MaxAge(s.MaxAge)}, opts...)...) return SignEncrypted(s.Alg, s.Key, s.Encrypt, claims, append([]SignOption{MaxAge(s.MaxAge)}, opts...)...)
} }
// NewTokenPair accepts the access and refresh claims plus the life time duration for the refresh token
// and generates a new token pair which can be sent to the client.
// The same token pair can be json-decoded.
func (s *Signer) NewTokenPair(accessClaims interface{}, refreshClaims interface{}, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) { func (s *Signer) NewTokenPair(accessClaims interface{}, refreshClaims interface{}, refreshMaxAge time.Duration, accessOpts ...SignOption) (TokenPair, error) {
if refreshMaxAge <= s.MaxAge { if refreshMaxAge <= s.MaxAge {
return TokenPair{}, fmt.Errorf("refresh max age should be bigger than access token's one[%d - %d]", refreshMaxAge, s.MaxAge) return TokenPair{}, fmt.Errorf("refresh max age should be bigger than access token's one[%d - %d]", refreshMaxAge, s.MaxAge)

View File

@ -49,6 +49,8 @@ type Verifier struct {
Validators []TokenValidator Validators []TokenValidator
ErrorHandler func(ctx *context.Context, err error) ErrorHandler func(ctx *context.Context, err error)
// DisableContextUser disables the registration of the claims as context User.
DisableContextUser bool
} }
// NewVerifier accepts the algorithm for the token's signature among with its (private) key // NewVerifier accepts the algorithm for the token's signature among with its (private) key
@ -67,8 +69,8 @@ func NewVerifier(signatureAlg Alg, signatureKey interface{}, validators ...Token
} }
} }
// WithGCM enables AES-GCM payload encryption. // WithDecryption enables AES-GCM payload encryption.
func (v *Verifier) WithGCM(key, additionalData []byte) *Verifier { func (v *Verifier) WithDecryption(key, additionalData []byte) *Verifier {
_, decrypt, err := jwt.GCM(key, additionalData) _, decrypt, err := jwt.GCM(key, additionalData)
if err != nil { if err != nil {
panic(err) // important error before serve, stop everything. panic(err) // important error before serve, stop everything.
@ -176,8 +178,8 @@ func (v *Verifier) Verify(claimsType func() interface{}, validators ...TokenVali
} }
} }
if u, ok := dest.(context.User); ok { if !v.DisableContextUser {
ctx.SetUser(u) ctx.SetUser(dest)
} }
ctx.Values().Set(claimsContextKey, dest) ctx.Values().Set(claimsContextKey, dest)