All checks were successful
Server Check Build (NixCN CMS) TeamCity build finished
Signed-off-by: Asai Neko <sugar@sne.moe>
208 lines
4.7 KiB
Go
208 lines
4.7 KiB
Go
package authtoken
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/spf13/viper"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"nixcn-cms/data"
|
|
"nixcn-cms/testutil"
|
|
)
|
|
|
|
func makeToken() Token {
|
|
return Token{Application: "test-app"}
|
|
}
|
|
|
|
// seedClient persists a client row whose encrypted secret decrypts to the
|
|
// plaintext given by testutil.TestClientSecret.
|
|
func seedTestClient(t *testing.T) {
|
|
t.Helper()
|
|
testutil.SeedClient(t)
|
|
}
|
|
|
|
func TestGenerateRefreshToken(t *testing.T) {
|
|
tok := makeToken()
|
|
r1, err := tok.GenerateRefreshToken()
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, r1)
|
|
|
|
r2, err := tok.GenerateRefreshToken()
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, r1, r2, "refresh tokens must be unique")
|
|
}
|
|
|
|
func TestIssueTokens(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
access, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, access)
|
|
assert.NotEmpty(t, refresh)
|
|
}
|
|
|
|
func TestRefreshAccessToken(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
newAccess, err := tok.RefreshAccessToken(ctx, refresh)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, newAccess)
|
|
}
|
|
|
|
func TestRefreshAccessTokenInvalid(t *testing.T) {
|
|
testutil.Setup(t)
|
|
|
|
ctx := context.Background()
|
|
tok := makeToken()
|
|
|
|
_, err := tok.RefreshAccessToken(ctx, "invalid-refresh-token")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestRenewRefreshToken(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
_, old, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
newRefresh, err := tok.RenewRefreshToken(ctx, old)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, newRefresh)
|
|
assert.NotEqual(t, old, newRefresh)
|
|
|
|
// Old token should no longer be valid
|
|
_, err = tok.RefreshAccessToken(ctx, old)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestRevokeRefreshToken(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, tok.RevokeRefreshToken(ctx, refresh))
|
|
|
|
_, err = tok.RefreshAccessToken(ctx, refresh)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestRevokeNonExistentTokenNoError(t *testing.T) {
|
|
testutil.Setup(t)
|
|
|
|
ctx := context.Background()
|
|
tok := makeToken()
|
|
// should not fail even if token does not exist
|
|
assert.NoError(t, tok.RevokeRefreshToken(ctx, "does-not-exist"))
|
|
}
|
|
|
|
func TestHeaderVerifyValid(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
access, _, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
uid, err := tok.HeaderVerify(ctx, "Bearer "+access)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, userId.String(), uid)
|
|
}
|
|
|
|
func TestHeaderVerifyEmpty(t *testing.T) {
|
|
testutil.Setup(t)
|
|
|
|
ctx := context.Background()
|
|
tok := makeToken()
|
|
|
|
uid, err := tok.HeaderVerify(ctx, "")
|
|
require.Error(t, err)
|
|
assert.Empty(t, uid)
|
|
}
|
|
|
|
func TestHeaderVerifyMalformed(t *testing.T) {
|
|
testutil.Setup(t)
|
|
|
|
ctx := context.Background()
|
|
tok := makeToken()
|
|
|
|
_, err := tok.HeaderVerify(ctx, "NotBearer token")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestHeaderVerifyExpiredToken(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
// Use a very short access TTL
|
|
viper.Set("ttl.access_ttl", -1*time.Second)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
access, _, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
_, err = tok.HeaderVerify(ctx, "Bearer "+access)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestNewClaims(t *testing.T) {
|
|
tok := makeToken()
|
|
userId := uuid.New()
|
|
claims := tok.NewClaims(testutil.TestClientID, userId)
|
|
|
|
assert.Equal(t, testutil.TestClientID, claims.ClientId)
|
|
assert.Equal(t, userId, claims.UserID)
|
|
assert.Equal(t, "test-app", claims.Issuer)
|
|
}
|
|
|
|
// Verify that IssueTokens registers the refresh token in the user and
|
|
// client index sets in Redis.
|
|
func TestIssueTokensRedisIndex(t *testing.T) {
|
|
testutil.Setup(t)
|
|
seedTestClient(t)
|
|
|
|
ctx := context.Background()
|
|
userId := uuid.New()
|
|
tok := makeToken()
|
|
|
|
_, refresh, err := tok.IssueTokens(ctx, testutil.TestClientID, userId)
|
|
require.NoError(t, err)
|
|
|
|
userSetKey := "user:" + userId.String() + ":refresh_tokens"
|
|
isMember, err := data.Redis.SIsMember(ctx, userSetKey, refresh).Result()
|
|
require.NoError(t, err)
|
|
assert.True(t, isMember)
|
|
}
|