mirror of
https://github.com/kataras/iris.git
synced 2025-02-02 15:30:36 +01:00
minor improvements
This commit is contained in:
parent
dceb09d4ff
commit
f17a325df6
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/kataras/iris/v12/auth"
|
"github.com/kataras/iris/v12/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func allowRole(role AccessRole) auth.TVerify[User] {
|
func allowRole(role AccessRole) auth.VerifyUserFunc[User] {
|
||||||
return func(u User) error {
|
return func(u User) error {
|
||||||
if !u.Role.Allow(role) {
|
if !u.Role.Allow(role) {
|
||||||
return fmt.Errorf("invalid role")
|
return fmt.Errorf("invalid role")
|
||||||
|
|
113
auth/auth.go
113
auth/auth.go
|
@ -18,51 +18,87 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type (
|
type (
|
||||||
|
// Auth holds the necessary functionality to authorize and optionally authenticating
|
||||||
|
// users to access and perform actions against the resource server (Iris API).
|
||||||
|
// It completes a secure and fast JSON Web Token signer and verifier which,
|
||||||
|
// based on the custom application needs, can be further customized.
|
||||||
|
// Each Auth of T instance can sign and verify a single custom <T> instance,
|
||||||
|
// more Auth instances can share the same configuration to support multiple custom user types.
|
||||||
|
// Initialize a new Auth of T instance using the New or MustLoad package-level functions.
|
||||||
|
// Most important methods of the instance are:
|
||||||
|
// - AddProvider
|
||||||
|
// - SigninHandler
|
||||||
|
// - VerifyHandler
|
||||||
|
// - SignoutHandler
|
||||||
|
// - SignoutAllHandler
|
||||||
|
//
|
||||||
|
// Example can be found at: https://github.com/kataras/iris/tree/master/_examples/auth/auth/main.go.
|
||||||
Auth[T User] struct {
|
Auth[T User] struct {
|
||||||
|
// Holds the configuration passed through the New and MustLoad
|
||||||
|
// package-level functions. One or more Auth instance can share the
|
||||||
|
// same configuration's values.
|
||||||
config Configuration
|
config Configuration
|
||||||
|
// Holds the result of the config.KeysConfiguration.
|
||||||
keys jwt.Keys
|
keys jwt.Keys
|
||||||
|
// This is an Iris cookie option used to encrypt and decrypt a cookie when
|
||||||
|
// the config.Cookie.Hash & Block are not empty.
|
||||||
securecookie context.SecureCookie
|
securecookie context.SecureCookie
|
||||||
|
// Defaults to an empty list, which cannot sign any tokens.
|
||||||
|
// One or more custom providers should be registered through
|
||||||
|
// the AddProvider or WithProviderAndErrorHandler methods.
|
||||||
providers []Provider[T] // at least one.
|
providers []Provider[T] // at least one.
|
||||||
|
// Always not nil, set to custom error handler on SetErrorHandler.
|
||||||
errorHandler ErrorHandler
|
errorHandler ErrorHandler
|
||||||
|
// Not nil if a transformer is registered.
|
||||||
transformer Transformer[T]
|
transformer Transformer[T]
|
||||||
|
// Not nil if a custom claims provider is registered.
|
||||||
claimsProvider ClaimsProvider
|
claimsProvider ClaimsProvider
|
||||||
refreshEnabled bool // if KIDRefresh exists in keys.
|
// True if KIDRefresh on config.Keys.
|
||||||
|
refreshEnabled bool
|
||||||
}
|
}
|
||||||
|
|
||||||
TVerify[T User] func(t T) error
|
// VerifyUserFunc is passed on Verify and VerifyHandler method
|
||||||
|
// to, optionally, further validate a T user value.
|
||||||
|
VerifyUserFunc[T User] func(t T) error
|
||||||
|
|
||||||
|
// SigninRequest is the request body the server expects
|
||||||
|
// on SignHandler. The Password and Username or Email should be filled.
|
||||||
SigninRequest struct {
|
SigninRequest struct {
|
||||||
Username string `json:"username" form:"username,omitempty"` // username OR email, username has priority over email.
|
Username string `json:"username" form:"username,omitempty"` // username OR email, username has priority over email.
|
||||||
Email string `json:"email" form:"email,omitempty"` // email OR username.
|
Email string `json:"email" form:"email,omitempty"` // email OR username.
|
||||||
Password string `json:"password" form:"password"`
|
Password string `json:"password" form:"password"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SigninResponse is the response body the server sends
|
||||||
|
// to the client on the SignHandler. It contains a pair of the access token
|
||||||
|
// and the refresh token if the refresh jwt token id exists in the configuration.
|
||||||
SigninResponse struct {
|
SigninResponse struct {
|
||||||
AccessToken string `json:"access_token"`
|
AccessToken string `json:"access_token"`
|
||||||
RefreshToken string `json:"refresh_token,omitempty"`
|
RefreshToken string `json:"refresh_token,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RefreshRequest is the request body the server expects
|
||||||
|
// on VerifyHandler to renew an access and refresh token pair.
|
||||||
RefreshRequest struct {
|
RefreshRequest struct {
|
||||||
RefreshToken string `json:"refresh_token"`
|
RefreshToken string `json:"refresh_token"`
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// MustLoad binds a filename (fullpath) configuration yaml or json
|
||||||
|
// and constructs a new Auth instance. It panics on error.
|
||||||
func MustLoad[T User](filename string) *Auth[T] {
|
func MustLoad[T User](filename string) *Auth[T] {
|
||||||
var config Configuration
|
var config Configuration
|
||||||
if err := config.BindFile(filename); err != nil {
|
if err := config.BindFile(filename); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := New[T](config)
|
return Must(New[T](config))
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must is a helper that wraps a call to a function returning (*Auth[T], error)
|
||||||
|
// and panics if the error is non-nil. It is intended for use in variable
|
||||||
|
// initializations such as
|
||||||
|
// var s = auth.Must(auth.New[MyUser](config))
|
||||||
func Must[T User](s *Auth[T], err error) *Auth[T] {
|
func Must[T User](s *Auth[T], err error) *Auth[T] {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -71,6 +107,14 @@ func Must[T User](s *Auth[T], err error) *Auth[T] {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// New initializes a new Auth instance typeof T and returns it.
|
||||||
|
// The T generic can be any custom struct.
|
||||||
|
// It accepts a Configuration value which can be constructed
|
||||||
|
// manually or through a configuration file using the
|
||||||
|
// MustGenerateConfiguration or MustLoadConfiguration
|
||||||
|
// or LoadConfiguration or MustLoad package-level functions.
|
||||||
|
//
|
||||||
|
// Example can be found at: https://github.com/kataras/iris/tree/master/_examples/auth/auth/main.go.
|
||||||
func New[T User](config Configuration) (*Auth[T], error) {
|
func New[T User](config Configuration) (*Auth[T], error) {
|
||||||
keys, err := config.validate()
|
keys, err := config.validate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -121,6 +165,8 @@ func (s *Auth[T]) WithProviderAndErrorHandler(provider Provider[T], errHandler E
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddProvider registers one or more providers to this Auth of T instance and returns itself.
|
||||||
|
// Look the Provider godoc for more.
|
||||||
func (s *Auth[T]) AddProvider(providers ...Provider[T]) *Auth[T] {
|
func (s *Auth[T]) AddProvider(providers ...Provider[T]) *Auth[T] {
|
||||||
// A provider can also implement both transformer and
|
// A provider can also implement both transformer and
|
||||||
// error handler if that's the design option of the end-developer.
|
// error handler if that's the design option of the end-developer.
|
||||||
|
@ -146,21 +192,31 @@ func (s *Auth[T]) AddProvider(providers ...Provider[T]) *Auth[T] {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetErrorHandler sets a custom error handler to this Auth of T instance and returns itself.
|
||||||
|
// Look the Provider and ErrorHandler godoc for more.
|
||||||
func (s *Auth[T]) SetErrorHandler(errHandler ErrorHandler) *Auth[T] {
|
func (s *Auth[T]) SetErrorHandler(errHandler ErrorHandler) *Auth[T] {
|
||||||
s.errorHandler = errHandler
|
s.errorHandler = errHandler
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTransformer sets a custom transformer to this Auth of T instance and returns itself.
|
||||||
|
// Look the Provider and Transformer godoc for more.
|
||||||
func (s *Auth[T]) SetTransformer(transformer Transformer[T]) *Auth[T] {
|
func (s *Auth[T]) SetTransformer(transformer Transformer[T]) *Auth[T] {
|
||||||
s.transformer = transformer
|
s.transformer = transformer
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetTransformerFunc like SetTransformer method but accepts a function instead.
|
||||||
func (s *Auth[T]) SetTransformerFunc(transfermerFunc func(ctx stdContext.Context, tok *VerifiedToken) (T, error)) *Auth[T] {
|
func (s *Auth[T]) SetTransformerFunc(transfermerFunc func(ctx stdContext.Context, tok *VerifiedToken) (T, error)) *Auth[T] {
|
||||||
s.transformer = TransformerFunc[T](transfermerFunc)
|
s.transformer = TransformerFunc[T](transfermerFunc)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signin signs a token based on the provided username and password
|
||||||
|
// and returns a pair of access and refresh tokens.
|
||||||
|
//
|
||||||
|
// Signin calls the Provider.Signin method to check if a user
|
||||||
|
// is authenticated by the given username and password combination.
|
||||||
func (s *Auth[T]) Signin(ctx stdContext.Context, username, password string) ([]byte, []byte, error) {
|
func (s *Auth[T]) Signin(ctx stdContext.Context, username, password string) ([]byte, []byte, error) {
|
||||||
var t T
|
var t T
|
||||||
|
|
||||||
|
@ -292,12 +348,22 @@ func (s *Auth[T]) SigninHandler(ctx *context.Context) {
|
||||||
ctx.JSON(resp)
|
ctx.JSON(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Auth[T]) Verify(ctx stdContext.Context, token []byte) (T, StandardClaims, error) {
|
func (s *Auth[T]) Verify(ctx stdContext.Context, token []byte, verifyFuncs ...VerifyUserFunc[T]) (T, StandardClaims, error) {
|
||||||
t, claims, err := s.verify(ctx, token)
|
t, claims, err := s.verify(ctx, token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return t, StandardClaims{}, fmt.Errorf("auth: verify: %w", err)
|
return t, StandardClaims{}, fmt.Errorf("auth: verify: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, verify := range verifyFuncs {
|
||||||
|
if verify == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = verify(t); err != nil {
|
||||||
|
return t, StandardClaims{}, fmt.Errorf("auth: verify: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return t, claims, nil
|
return t, claims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,7 +414,7 @@ func (s *Auth[T]) verify(ctx stdContext.Context, token []byte) (T, StandardClaim
|
||||||
return t, standardClaims, nil
|
return t, standardClaims, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Auth[T]) VerifyHandler(verifyFuncs ...TVerify[T]) context.Handler {
|
func (s *Auth[T]) VerifyHandler(verifyFuncs ...VerifyUserFunc[T]) context.Handler {
|
||||||
return func(ctx *context.Context) {
|
return func(ctx *context.Context) {
|
||||||
accessToken := s.extractAccessToken(ctx)
|
accessToken := s.extractAccessToken(ctx)
|
||||||
|
|
||||||
|
@ -357,31 +423,18 @@ func (s *Auth[T]) VerifyHandler(verifyFuncs ...TVerify[T]) context.Handler {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
t, claims, err := s.Verify(ctx, []byte(accessToken))
|
t, claims, err := s.Verify(ctx, []byte(accessToken), verifyFuncs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.errorHandler.Unauthenticated(ctx, err)
|
s.errorHandler.Unauthenticated(ctx, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, verify := range verifyFuncs {
|
|
||||||
if verify == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = verify(t); err != nil {
|
|
||||||
err = fmt.Errorf("auth: verify: %v", err)
|
|
||||||
s.errorHandler.Unauthenticated(ctx, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.SetUser(t)
|
ctx.SetUser(t)
|
||||||
|
|
||||||
// store the user to the request.
|
// store the user to the request.
|
||||||
ctx.Values().Set(accessTokenContextKey, accessToken)
|
ctx.Values().Set(accessTokenContextKey, accessToken)
|
||||||
|
|
||||||
ctx.Values().Set(userContextKey, t)
|
|
||||||
ctx.Values().Set(standardClaimsContextKey, claims)
|
ctx.Values().Set(standardClaimsContextKey, claims)
|
||||||
|
ctx.Values().Set(userContextKey, t)
|
||||||
|
|
||||||
ctx.Next()
|
ctx.Next()
|
||||||
}
|
}
|
||||||
|
@ -504,8 +557,8 @@ func (s *Auth[T]) signoutHandler(ctx *context.Context, all bool) {
|
||||||
ctx.SetUser(nil)
|
ctx.SetUser(nil)
|
||||||
|
|
||||||
ctx.Values().Remove(accessTokenContextKey)
|
ctx.Values().Remove(accessTokenContextKey)
|
||||||
ctx.Values().Remove(userContextKey)
|
|
||||||
ctx.Values().Remove(standardClaimsContextKey)
|
ctx.Values().Remove(standardClaimsContextKey)
|
||||||
|
ctx.Values().Remove(userContextKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Auth[T]) extractTokenFromHeader(ctx *context.Context) string {
|
func (s *Auth[T]) extractTokenFromHeader(ctx *context.Context) string {
|
||||||
|
@ -535,9 +588,9 @@ func (s *Auth[T]) trySetCookie(ctx *context.Context, accessToken string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cookie := &http.Cookie{
|
cookie := &http.Cookie{
|
||||||
Path: "/",
|
|
||||||
Name: cookieName,
|
Name: cookieName,
|
||||||
Value: url.QueryEscape(accessToken),
|
Value: url.QueryEscape(accessToken),
|
||||||
|
Path: "/",
|
||||||
HttpOnly: true,
|
HttpOnly: true,
|
||||||
Secure: s.config.Cookie.Secure || ctx.IsSSL(),
|
Secure: s.config.Cookie.Secure || ctx.IsSSL(),
|
||||||
Domain: ctx.Domain(),
|
Domain: ctx.Domain(),
|
||||||
|
@ -546,7 +599,7 @@ func (s *Auth[T]) trySetCookie(ctx *context.Context, accessToken string) {
|
||||||
MaxAge: int(maxAge.Seconds()),
|
MaxAge: int(maxAge.Seconds()),
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.SetCookie(cookie, context.CookieEncoding(s.securecookie))
|
ctx.SetCookie(cookie, context.CookieEncoding(s.securecookie), context.CookieAllowReclaim())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -192,16 +192,8 @@ func MustGenerateConfiguration() (c Configuration) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// LoadConfiguration reads a filename (fullpath)
|
|
||||||
// and returns a Configuration binded to the contents of the given filename.
|
|
||||||
// See Configuration.BindFile method too.
|
|
||||||
func LoadConfiguration(filename string) (c Configuration, err error) {
|
|
||||||
err = c.BindFile(filename)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustLoadConfiguration same as LoadConfiguration package-level function
|
// MustLoadConfiguration same as LoadConfiguration package-level function
|
||||||
// but it panics on errors.
|
// but it panics on error.
|
||||||
func MustLoadConfiguration(filename string) Configuration {
|
func MustLoadConfiguration(filename string) Configuration {
|
||||||
c, err := LoadConfiguration(filename)
|
c, err := LoadConfiguration(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -210,3 +202,11 @@ func MustLoadConfiguration(filename string) Configuration {
|
||||||
|
|
||||||
return c
|
return c
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadConfiguration reads a filename (fullpath)
|
||||||
|
// and returns a Configuration binded to the contents of the given filename.
|
||||||
|
// See Configuration.BindFile method too.
|
||||||
|
func LoadConfiguration(filename string) (c Configuration, err error) {
|
||||||
|
err = c.BindFile(filename)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
|
@ -21,12 +21,17 @@ type VerifiedToken = jwt.VerifiedToken
|
||||||
// A provider can optionally complete the Transformer, ClaimsProvider and
|
// A provider can optionally complete the Transformer, ClaimsProvider and
|
||||||
// ErrorHandler all in once when necessary.
|
// ErrorHandler all in once when necessary.
|
||||||
// Set a provider using the AddProvider method of Auth type.
|
// Set a provider using the AddProvider method of Auth type.
|
||||||
|
//
|
||||||
|
// Example can be found at: https://github.com/kataras/iris/tree/master/_examples/auth/auth/user_provider.go.
|
||||||
type Provider[T User] interface {
|
type Provider[T User] interface {
|
||||||
// Signin accepts a username (or email) and a password and should
|
// Signin accepts a username (or email) and a password and should
|
||||||
// return a valid T value or an error describing
|
// return a valid T value or an error describing
|
||||||
// the user authentication or verification reason of failure.
|
// the user authentication or verification reason of failure.
|
||||||
//
|
//
|
||||||
// It's called on Auth.SigninHandler
|
// The first input argument standard context can be
|
||||||
|
// casted to iris.Context if executed through Auth.SigninHandler.
|
||||||
|
//
|
||||||
|
// It's called on Auth.SigninHandler.
|
||||||
Signin(ctx stdContext.Context, username, password string) (T, error)
|
Signin(ctx stdContext.Context, username, password string) (T, error)
|
||||||
|
|
||||||
// ValidateToken accepts the standard JWT claims and the T value obtained
|
// ValidateToken accepts the standard JWT claims and the T value obtained
|
||||||
|
@ -36,6 +41,9 @@ type Provider[T User] interface {
|
||||||
// the standard claim's (e.g. origin jwt token id).
|
// the standard claim's (e.g. origin jwt token id).
|
||||||
// It can be an empty method too which returns a nil error.
|
// It can be an empty method too which returns a nil error.
|
||||||
//
|
//
|
||||||
|
// The first input argument standard context can be
|
||||||
|
// casted to iris.Context if executed through Auth.VerifyHandler.
|
||||||
|
//
|
||||||
// It's caleld on Auth.VerifyHandler.
|
// It's caleld on Auth.VerifyHandler.
|
||||||
ValidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error
|
ValidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error
|
||||||
|
|
||||||
|
@ -44,12 +52,18 @@ type Provider[T User] interface {
|
||||||
// on a persistence storage and server can decide which token is valid or invalid.
|
// on a persistence storage and server can decide which token is valid or invalid.
|
||||||
// It can be an empty method too which returns a nil error.
|
// It can be an empty method too which returns a nil error.
|
||||||
//
|
//
|
||||||
|
// The first input argument standard context can be
|
||||||
|
// casted to iris.Context if executed through Auth.SignoutHandler.
|
||||||
|
//
|
||||||
// It's called on Auth.SignoutHandler.
|
// It's called on Auth.SignoutHandler.
|
||||||
InvalidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error
|
InvalidateToken(ctx stdContext.Context, standardClaims StandardClaims, t T) error
|
||||||
// InvalidateTokens is like InvalidateToken but it should invalidate
|
// InvalidateTokens is like InvalidateToken but it should invalidate
|
||||||
// all tokens generated for a specific T value.
|
// all tokens generated for a specific T value.
|
||||||
// It can be an empty method too which returns a nil error.
|
// It can be an empty method too which returns a nil error.
|
||||||
//
|
//
|
||||||
|
// The first input argument standard context can be
|
||||||
|
// casted to iris.Context if executed through Auth.SignoutAllHandler.
|
||||||
|
//
|
||||||
// It's called on Auth.SignoutAllHandler.
|
// It's called on Auth.SignoutAllHandler.
|
||||||
InvalidateTokens(ctx stdContext.Context, t T) error
|
InvalidateTokens(ctx stdContext.Context, t T) error
|
||||||
}
|
}
|
||||||
|
@ -70,6 +84,9 @@ type ClaimsProvider interface {
|
||||||
// A transformer is called on Auth.VerifyHandler before Provider.ValidateToken and it can
|
// A transformer is called on Auth.VerifyHandler before Provider.ValidateToken and it can
|
||||||
// be used to modify the T value based on the token's contents. It is mostly used
|
// be used to modify the T value based on the token's contents. It is mostly used
|
||||||
// to convert the json claims to T value manually, when they differ.
|
// to convert the json claims to T value manually, when they differ.
|
||||||
|
//
|
||||||
|
// The first input argument standard context can be
|
||||||
|
// casted to iris.Context if executed through Auth.VerifyHandler.
|
||||||
type Transformer[T User] interface {
|
type Transformer[T User] interface {
|
||||||
Transform(ctx stdContext.Context, tok *VerifiedToken) (T, error)
|
Transform(ctx stdContext.Context, tok *VerifiedToken) (T, error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,9 @@ type (
|
||||||
StandardClaims = jwt.Claims
|
StandardClaims = jwt.Claims
|
||||||
// User is an alias of an empty interface, it's here to declare the typeof T,
|
// User is an alias of an empty interface, it's here to declare the typeof T,
|
||||||
// which can be any custom struct type.
|
// which can be any custom struct type.
|
||||||
User = interface{}
|
//
|
||||||
|
// Example can be found at: https://github.com/kataras/iris/tree/master/_examples/auth/auth/user.go.
|
||||||
|
User = any
|
||||||
)
|
)
|
||||||
|
|
||||||
const accessTokenContextKey = "iris.auth.context.access_token"
|
const accessTokenContextKey = "iris.auth.context.access_token"
|
||||||
|
|
|
@ -3865,9 +3865,10 @@ func WriteJSON(writer io.Writer, v interface{}, options JSON, shouldOptimize boo
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
if !shouldOptimize && options.Indent == "" {
|
// Let's keep it as it is.
|
||||||
options.Indent = " "
|
// if !shouldOptimize && options.Indent == "" {
|
||||||
}
|
// options.Indent = " "
|
||||||
|
// }
|
||||||
|
|
||||||
if indent := options.Indent; indent != "" {
|
if indent := options.Indent; indent != "" {
|
||||||
if shouldOptimize {
|
if shouldOptimize {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user