mirror of
https://github.com/memohai/Memoh.git
synced 2026-04-25 07:00:48 +09:00
97 lines
2.8 KiB
Go
97 lines
2.8 KiB
Go
package auth
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/labstack/echo/v4"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
func TestRefreshTokenFromContext(t *testing.T) {
|
|
e := echo.New()
|
|
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
c := e.NewContext(req, rec)
|
|
|
|
secret := "test-secret"
|
|
userID := "user-123"
|
|
|
|
// Create an initial token with a 5-minute lifespan
|
|
initialDuration := 5 * time.Minute
|
|
initialTokenStr, _, err := GenerateToken(userID, secret, initialDuration)
|
|
assert.NoError(t, err)
|
|
|
|
// Parse the token to place it into the echo context
|
|
token, err := jwt.Parse(initialTokenStr, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(secret), nil
|
|
})
|
|
assert.NoError(t, err)
|
|
c.Set("user", token)
|
|
|
|
// Simulate some time passing to ensure the new token has a different 'iat' and 'exp'
|
|
time.Sleep(1 * time.Second)
|
|
|
|
// Run the refresh function
|
|
defaultDuration := 1 * time.Hour
|
|
newTokenStr, newExpiresAt, err := RefreshTokenFromContext(c, secret, defaultDuration)
|
|
assert.NoError(t, err)
|
|
assert.NotEmpty(t, newTokenStr)
|
|
|
|
// Parse the original token claims for comparison
|
|
originalClaims, ok := token.Claims.(jwt.MapClaims)
|
|
assert.True(t, ok)
|
|
origIat := int64(originalClaims["iat"].(float64))
|
|
|
|
// Parse the new token
|
|
newToken, err := jwt.Parse(newTokenStr, func(token *jwt.Token) (interface{}, error) {
|
|
return []byte(secret), nil
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.True(t, newToken.Valid)
|
|
|
|
newClaims, ok := newToken.Claims.(jwt.MapClaims)
|
|
assert.True(t, ok)
|
|
|
|
// Ensure standard payload claims are retained
|
|
assert.Equal(t, userID, newClaims[claimSubject])
|
|
assert.Equal(t, userID, newClaims[claimUserID])
|
|
|
|
// Validate the new time bounds
|
|
newIat := int64(newClaims["iat"].(float64))
|
|
newExp := int64(newClaims["exp"].(float64))
|
|
|
|
// 1. Ensure time has advanced
|
|
assert.Greater(t, newIat, origIat)
|
|
|
|
// 2. Ensure the refreshed token has a positive lifetime and does not exceed the configured default duration
|
|
lifetimeSeconds := newExp - newIat
|
|
assert.Greater(t, lifetimeSeconds, int64(0))
|
|
assert.LessOrEqual(t, lifetimeSeconds, int64(defaultDuration.Seconds()))
|
|
|
|
// 3. Ensure the return value matches the claim
|
|
assert.Equal(t, newExpiresAt.Unix(), newExp)
|
|
}
|
|
|
|
func TestRefreshTokenFromContext_MissingUser(t *testing.T) {
|
|
e := echo.New()
|
|
req := httptest.NewRequest(http.MethodPost, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
c := e.NewContext(req, rec)
|
|
|
|
secret := "test-secret"
|
|
defaultDuration := 1 * time.Hour
|
|
|
|
// Context without the "user" key
|
|
_, _, err := RefreshTokenFromContext(c, secret, defaultDuration)
|
|
assert.Error(t, err)
|
|
|
|
httpErr, ok := err.(*echo.HTTPError)
|
|
assert.True(t, ok)
|
|
assert.Equal(t, http.StatusUnauthorized, httpErr.Code)
|
|
assert.Equal(t, "invalid token", httpErr.Message)
|
|
}
|