diff --git a/internal/authentication/token.go b/internal/authentication/token.go index df057b8..6edda01 100644 --- a/internal/authentication/token.go +++ b/internal/authentication/token.go @@ -2,15 +2,14 @@ package authentication import ( "crypto/rand" - "log" "math/big" ) var ( - tokenCharacters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") - standardTokenLength = 64 // This length includes the prefix (one character). - compatTokenLength = 15 // This length includes the prefix (one character). - applicationPrefix = "A" + tokenCharacters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") + regularTokenLength = 64 // This length includes the prefix (one character). + compatTokenLength = 15 // This length includes the prefix (one character). + applicationTokenPrefix = "A" ) func randIntn(n int) int { @@ -46,23 +45,13 @@ func generateRandomString(length int) string { return string(res) } -func generateRandomToken(prefix string, compat bool) string { - tokenLength := standardTokenLength +// GenerateApplicationToken generates a token for an application. +func GenerateApplicationToken(compat bool) string { + tokenLength := regularTokenLength if compat { tokenLength = compatTokenLength } - // Although constant at the time of writing, this check should prevent future changes from generating insecure tokens. - randomLength := tokenLength - len(prefix) - if randomLength < 14 { - log.Fatalf("Tokens should have more than %d random characters", randomLength) - } - - return prefix + generateRandomString(randomLength) -} - -// GenerateApplicationToken generates a token for an application. -func GenerateApplicationToken(compat bool) string { - return generateRandomToken(applicationPrefix, compat) + return applicationTokenPrefix + generateRandomString(tokenLength) } diff --git a/internal/authentication/token_test.go b/internal/authentication/token_test.go new file mode 100644 index 0000000..83dc891 --- /dev/null +++ b/internal/authentication/token_test.go @@ -0,0 +1,48 @@ +package authentication + +import ( + "log" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func isGoodToken(assert *assert.Assertions, require *require.Assertions, token string, compat bool) { + prefix := token[0:len(applicationTokenPrefix)] + token = token[len(applicationTokenPrefix):] + + // Although constant at the time of writing, this check should prevent future changes from generating insecure tokens. + if len(token) < 14 { + log.Fatalf("Tokens should have more random characters") + } + + if compat { + assert.Equal(len(token), compatTokenLength, "Unexpected compatibility token length") + } else { + assert.Equal(len(token), regularTokenLength, "Unexpected regular token length") + } + + assert.Equal(prefix, applicationTokenPrefix, "Invalid token prefix") + + for _, c := range []byte(token) { + assert.Contains(tokenCharacters, c, "Unexpected character in token") + } +} + +func TestAuthentication_GenerateApplicationToken(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + for i := 0; i < 64; i++ { + token := GenerateApplicationToken(false) + + isGoodToken(assert, require, token, false) + } + + for i := 0; i < 64; i++ { + token := GenerateApplicationToken(true) + + isGoodToken(assert, require, token, true) + } +}