Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix exp field in access token and introspect results (#80) #81

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/rest/web_api_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ func (wCtx *WebApiContext) Introspect(respWriter http.ResponseWriter, request *h
result := dto.IntrospectTokenResult{
Active: active,
Type: authTokenType,
Exp: realmPtr.TokenExpiration,
Exp: int(session.Expired.Unix()),
}
afterHandle(&respWriter, status, &result)
}
Expand Down
18 changes: 18 additions & 0 deletions application/application_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"testing"
"time"

"github.com/golang-jwt/jwt/v4"
"github.com/stretchr/testify/assert"
"github.com/wissance/Ferrum/config"
"github.com/wissance/Ferrum/data"
Expand Down Expand Up @@ -108,6 +109,7 @@ func testRunCommonTestCycleImpl(t *testing.T, appConfig *config.AppConfig, baseU
// 2. Introspect valid token
// todo(UMV): add Introspect result check
tokenIntResult := checkIntrospectToken(t, baseUrl, realm, token.AccessToken, testClient1, testClient1Secret, "200 OK")
compareTokenClaimsAndIntrospect(t, token.AccessToken, tokenIntResult)
active, ok := tokenIntResult["active"]
assert.True(t, ok)
assert.True(t, active.(bool))
Expand Down Expand Up @@ -246,3 +248,19 @@ func checkIntrospectToken(t *testing.T, baseUrl string, realm string, token stri
assert.Nil(t, err)
return result
}

func compareTokenClaimsAndIntrospect(t *testing.T, accessToken string, introspectResult map[string]interface{}) {
t.Helper()

mapClaims := &jwt.MapClaims{}
parser := jwt.NewParser()
_, _, err := parser.ParseUnverified(accessToken, mapClaims)
assert.NoError(t, err)
tokenExp, ok := (map[string]interface{})(*mapClaims)["exp"]
assert.True(t, ok)

introspectExp, ok := introspectResult["exp"]
assert.True(t, ok)

assert.EqualValues(t, tokenExp, introspectExp)
}
6 changes: 2 additions & 4 deletions data/token.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package data

import (
"time"

"github.com/google/uuid"
"github.com/wissance/Ferrum/utils/jsontools"
)
Expand All @@ -12,8 +10,8 @@ type RawUserInfo interface{}

// JwtCommonInfo - struct with all field for representing token in JWT format
type JwtCommonInfo struct {
IssuedAt time.Time `json:"iat"`
ExpiredAt time.Time `json:"exp"`
IssuedAt int64 `json:"iat"`
ExpiredAt int64 `json:"exp"`
JwtId uuid.UUID `json:"jti"`
Type string `json:"typ"`
Issuer string `json:"iss"`
Expand Down
28 changes: 17 additions & 11 deletions services/jwt_generator_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ type JwtGenerator struct {
* Returns: JWT-encoded string with access token
*/
func (generator *JwtGenerator) GenerateJwtAccessToken(realmBaseUrl string, tokenType string, scope string, sessionData *data.UserSession,
userData data.User) string {
userData data.User,
) string {
accessToken := generator.prepareAccessToken(realmBaseUrl, tokenType, scope, sessionData, userData)
return generator.generateJwtAccessToken(accessToken)
}
Expand All @@ -55,9 +56,9 @@ func (generator *JwtGenerator) generateJwtAccessToken(tokenData *data.AccessToke
token := jwt.New(jwt.SigningMethodHS256)
// signed token contains embedded type because we don't actually know type of User, therefore we do it like jwt do but use RawStr
signedToken, err := generator.makeSignedToken(token, tokenData, generator.SignKey)
//token.SignedString([]byte("secureSecretText"))
// token.SignedString([]byte("secureSecretText"))
if err != nil {
//todo(UMV): think what to do on Error
// todo(UMV): think what to do on Error
generator.Logger.Error(stringFormatter.Format("An error occurred during signed Jwt Access Token Generation: {0}", err.Error()))
}

Expand All @@ -69,29 +70,34 @@ func (generator *JwtGenerator) generateJwtRefreshToken(tokenData *data.TokenRefr
token := jwt.NewWithClaims(jwt.SigningMethodHS256, tokenData)
signedToken, err := token.SignedString(generator.SignKey)
if err != nil {
//todo(UMV): think what to do on Error
// todo(UMV): think what to do on Error
generator.Logger.Error(stringFormatter.Format("An error occurred during signed Jwt Refresh Token Generation: {0}", err.Error()))
}
return signedToken
}

// prepareAccessToken builds data.AccessTokenData from a lot of params
func (generator *JwtGenerator) prepareAccessToken(realmBaseUrl string, tokenType string, scope string, sessionData *data.UserSession,
userData data.User) *data.AccessTokenData {
userData data.User,
) *data.AccessTokenData {
issuer := realmBaseUrl
jwtCommon := data.JwtCommonInfo{Issuer: issuer, Type: tokenType, Audience: "account", Scope: scope, JwtId: uuid.New(),
IssuedAt: sessionData.Started, ExpiredAt: sessionData.Expired, Subject: sessionData.UserId,
SessionId: sessionData.Id, SessionState: sessionData.Id}
jwtCommon := data.JwtCommonInfo{
Issuer: issuer, Type: tokenType, Audience: "account", Scope: scope, JwtId: uuid.New(),
IssuedAt: sessionData.Started.Unix(), ExpiredAt: sessionData.Expired.Unix(), Subject: sessionData.UserId,
SessionId: sessionData.Id, SessionState: sessionData.Id,
}
accessToken := data.CreateAccessToken(&jwtCommon, userData)
return accessToken
}

// prepareRefreshToken builds data.TokenRefreshData from a lot of params
func (generator *JwtGenerator) prepareRefreshToken(realmBaseUrl string, tokenType string, scope string, sessionData *data.UserSession) *data.TokenRefreshData {
issuer := realmBaseUrl
jwtCommon := data.JwtCommonInfo{Issuer: issuer, Type: tokenType, Audience: issuer, Scope: scope, JwtId: uuid.New(),
IssuedAt: sessionData.Started, ExpiredAt: sessionData.Expired, Subject: sessionData.UserId,
SessionId: sessionData.Id, SessionState: sessionData.Id}
jwtCommon := data.JwtCommonInfo{
Issuer: issuer, Type: tokenType, Audience: issuer, Scope: scope, JwtId: uuid.New(),
IssuedAt: sessionData.Started.Unix(), ExpiredAt: sessionData.Expired.Unix(), Subject: sessionData.UserId,
SessionId: sessionData.Id, SessionState: sessionData.Id,
}
accessToken := data.CreateRefreshToken(&jwtCommon)
return accessToken
}
Expand Down