Improve test coverage for `wireDPOP01Validate`

pull/1673/head
Herman Slatman 5 months ago
parent a24b2a5c84
commit 7520736f5b
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -484,8 +484,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
var dpopPayload wireDpopPayload
if err := json.Unmarshal(payload, &dpopPayload); err != nil {
return storeError(ctx, db, ch, false, WrapError(ErrorRejectedIdentifierType, err,
"error unmarshalling Wire challenge payload"))
return WrapError(ErrorMalformedType, err, "error unmarshalling Wire challenge payload")
}
wireID, err := wire.ParseID([]byte(ch.Value))

@ -31,7 +31,6 @@ import (
"time"
"github.com/fxamacker/cbor/v2"
"github.com/smallstep/certificates/acme/wire"
"github.com/smallstep/certificates/authority/config"
"github.com/smallstep/certificates/authority/provisioner"
wireprovisioner "github.com/smallstep/certificates/authority/provisioner/wire"
@ -1047,7 +1046,6 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
APIVersion: 5,
Scope: "wire_client_id",
})
require.NoError(t, err)
signed, err := signer.Sign(tokenBytes)
require.NoError(t, err)
@ -4615,190 +4613,3 @@ func createSubjectAltNameExtension(dnsNames, emailAddresses x509util.MultiString
Value: rawBytes,
}, nil
}
func Test_parseAndVerifyWireAccessToken(t *testing.T) {
key := `
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
-----END PUBLIC KEY-----`
publicKey, err := pemutil.Parse([]byte(key))
require.NoError(t, err)
issuer := "http://wire.com:19983/clients/7a41cf5b79683410/access-token"
wireID := wire.ID{
ClientID: "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com",
Handle: "wireapp://%40alice_wire@wire.com",
}
token := `eyJhbGciOiJFZERTQSIsInR5cCI6ImF0K2p3dCIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IkIySVlxQldYQW91RHQzV2NDWmdDTTN0OWd1bU1FS01sZ01zR2VuU3UtZkEifX0.eyJpYXQiOjE3MDQ5ODUyMDUsImV4cCI6MTcwNDk4OTE2NSwibmJmIjoxNzA0OTg1MjA1LCJpc3MiOiJodHRwOi8vd2lyZS5jb206MTk5ODMvY2xpZW50cy83YTQxY2Y1Yjc5NjgzNDEwL2FjY2Vzcy10b2tlbiIsInN1YiI6IndpcmVhcHA6Ly9ndVZYNXhlRlMzZVRhdG1YQkl5QTRBITdhNDFjZjViNzk2ODM0MTBAd2lyZS5jb20iLCJhdWQiOiJodHRwOi8vd2lyZS5jb206MTk5ODMvY2xpZW50cy83YTQxY2Y1Yjc5NjgzNDEwL2FjY2Vzcy10b2tlbiIsImp0aSI6IjQyYzQ2ZDRjLWU1MTAtNDE3NS05ZmI1LWQwNTVlMTI1YTQ5ZCIsIm5vbmNlIjoiVUVKeVIyZHFPRWh6WkZKRVlXSkJhVGt5T0RORVlURTJhRXMwZEhJeGNFYyIsImNoYWwiOiJiWFVHTnBVZmNSeDNFaEIzNHhQM3k2MmFRWm9HWlM2aiIsImNuZiI6eyJraWQiOiJvTVdmTkRKUXNJNWNQbFhONVVvQk5uY0t0YzRmMmRxMnZ3Q2pqWHNxdzdRIn0sInByb29mIjoiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNkltUndiM0FyYW5kMElpd2lhbmRySWpwN0ltdDBlU0k2SWs5TFVDSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSjRJam9pTVV3eFpVZ3lZVFpCWjFaMmVsUndOVnBoYkV0U1puRTJjRlpRVDNSRmFrazNhRGhVVUhwQ1dVWm5UU0o5ZlEuZXlKcFlYUWlPakUzTURRNU9EVXlNRFVzSW1WNGNDSTZNVGN3TkRrNU1qUXdOU3dpYm1KbUlqb3hOekEwT1RnMU1qQTFMQ0p6ZFdJaU9pSjNhWEpsWVhCd09pOHZaM1ZXV0RWNFpVWlRNMlZVWVhSdFdFSkplVUUwUVNFM1lUUXhZMlkxWWpjNU5qZ3pOREV3UUhkcGNtVXVZMjl0SWl3aWFuUnBJam9pTldVMk5qZzBZMkl0Tm1JME9DMDBOamhrTFdJd09URXRabVl3TkdKbFpEWmxZekpsSWl3aWJtOXVZMlVpT2lKVlJVcDVVakprY1U5RmFIcGFSa3BGV1ZkS1FtRlVhM2xQUkU1RldWUkZNbUZGY3pCa1NFbDRZMFZqSWl3aWFIUnRJam9pVUU5VFZDSXNJbWgwZFNJNkltaDBkSEE2THk5M2FYSmxMbU52YlRveE9UazRNeTlqYkdsbGJuUnpMemRoTkRGalpqVmlOemsyT0RNME1UQXZZV05qWlhOekxYUnZhMlZ1SWl3aVkyaGhiQ0k2SW1KWVZVZE9jRlZtWTFKNE0wVm9Rak0wZUZBemVUWXlZVkZhYjBkYVV6WnFJaXdpYUdGdVpHeGxJam9pZDJseVpXRndjRG92THlVME1HRnNhV05sWDNkcGNtVkFkMmx5WlM1amIyMGlMQ0owWldGdElqb2lkMmx5WlNKOS52bkN1T2JURFRLVFhCYXpyX3Z2X0xyZDBZT1Rac2xteHQtM2xKNWZKSU9iRVRidUVCTGlEaS1JVWZHcFJHTm1Dbm9IZjVocHNsWW5HeFMzSjloUmVDZyIsImNsaWVudF9pZCI6IndpcmVhcHA6Ly9ndVZYNXhlRlMzZVRhdG1YQkl5QTRBITdhNDFjZjViNzk2ODM0MTBAd2lyZS5jb20iLCJhcGlfdmVyc2lvbiI6NSwic2NvcGUiOiJ3aXJlX2NsaWVudF9pZCJ9.uCVYhmvCJm7nM1NxJQKl_XZJcSqm9eFmNmbRJkA5Wpsw70ZF1YANYC9nQ91QgsnuAbaRZMJiJt3P8ZntR2ozDQ`
ch := &Challenge{
Token: "bXUGNpUfcRx3EhB34xP3y62aQZoGZS6j",
}
issuedAtUnix, err := strconv.ParseInt("1704985205", 10, 64)
require.NoError(t, err)
issuedAt := time.Unix(issuedAtUnix, 0)
jwkBytes := []byte(`{"crv": "Ed25519", "kty": "OKP", "x": "1L1eH2a6AgVvzTp5ZalKRfq6pVPOtEjI7h8TPzBYFgM"}`)
var accountJWK jose.JSONWebKey
json.Unmarshal(jwkBytes, &accountJWK)
rawKid, err := accountJWK.Thumbprint(crypto.SHA256)
require.NoError(t, err)
accountJWK.KeyID = base64.RawURLEncoding.EncodeToString(rawKid)
at, dpop, err := parseAndVerifyWireAccessToken(wireVerifyParams{
token: token,
tokenKey: publicKey,
dpopKey: accountJWK.Public(),
dpopKeyID: accountJWK.KeyID,
issuer: issuer,
wireID: wireID,
chToken: ch.Token,
t: issuedAt.Add(1 * time.Minute), // set validation time to be one minute after issuance
})
if assert.NoError(t, err) {
// token assertions
assert.Equal(t, "42c46d4c-e510-4175-9fb5-d055e125a49d", at.ID)
assert.Equal(t, "http://wire.com:19983/clients/7a41cf5b79683410/access-token", at.Issuer)
assert.Equal(t, "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com", at.Subject)
assert.Contains(t, at.Audience, "http://wire.com:19983/clients/7a41cf5b79683410/access-token")
assert.Equal(t, "bXUGNpUfcRx3EhB34xP3y62aQZoGZS6j", at.Challenge)
assert.Equal(t, "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com", at.ClientID)
assert.Equal(t, 5, at.APIVersion)
assert.Equal(t, "wire_client_id", at.Scope)
if assert.NotNil(t, at.Cnf) {
assert.Equal(t, "oMWfNDJQsI5cPlXN5UoBNncKtc4f2dq2vwCjjXsqw7Q", at.Cnf.Kid)
}
// dpop proof assertions
dt := *dpop
assert.Equal(t, "bXUGNpUfcRx3EhB34xP3y62aQZoGZS6j", dt["chal"].(string))
assert.Equal(t, "wireapp://%40alice_wire@wire.com", dt["handle"].(string))
assert.Equal(t, "POST", dt["htm"].(string))
assert.Equal(t, "http://wire.com:19983/clients/7a41cf5b79683410/access-token", dt["htu"].(string))
assert.Equal(t, "5e6684cb-6b48-468d-b091-ff04bed6ec2e", dt["jti"].(string))
assert.Equal(t, "UEJyR2dqOEhzZFJEYWJBaTkyODNEYTE2aEs0dHIxcEc", dt["nonce"].(string))
assert.Equal(t, "wireapp://guVX5xeFS3eTatmXBIyA4A!7a41cf5b79683410@wire.com", dt["sub"].(string))
assert.Equal(t, "wire", dt["team"].(string))
}
}
func Test_validateWireOIDCClaims(t *testing.T) {
fakeKey := `
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`
opts := &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "http://dex:15818/dex",
},
Config: &wireprovisioner.Config{
ClientID: "wireapp",
Now: func() time.Time {
return time.Date(2024, 1, 12, 18, 32, 41, 0, time.UTC) // (Token Expiry: 2024-01-12 21:32:42 +0100 CET)
},
InsecureSkipSignatureCheck: true,
},
TransformTemplate: `{"name": "{{ .preferred_username }}", "handle": "{{ .name }}"}`,
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
}
err := opts.Validate()
require.NoError(t, err)
idTokenString := `eyJhbGciOiJSUzI1NiIsImtpZCI6IjZhNDZlYzQ3YTQzYWI1ZTc4NzU3MzM5NWY1MGY4ZGQ5MWI2OTM5MzcifQ.eyJpc3MiOiJodHRwOi8vZGV4OjE1ODE4L2RleCIsInN1YiI6IkNqcDNhWEpsWVhCd09pOHZTMmh0VjBOTFpFTlRXakoyT1dWTWFHRk9XVlp6WnlFeU5UZzFNVEpoT0RRek5qTXhaV1V6UUhkcGNtVXVZMjl0RWdSc1pHRnciLCJhdWQiOiJ3aXJlYXBwIiwiZXhwIjoxNzA1MDkxNTYyLCJpYXQiOjE3MDUwMDUxNjIsIm5vbmNlIjoib0VjUzBRQUNXLVIyZWkxS09wUmZ2QSIsImF0X2hhc2giOiJoYzk0NmFwS25FeEV5TDVlSzJZMzdRIiwiY19oYXNoIjoidmRubFp2V1d1bVd1Z2NYR1JpOU5FUSIsIm5hbWUiOiJ3aXJlYXBwOi8vJTQwYWxpY2Vfd2lyZUB3aXJlLmNvbSIsInByZWZlcnJlZF91c2VybmFtZSI6IkFsaWNlIFNtaXRoIn0.aEBhWJugBJ9J_0L_4odUCg8SR8HMXVjd__X8uZRo42BSJQQO7-wdpy0jU3S4FOX9fQKr68wD61gS_QsnhfiT7w9U36mLpxaYlNVDCYfpa-gklVFit_0mjUOukXajTLK6H527TGiSss8z22utc40ckS1SbZa2BzKu3yOcqnFHUQwQc5sLYfpRABTB6WBoYFtnWDzdpyWJDaOzz7lfKYv2JBnf9vV8u8SYm-6gNKgtiQ3UUnjhIVUjdfHet2BMvmV2ooZ8V441RULCzKKG_sWZba-D_k_TOnSholGobtUOcKHlmVlmfUe8v7kuyBdhbPcembfgViaNldLQGKZjZfgvLg`
ctx := context.Background()
o := opts.GetOIDCOptions()
c := o.GetConfig()
verifier := o.GetProvider(ctx).Verifier(c)
idToken, err := verifier.Verify(ctx, idTokenString)
require.NoError(t, err)
wireID := wire.ID{
Name: "Alice Smith",
Handle: "wireapp://%40alice_wire@wire.com",
}
got, err := validateWireOIDCClaims(o, idToken, wireID)
assert.NoError(t, err)
assert.Equal(t, "wireapp://%40alice_wire@wire.com", got["handle"].(string))
assert.Equal(t, "Alice Smith", got["name"].(string))
assert.Equal(t, "http://dex:15818/dex", got["iss"].(string))
}
func createWireOptions(t *testing.T, transformTemplate string) *wireprovisioner.Options {
t.Helper()
fakeKey := `
-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`
opts := &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
},
Config: &wireprovisioner.Config{
ClientID: "unit test",
Now: time.Now,
},
TransformTemplate: transformTemplate,
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
}
err := opts.Validate()
require.NoError(t, err)
return opts
}
func Test_idTokenTransformation(t *testing.T) {
// {"name": "wireapp://%40alice_wire@wire.com", "preferred_username": "Alice Smith", "iss": "http://dex:15818/dex", ...}
idTokenString := `eyJhbGciOiJSUzI1NiIsImtpZCI6IjZhNDZlYzQ3YTQzYWI1ZTc4NzU3MzM5NWY1MGY4ZGQ5MWI2OTM5MzcifQ.eyJpc3MiOiJodHRwOi8vZGV4OjE1ODE4L2RleCIsInN1YiI6IkNqcDNhWEpsWVhCd09pOHZTMmh0VjBOTFpFTlRXakoyT1dWTWFHRk9XVlp6WnlFeU5UZzFNVEpoT0RRek5qTXhaV1V6UUhkcGNtVXVZMjl0RWdSc1pHRnciLCJhdWQiOiJ3aXJlYXBwIiwiZXhwIjoxNzA1MDkxNTYyLCJpYXQiOjE3MDUwMDUxNjIsIm5vbmNlIjoib0VjUzBRQUNXLVIyZWkxS09wUmZ2QSIsImF0X2hhc2giOiJoYzk0NmFwS25FeEV5TDVlSzJZMzdRIiwiY19oYXNoIjoidmRubFp2V1d1bVd1Z2NYR1JpOU5FUSIsIm5hbWUiOiJ3aXJlYXBwOi8vJTQwYWxpY2Vfd2lyZUB3aXJlLmNvbSIsInByZWZlcnJlZF91c2VybmFtZSI6IkFsaWNlIFNtaXRoIn0.aEBhWJugBJ9J_0L_4odUCg8SR8HMXVjd__X8uZRo42BSJQQO7-wdpy0jU3S4FOX9fQKr68wD61gS_QsnhfiT7w9U36mLpxaYlNVDCYfpa-gklVFit_0mjUOukXajTLK6H527TGiSss8z22utc40ckS1SbZa2BzKu3yOcqnFHUQwQc5sLYfpRABTB6WBoYFtnWDzdpyWJDaOzz7lfKYv2JBnf9vV8u8SYm-6gNKgtiQ3UUnjhIVUjdfHet2BMvmV2ooZ8V441RULCzKKG_sWZba-D_k_TOnSholGobtUOcKHlmVlmfUe8v7kuyBdhbPcembfgViaNldLQGKZjZfgvLg`
var claims struct {
Name string `json:"name,omitempty"`
Handle string `json:"preferred_username,omitempty"`
Issuer string `json:"iss,omitempty"`
}
idToken, err := jose.ParseSigned(idTokenString)
require.NoError(t, err)
err = idToken.UnsafeClaimsWithoutVerification(&claims)
require.NoError(t, err)
// original token contains "Alice Smith" as handle, and name as "wireapp://%40alice_wire@wire.com"
assert.Equal(t, "Alice Smith", claims.Handle)
assert.Equal(t, "wireapp://%40alice_wire@wire.com", claims.Name)
assert.Equal(t, "http://dex:15818/dex", claims.Issuer)
var m map[string]any
err = idToken.UnsafeClaimsWithoutVerification(&m)
require.NoError(t, err)
opts := createWireOptions(t, "") // uses default transformation template
result, err := opts.GetOIDCOptions().Transform(m)
require.NoError(t, err)
// default transformation sets preferred username to handle; name as name
assert.Equal(t, "Alice Smith", result["handle"].(string))
assert.Equal(t, "wireapp://%40alice_wire@wire.com", result["name"].(string))
assert.Equal(t, "http://dex:15818/dex", result["iss"].(string))
// swap the preferred_name and the name
swap := `{"name": "{{ .preferred_username }}", "handle": "{{ .name }}"}`
opts = createWireOptions(t, swap)
result, err = opts.GetOIDCOptions().Transform(m)
require.NoError(t, err)
// with the transformation, handle now contains wireapp://%40alice_wire@wire.com, name contains Alice Smith
assert.Equal(t, "wireapp://%40alice_wire@wire.com", result["handle"].(string))
assert.Equal(t, "Alice Smith", result["name"].(string))
assert.Equal(t, "http://dex:15818/dex", result["iss"].(string))
}

File diff suppressed because it is too large Load Diff

@ -57,6 +57,9 @@ func (o *Options) GetSSHOptions() *SSHOptions {
// GetWireOptions returns the SSH options.
func (o *Options) GetWireOptions() (*wire.Options, error) {
if o == nil {
return nil, errors.New("no options available")
}
if o.Wire == nil {
return nil, errors.New("no Wire options available")
}
if err := o.Wire.Validate(); err != nil {

Loading…
Cancel
Save