mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-08 10:49:06 +02:00
90 lines
3.1 KiB
Go
90 lines
3.1 KiB
Go
package auditlogs
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/lestrrat-go/jwx/v4/jwk"
|
|
"github.com/lestrrat-go/jwx/v4/jwt"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
jwkutils "github.com/pocket-id/pocket-id/backend/internal/utils/jwk"
|
|
)
|
|
|
|
type browserTokenTestSigner struct{ key jwk.Key }
|
|
|
|
func (s browserTokenTestSigner) SignSessionToken(token jwt.Token) (string, error) {
|
|
signed, err := jwt.Sign(token, jwt.WithKey(jwkutils.SessionKeyAlg(), s.key))
|
|
return string(signed), err
|
|
}
|
|
|
|
func (s browserTokenTestSigner) VerifySessionToken(token string, options ...jwt.ValidateOption) (jwt.Token, error) {
|
|
parseOptions := []jwt.ParseOption{jwt.WithKey(jwkutils.SessionKeyAlg(), s.key), jwt.WithValidate(true)}
|
|
for _, option := range options {
|
|
parseOptions = append(parseOptions, option)
|
|
}
|
|
return jwt.ParseString(token, parseOptions...)
|
|
}
|
|
|
|
func newBrowserTokenService(t *testing.T) *browserTokens {
|
|
t.Helper()
|
|
key, err := jwkutils.GenerateSessionKey()
|
|
require.NoError(t, err)
|
|
return &browserTokens{signer: browserTokenTestSigner{key: key}, appURL: "https://test.example.com"}
|
|
}
|
|
|
|
func TestKnownBrowserToken(t *testing.T) {
|
|
s := newBrowserTokenService(t)
|
|
const lifetime = 180 * 24 * time.Hour
|
|
encoded, err := s.generate("user", lifetime)
|
|
require.NoError(t, err)
|
|
require.NoError(t, s.verify(encoded, "user"))
|
|
require.Error(t, s.verify(encoded, "other-user"))
|
|
require.Error(t, s.verify(encoded, ""))
|
|
|
|
token, err := jwt.ParseString(encoded, jwt.WithKey(jwkutils.SessionKeyAlg(), s.signer.(browserTokenTestSigner).key))
|
|
require.NoError(t, err)
|
|
issued, ok := token.IssuedAt()
|
|
require.True(t, ok)
|
|
expires, ok := token.Expiration()
|
|
require.True(t, ok)
|
|
require.Equal(t, lifetime, expires.Sub(issued))
|
|
}
|
|
|
|
func TestKnownBrowserTokenRejectsInvalidClaims(t *testing.T) {
|
|
s := newBrowserTokenService(t)
|
|
for _, tt := range []struct {
|
|
name string
|
|
claim string
|
|
value any
|
|
}{
|
|
{"expired", jwt.ExpirationKey, time.Now().Add(-time.Second)},
|
|
{"missing expiry", jwt.ExpirationKey, nil},
|
|
{"missing issued at", jwt.IssuedAtKey, nil},
|
|
{"future issued at", jwt.IssuedAtKey, time.Now().Add(time.Hour)},
|
|
{"wrong issuer", jwt.IssuerKey, "https://other.example.com"},
|
|
{"missing issuer", jwt.IssuerKey, nil},
|
|
{"wrong audience", jwt.AudienceKey, []string{"other"}},
|
|
{"missing audience", jwt.AudienceKey, nil},
|
|
{"missing user", jwt.SubjectKey, nil},
|
|
{"wrong purpose", "type", "access-token"},
|
|
{"missing purpose", "type", nil},
|
|
} {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
// Sign invalid claims with the real key so validation, rather than signature failure, rejects them
|
|
token, err := jwt.NewBuilder().Subject("user").Issuer(s.appURL).
|
|
Audience([]string{s.appURL}).IssuedAt(time.Now()).Expiration(time.Now().Add(time.Hour)).
|
|
Claim("type", knownBrowserJWTType).Build()
|
|
require.NoError(t, err)
|
|
if tt.value == nil {
|
|
require.NoError(t, token.Remove(tt.claim))
|
|
} else {
|
|
require.NoError(t, token.Set(tt.claim, tt.value))
|
|
}
|
|
encoded, err := jwt.Sign(token, jwt.WithKey(jwkutils.SessionKeyAlg(), s.signer.(browserTokenTestSigner).key))
|
|
require.NoError(t, err)
|
|
require.Error(t, s.verify(string(encoded), "user"))
|
|
})
|
|
}
|
|
}
|