diff --git a/api/rest/web_api_handler.go b/api/rest/web_api_handler.go index 70c8c84..465cf50 100644 --- a/api/rest/web_api_handler.go +++ b/api/rest/web_api_handler.go @@ -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) } diff --git a/application/application_test.go b/application/application_test.go index 7faec27..bfa7b25 100644 --- a/application/application_test.go +++ b/application/application_test.go @@ -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" @@ -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)) @@ -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) +} diff --git a/data/token.go b/data/token.go index 3baadad..ffceb77 100644 --- a/data/token.go +++ b/data/token.go @@ -1,8 +1,6 @@ package data import ( - "time" - "github.com/google/uuid" "github.com/wissance/Ferrum/utils/jsontools" ) @@ -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"` diff --git a/services/jwt_generator_service.go b/services/jwt_generator_service.go index 68af34a..89f685b 100644 --- a/services/jwt_generator_service.go +++ b/services/jwt_generator_service.go @@ -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) } @@ -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())) } @@ -69,7 +70,7 @@ 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 @@ -77,11 +78,14 @@ func (generator *JwtGenerator) generateJwtRefreshToken(tokenData *data.TokenRefr // 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 } @@ -89,9 +93,11 @@ func (generator *JwtGenerator) prepareAccessToken(realmBaseUrl string, tokenType // 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 }