From 47dd6f8ee0ad44ac53262c52934f3c6fe39b904a Mon Sep 17 00:00:00 2001 From: Cronus <105345303+ice-cronus@users.noreply.github.com> Date: Thu, 18 Jul 2024 17:06:03 +0700 Subject: [PATCH] option to add extra dynamic fields to ice token, expose them in Claims (#54) --- auth/auth.go | 8 ++++++-- auth/auth_test.go | 3 ++- auth/contract.go | 2 +- auth/fixture/fixture.go | 2 +- auth/internal/ice/auth.go | 11 ++++++++--- auth/internal/ice/contract.go | 13 +++++++------ auth/internal/ice/tokens_generate.go | 11 ++++++++--- go.mod | 6 +++--- go.sum | 12 ++++++------ 9 files changed, 42 insertions(+), 26 deletions(-) diff --git a/auth/auth.go b/auth/auth.go index eff2438..bd57ebd 100644 --- a/auth/auth.go +++ b/auth/auth.go @@ -128,9 +128,13 @@ func (a *auth) GetUserUIDByEmail(ctx context.Context, email string) (string, err } func (a *auth) GenerateTokens( //nolint:revive // We need to have these parameters. - now *time.Time, userID, deviceUniqueID, email string, hashCode, seq int64, role string, + now *time.Time, userID, deviceUniqueID, email string, hashCode, seq int64, role string, extras ...map[string]any, ) (accessToken, refreshToken string, err error) { - accessToken, refreshToken, err = a.ice.GenerateTokens(now, userID, deviceUniqueID, email, hashCode, seq, role) + var extra map[string]any + if len(extras) > 0 { + extra = extras[0] + } + accessToken, refreshToken, err = a.ice.GenerateTokens(now, userID, deviceUniqueID, email, hashCode, seq, role, extra) err = errors.Wrapf(err, "can't generate tokens for userID:%v, email:%v", userID, email) return diff --git a/auth/auth_test.go b/auth/auth_test.go index 7cb3a8e..2345122 100644 --- a/auth/auth_test.go +++ b/auth/auth_test.go @@ -156,7 +156,7 @@ func TestGenerateIceTokens_Valid(t *testing.T) { deviceID = "00000000-0000-0000-0000-000000000001" role = "author" ) - refreshToken, accessToken, err := client.GenerateTokens(now, userID, deviceID, email, hashCode, seq, role) + refreshToken, accessToken, err := client.GenerateTokens(now, userID, deviceID, email, hashCode, seq, role, map[string]any{"extra": "extra"}) require.NoError(t, err) assert.NotEmpty(t, refreshToken) assert.NotEmpty(t, accessToken) @@ -170,6 +170,7 @@ func TestGenerateIceTokens_Valid(t *testing.T) { assert.Equal(t, seq, verifiedAccessToken.Claims["seq"]) assert.Equal(t, role, verifiedAccessToken.Claims["role"]) assert.Equal(t, deviceID, verifiedAccessToken.Claims["deviceUniqueID"]) + assert.Equal(t, "extra", verifiedAccessToken.Claims["extra"]) _, err = client.VerifyToken(ctx, refreshToken) require.ErrorIs(t, err, ErrWrongTypeToken) } diff --git a/auth/contract.go b/auth/contract.go index 55ad3cd..59595ee 100644 --- a/auth/contract.go +++ b/auth/contract.go @@ -40,7 +40,7 @@ type ( UpdateCustomClaims(ctx context.Context, userID string, customClaims map[string]any) error DeleteUser(ctx context.Context, userID string) error UpdateEmail(ctx context.Context, userID, email string) error - GenerateTokens(now *time.Time, userID, deviceUniqueID, email string, hashCode, seq int64, role string) (accessToken, refreshToken string, err error) + GenerateTokens(now *time.Time, userID, deviceUniqueID, email string, hashCode, seq int64, role string, extras ...map[string]any) (accessToken, refreshToken string, err error) //nolint:lll // . GenerateMetadata(now *time.Time, userID string, md map[string]any) (string, error) ModifyTokenWithMetadata(token *Token, metadataStr string) (*Token, error) GetUserUIDByEmail(ctx context.Context, email string) (string, error) diff --git a/auth/fixture/fixture.go b/auth/fixture/fixture.go index d2bb4b6..eb978c7 100644 --- a/auth/fixture/fixture.go +++ b/auth/fixture/fixture.go @@ -100,7 +100,7 @@ func GenerateIceTokens(userID, role string) (refreshToken, accessToken string, e seq = int64(0) hashCode = int64(0) ) - refreshToken, accessToken, err = clientIce().GenerateTokens(now, userID, deviceUniqueID, email, hashCode, seq, role) + refreshToken, accessToken, err = clientIce().GenerateTokens(now, userID, deviceUniqueID, email, hashCode, seq, role, nil) return } diff --git a/auth/internal/ice/auth.go b/auth/internal/ice/auth.go index e490b02..6437392 100644 --- a/auth/internal/ice/auth.go +++ b/auth/internal/ice/auth.go @@ -35,8 +35,6 @@ func (a *auth) VerifyToken(token string) (*internal.Token, error) { if iceToken.Issuer != internal.AccessJwtIssuer { return nil, errors.Wrapf(ErrWrongTypeToken, "access to endpoint with refresh token: %v", iceToken.Issuer) } - userID := iceToken.Subject - tok := &internal.Token{ Claims: map[string]any{ "email": iceToken.Email, @@ -45,11 +43,18 @@ func (a *auth) VerifyToken(token string) (*internal.Token, error) { "hashCode": iceToken.HashCode, "deviceUniqueID": iceToken.DeviceUniqueID, }, - UserID: userID, + UserID: iceToken.Subject, Email: iceToken.Email, Role: iceToken.Role, Provider: internal.ProviderIce, } + if len(iceToken.Claims) > 0 { + for k, v := range iceToken.Claims { + if _, alreadyPresented := tok.Claims[k]; !alreadyPresented { + tok.Claims[k] = v + } + } + } return tok, nil } diff --git a/auth/internal/ice/contract.go b/auth/internal/ice/contract.go index af27c6e..1554dfc 100644 --- a/auth/internal/ice/contract.go +++ b/auth/internal/ice/contract.go @@ -23,18 +23,19 @@ var ( type ( Client interface { VerifyToken(token string) (*internal.Token, error) - GenerateTokens(now *time.Time, userID, deviceUniqueID, email string, hashCode, seq int64, role string) (accessToken, refreshToken string, err error) + GenerateTokens(now *time.Time, userID, deviceID, email string, hashCode, seq int64, role string, extra map[string]any) (access, refresh string, err error) VerifyTokenFields(token string, res jwt.Claims) error GenerateMetadata(now *time.Time, tokenID string, metadata map[string]any) (string, error) } Token struct { *jwt.RegisteredClaims - Role string `json:"role" example:"1"` - Email string `json:"email" example:"jdoe@example.com"` - DeviceUniqueID string `json:"deviceUniqueId" example:"6FB988F3-36F4-433D-9C7C-555887E57EB2"` - HashCode int64 `json:"hashCode,omitempty" example:"12356789"` - Seq int64 `json:"seq" example:"1"` + Claims map[string]any `json:"claims,omitempty"` + Role string `json:"role" example:"1"` + Email string `json:"email" example:"jdoe@example.com"` + DeviceUniqueID string `json:"deviceUniqueId" example:"6FB988F3-36F4-433D-9C7C-555887E57EB2"` + HashCode int64 `json:"hashCode,omitempty" example:"12356789"` + Seq int64 `json:"seq" example:"1"` } ) diff --git a/auth/internal/ice/tokens_generate.go b/auth/internal/ice/tokens_generate.go index 228e325..f063943 100644 --- a/auth/internal/ice/tokens_generate.go +++ b/auth/internal/ice/tokens_generate.go @@ -17,17 +17,19 @@ func (a *auth) GenerateTokens( hashCode, seq int64, role string, + extra map[string]any, ) (refreshToken, accessToken string, err error) { - refreshToken, err = a.generateRefreshToken(now, userID, deviceUniqueID, email, seq) + refreshToken, err = a.generateRefreshToken(now, userID, deviceUniqueID, email, seq, extra) if err != nil { return "", "", errors.Wrapf(err, "failed to generate jwt refreshToken for userID:%v", userID) } - accessToken, err = a.generateAccessToken(now, seq, hashCode, userID, deviceUniqueID, email, role) + accessToken, err = a.generateAccessToken(now, seq, hashCode, userID, deviceUniqueID, email, role, extra) return refreshToken, accessToken, errors.Wrapf(err, "failed to generate jwt accessToken for userID:%v", userID) } -func (a *auth) generateRefreshToken(now *time.Time, userID, deviceUniqueID, email string, seq int64) (string, error) { +//nolint:revive // . +func (a *auth) generateRefreshToken(now *time.Time, userID, deviceUniqueID, email string, seq int64, extra map[string]any) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, Token{ RegisteredClaims: &jwt.RegisteredClaims{ Issuer: internal.RefreshJwtIssuer, @@ -39,6 +41,7 @@ func (a *auth) generateRefreshToken(now *time.Time, userID, deviceUniqueID, emai Email: email, Seq: seq, DeviceUniqueID: deviceUniqueID, + Claims: extra, }) refreshToken, err := a.signToken(token) @@ -50,6 +53,7 @@ func (a *auth) generateAccessToken( now *time.Time, refreshTokenSeq, hashCode int64, userID, deviceUniqueID, email string, role string, + extra map[string]any, ) (string, error) { token := jwt.NewWithClaims(jwt.SigningMethodHS256, Token{ RegisteredClaims: &jwt.RegisteredClaims{ @@ -64,6 +68,7 @@ func (a *auth) generateAccessToken( DeviceUniqueID: deviceUniqueID, HashCode: hashCode, Seq: refreshTokenSeq, + Claims: extra, }) tokenStr, err := a.signToken(token) diff --git a/go.mod b/go.mod index 8713ddd..42c5e74 100644 --- a/go.mod +++ b/go.mod @@ -53,7 +53,7 @@ require ( github.com/KyleBanks/depth v1.2.1 // indirect github.com/MicahParks/keyfunc v1.9.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect - github.com/Microsoft/hcsshim v0.12.4 // indirect + github.com/Microsoft/hcsshim v0.12.5 // indirect github.com/andybalholm/brotli v1.1.0 // indirect github.com/bytedance/sonic v1.11.9 // indirect github.com/bytedance/sonic/loader v0.1.1 // indirect @@ -113,7 +113,7 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/sys/mount v0.3.3 // indirect - github.com/moby/sys/mountinfo v0.7.1 // indirect + github.com/moby/sys/mountinfo v0.7.2 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -151,7 +151,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/arch v0.8.0 // indirect golang.org/x/crypto v0.25.0 // indirect - golang.org/x/exp v0.0.0-20240707233637-46b078467d37 // indirect + golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7 // indirect golang.org/x/mod v0.19.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.7.0 // indirect diff --git a/go.sum b/go.sum index de3df7b..86cbee8 100644 --- a/go.sum +++ b/go.sum @@ -32,8 +32,8 @@ github.com/MicahParks/keyfunc v1.9.0 h1:lhKd5xrFHLNOWrDc4Tyb/Q1AJ4LCzQ48GVJyVIID github.com/MicahParks/keyfunc v1.9.0/go.mod h1:IdnCilugA0O/99dW+/MkvlyrsX8+L8+x95xuVNtM5jw= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.12.4 h1:Ev7YUMHAHoWNm+aDSPzc5W9s6E2jyL1szpVDJeZ/Rr4= -github.com/Microsoft/hcsshim v0.12.4/go.mod h1:Iyl1WVpZzr+UkzjekHZbV8o5Z9ZkxNGx6CtY2Qg/JVQ= +github.com/Microsoft/hcsshim v0.12.5 h1:bpTInLlDy/nDRWFVcefDZZ1+U8tS+rz3MxjKgu9boo0= +github.com/Microsoft/hcsshim v0.12.5/go.mod h1:tIUGego4G1EN5Hb6KC90aDYiUI2dqLSTTOCjVNpOgZ8= github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= @@ -266,8 +266,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/moby/sys/mount v0.3.3 h1:fX1SVkXFJ47XWDoeFW4Sq7PdQJnV2QIDZAqjNqgEjUs= github.com/moby/sys/mount v0.3.3/go.mod h1:PBaEorSNTLG5t/+4EgukEQVlAvVEc6ZjTySwKdqp5K0= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g= -github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -406,8 +406,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240707233637-46b078467d37 h1:uLDX+AfeFCct3a2C7uIWBKMJIR3CJMhcgfrUAqjRK6w= -golang.org/x/exp v0.0.0-20240707233637-46b078467d37/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7 h1:wDLEX9a7YQoKdKNQt88rtydkqDxeGaBUTnIYc3iG/mA= +golang.org/x/exp v0.0.0-20240716175740-e3f259677ff7/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=