Add test for wireOIDC01Validate

This commit is contained in:
Herman Slatman 2024-01-15 21:59:20 +01:00
parent a2304c8498
commit d84abac4df
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F
2 changed files with 184 additions and 16 deletions

View File

@ -115,7 +115,7 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey,
case WIREDPOP01:
return wireDPOP01Validate(ctx, ch, db, jwk, payload)
default:
return NewErrorISE("unexpected challenge type '%s'", ch.Type)
return NewErrorISE("unexpected challenge type %q", ch.Type)
}
}
@ -378,7 +378,8 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
}
oidcOptions := wireOptions.GetOIDCOptions()
idToken, err := oidcOptions.GetProvider(ctx).Verifier(oidcOptions.GetConfig()).Verify(ctx, oidcPayload.IDToken)
verifier := oidcOptions.GetProvider(ctx).Verifier(oidcOptions.GetConfig())
idToken, err := verifier.Verify(ctx, oidcPayload.IDToken)
if err != nil {
return storeError(ctx, db, ch, false, WrapError(ErrorRejectedIdentifierType, err,
"error verifying ID token signature"))

View File

@ -200,6 +200,25 @@ func mustAttestYubikey(t *testing.T, _, keyAuthorization string, serial int) ([]
return payload, leaf, ca.Root
}
func newWireProvisionerWithOptions(t *testing.T, options *provisioner.Options) *provisioner.ACME {
t.Helper()
prov := &provisioner.ACME{
Type: "ACME",
Name: "acme",
Options: options,
Challenges: []provisioner.ACMEChallenge{
provisioner.WIREOIDC_01,
provisioner.WIREDPOP_01,
},
}
if err := prov.Init(provisioner.Config{
Claims: config.GlobalProvisionerClaims,
}); err != nil {
t.Fatal(err)
}
return prov
}
func Test_storeError(t *testing.T) {
type test struct {
ch *Challenge
@ -400,6 +419,9 @@ func TestKeyAuthorization(t *testing.T) {
}
func TestChallenge_Validate(t *testing.T) {
fakeKey := `-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
-----END PUBLIC KEY-----`
type test struct {
ch *Challenge
vc Client
@ -434,7 +456,7 @@ func TestChallenge_Validate(t *testing.T) {
}
return test{
ch: ch,
err: NewErrorISE("unexpected challenge type 'foo'"),
err: NewErrorISE(`unexpected challenge type "foo"`),
}
},
"fail/http-01": func(t *testing.T) test {
@ -857,6 +879,113 @@ func TestChallenge_Validate(t *testing.T) {
},
}
},
"ok/wire-oidc-01": func(t *testing.T) test {
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
require.NoError(t, err)
signer, err := jose.NewSigner(jose.SigningKey{
Algorithm: jose.SignatureAlgorithm(signerJWK.Algorithm),
Key: signerJWK,
}, new(jose.SignerOptions))
require.NoError(t, err)
srv := mustOIDCServer(t, signerJWK.Public())
tokenBytes, err := json.Marshal(struct {
jose.Claims
Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"`
}{
Claims: jose.Claims{
Issuer: srv.URL,
Audience: []string{"test"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
},
Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com",
})
require.NoError(t, err)
signed, err := signer.Sign(tokenBytes)
require.NoError(t, err)
idToken, err := signed.CompactSerialize()
require.NoError(t, err)
payload, err := json.Marshal(struct {
IDToken string `json:"id_token"`
KeyAuth string `json:"keyauth"`
}{
IDToken: idToken,
KeyAuth: keyAuth,
})
require.NoError(t, err)
valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"`
ClientID string `json:"client-id,omitempty"`
Handle string `json:"handle,omitempty"`
}{
Name: "Alice Smith",
Domain: "wire.com",
ClientID: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Handle: "wireapp://%40alice_wire@wire.com",
})
require.NoError(t, err)
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: srv.URL,
JWKSURL: srv.URL + "/keys",
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
SkipClientIDCheck: false,
SkipExpiryCheck: false,
SkipIssuerCheck: false,
InsecureSkipSignatureCheck: false,
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
return test{
ch: &Challenge{
ID: "chID",
AuthorizationID: "azID",
AccountID: "accID",
Token: "token",
Type: "wire-oidc-01",
Status: StatusPending,
Value: string(valueBytes),
},
srv: srv,
payload: payload,
ctx: ctx,
jwk: jwk,
db: &MockDB{
MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error {
assert.Equal(t, "chID", updch.ID)
assert.Equal(t, "token", updch.Token)
assert.Equal(t, StatusValid, updch.Status)
assert.Equal(t, ChallengeType("wire-oidc-01"), updch.Type)
assert.Equal(t, string(valueBytes), updch.Value)
return nil
},
MockGetAllOrdersByAccountID: func(ctx context.Context, accountID string) ([]string, error) {
assert.Equal(t, "accID", accountID)
return []string{"orderID"}, nil
},
MockCreateOidcToken: func(ctx context.Context, orderID string, idToken map[string]interface{}) error {
assert.Equal(t, "orderID", orderID)
assert.Equal(t, "Alice Smith", idToken["name"].(string))
assert.Equal(t, "wireapp://%40alice_wire@wire.com", idToken["handle"].(string))
return nil
},
},
}
},
}
for name, run := range tests {
t.Run(name, func(t *testing.T) {
@ -871,25 +1000,63 @@ func TestChallenge_Validate(t *testing.T) {
ctx = context.Background()
}
ctx = NewClientContext(ctx, tc.vc)
if err := tc.ch.Validate(ctx, tc.db, tc.jwk, tc.payload); err != nil {
if assert.Error(t, tc.err) {
var k *Error
if errors.As(err, &k) {
assert.Equal(t, tc.err.Type, k.Type)
assert.Equal(t, tc.err.Detail, k.Detail)
assert.Equal(t, tc.err.Status, k.Status)
assert.Equal(t, tc.err.Err.Error(), k.Err.Error())
} else {
assert.Fail(t, "unexpected error type")
}
err := tc.ch.Validate(ctx, tc.db, tc.jwk, tc.payload)
if tc.err != nil {
var k *Error
if errors.As(err, &k) {
assert.Equal(t, tc.err.Type, k.Type)
assert.Equal(t, tc.err.Detail, k.Detail)
assert.Equal(t, tc.err.Status, k.Status)
assert.Equal(t, tc.err.Err.Error(), k.Err.Error())
} else {
assert.Fail(t, "unexpected error type")
}
} else {
assert.Nil(t, tc.err)
return
}
assert.NoError(t, err)
})
}
}
func mustOIDCServer(t *testing.T, pub jose.JSONWebKey) *httptest.Server {
t.Helper()
mux := http.NewServeMux()
server := httptest.NewServer(mux)
b, err := json.Marshal(struct {
Keys []jose.JSONWebKey `json:"keys,omitempty"`
}{
Keys: []jose.JSONWebKey{pub},
})
require.NoError(t, err)
jwks := string(b)
wellKnown := fmt.Sprintf(`{
"issuer": "%[1]s",
"authorization_endpoint": "%[1]s/auth",
"token_endpoint": "%[1]s/token",
"jwks_uri": "%[1]s/keys",
"userinfo_endpoint": "%[1]s/userinfo",
"id_token_signing_alg_values_supported": ["ES256"]
}`, server.URL)
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, req *http.Request) {
_, err := io.WriteString(w, wellKnown)
if err != nil {
w.WriteHeader(500)
}
})
mux.HandleFunc("/keys", func(w http.ResponseWriter, req *http.Request) {
_, err := io.WriteString(w, jwks)
if err != nil {
w.WriteHeader(500)
}
})
t.Cleanup(server.Close)
return server
}
type errReader int
func (errReader) Read([]byte) (int, error) {