🏷️ Created Code domain

This commit is contained in:
Maxim Lebedev 2021-12-26 15:02:23 +05:00
parent b43a4eed4c
commit f8b2b42772
Signed by: toby3d
GPG Key ID: 1F14E25B7C119FC5
2 changed files with 156 additions and 0 deletions

63
internal/domain/code.go Normal file
View File

@ -0,0 +1,63 @@
//nolint: gosec
package domain
import (
"encoding/base64"
"testing"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/require"
"source.toby3d.me/website/oauth/internal/random"
)
// Code describes the PKCE challenge to validate the security of the request.
type Code struct {
Method CodeChallengeMethod
Verifier string
Challenge string
}
const (
CodeLengthMin int = 43
CodeLengthMax int = 128
)
// TestCode returns valid random generated PKCE code for tests.
func TestCode(tb testing.TB) *Code {
tb.Helper()
verifier, err := random.String(
gofakeit.Number(CodeLengthMin, CodeLengthMax), random.Alphanumeric, "-", ".", "_", "~",
)
require.NoError(tb, err)
h := CodeChallengeMethodS256.hash
h.Reset()
_, err = h.Write([]byte(verifier))
require.NoError(tb, err)
return &Code{
Method: CodeChallengeMethodS256,
Verifier: verifier,
Challenge: base64.RawURLEncoding.EncodeToString(h.Sum(nil)),
}
}
// IsValid returns true if code challenge is equal to the generated hash from
// the verifier.
func (c Code) IsValid() bool {
if c.Method == CodeChallengeMethodUndefined {
return false
}
if c.Method == CodeChallengeMethodPLAIN {
return c.Challenge == c.Verifier
}
h := c.Method.hash
h.Reset()
return c.Challenge == base64.RawURLEncoding.EncodeToString(h.Sum([]byte(c.Verifier)))
}

View File

@ -0,0 +1,93 @@
package domain_test
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"hash"
"testing"
"github.com/brianvoe/gofakeit/v6"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"source.toby3d.me/website/oauth/internal/domain"
"source.toby3d.me/website/oauth/internal/random"
)
//nolint: funlen
func TestCodeIsValid(t *testing.T) {
t.Parallel()
verifier, err := random.String(gofakeit.Number(domain.CodeLengthMin, domain.CodeLengthMax))
require.NoError(t, err)
for _, testCase := range []struct {
hash hash.Hash
name string
method string
isValid bool
}{{
name: "invalid",
method: domain.CodeChallengeMethodS256.String(),
hash: md5.New(),
isValid: false,
}, {
name: "MD5",
method: domain.CodeChallengeMethodMD5.String(),
hash: md5.New(),
isValid: true,
}, {
name: "plain",
method: domain.CodeChallengeMethodPLAIN.String(),
hash: nil,
isValid: true,
}, {
name: "S1",
method: domain.CodeChallengeMethodS1.String(),
hash: sha1.New(),
isValid: true,
}, {
name: "S256",
method: domain.CodeChallengeMethodS256.String(),
hash: sha256.New(),
isValid: true,
}, {
name: "S512",
method: domain.CodeChallengeMethodS512.String(),
hash: sha512.New(),
isValid: true,
}, {
name: "undefined",
method: "und",
hash: nil,
isValid: false,
}} {
testCase := testCase
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
method, _ := domain.ParseCodeChallengeMethod(testCase.method)
result := &domain.Code{
Method: method,
Verifier: verifier,
Challenge: verifier,
}
if method == domain.CodeChallengeMethodPLAIN ||
method == domain.CodeChallengeMethodUndefined {
assert.Equal(t, testCase.isValid, result.IsValid())
return
}
result.Challenge = base64.RawURLEncoding.EncodeToString(
testCase.hash.Sum([]byte(result.Verifier)),
)
assert.Equal(t, testCase.isValid, result.IsValid())
})
}
}