diff --git a/acme/api/order_test.go b/acme/api/order_test.go index 1a3c5df4..4948ea94 100644 --- a/acme/api/order_test.go +++ b/acme/api/order_test.go @@ -1729,11 +1729,11 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= TokenURL: "", JWKSURL: "", UserInfoURL: "", - Algorithms: []string{}, + Algorithms: []string{"ES256"}, }, Config: &wire.Config{ ClientID: "integration test", - SignatureAlgorithms: []string{}, + SignatureAlgorithms: []string{"ES256"}, SkipClientIDCheck: true, SkipExpiryCheck: true, SkipIssuerCheck: true, diff --git a/acme/api/wire_integration_test.go b/acme/api/wire_integration_test.go index 2fa72797..3bb62a9a 100644 --- a/acme/api/wire_integration_test.go +++ b/acme/api/wire_integration_test.go @@ -85,7 +85,7 @@ func TestWireIntegration(t *testing.T) { "organization": "WireTest", "commonName": {{ toJson .Oidc.name }} }, - "uris": [{{ toJson .Oidc.handle }}, {{ toJson .Dpop.sub }}], + "uris": [{{ toJson .Oidc.preferred_username }}, {{ toJson .Dpop.sub }}], "keyUsage": ["digitalSignature"], "extKeyUsage": ["clientAuth"] }`, @@ -98,11 +98,11 @@ func TestWireIntegration(t *testing.T) { TokenURL: "", JWKSURL: "", UserInfoURL: "", - Algorithms: []string{}, + Algorithms: []string{"ES256"}, }, Config: &wire.Config{ ClientID: "integration test", - SignatureAlgorithms: []string{}, + SignatureAlgorithms: []string{"ES256"}, SkipClientIDCheck: true, SkipExpiryCheck: true, SkipIssuerCheck: true, @@ -306,12 +306,16 @@ func TestWireIntegration(t *testing.T) { jose.Claims Challenge string `json:"chal,omitempty"` Handle string `json:"handle,omitempty"` + Nonce string `json:"nonce,omitempty"` + HTU string `json:"htu,omitempty"` }{ Claims: jose.Claims{ Subject: "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com", }, Challenge: "token", Handle: "wireapp://%40alice.smith.qa@example.com", + Nonce: "nonce", + HTU: "http://issuer.example.com", }) require.NoError(t, err) dpop, err := dpopSigner.Sign(dpopBytes) @@ -366,6 +370,7 @@ func TestWireIntegration(t *testing.T) { jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: "https://issuer.example.com", @@ -374,6 +379,7 @@ func TestWireIntegration(t *testing.T) { }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := oidcTokenSigner.Sign(tokenBytes) @@ -382,10 +388,8 @@ func TestWireIntegration(t *testing.T) { require.NoError(t, err) p, err := json.Marshal(struct { IDToken string `json:"id_token"` - KeyAuth string `json:"keyauth"` }{ IDToken: idToken, - KeyAuth: keyAuth, }) require.NoError(t, err) payload = p @@ -436,7 +440,7 @@ func TestWireIntegration(t *testing.T) { t.Log("updated challenge:", challenge.ID, challenge.Status) switch challenge.Type { case acme.WIREOIDC01: - err = db.CreateOidcToken(ctx, order.ID, map[string]any{"name": "Smith, Alice M (QA)", "handle": "wireapp://%40alice.smith.qa@example.com"}) + err = db.CreateOidcToken(ctx, order.ID, map[string]any{"name": "Smith, Alice M (QA)", "preferred_username": "wireapp://%40alice.smith.qa@example.com"}) require.NoError(t, err) case acme.WIREDPOP01: err = db.CreateDpopToken(ctx, order.ID, map[string]any{"sub": "wireapp://lJGYPz0ZRq2kvc_XpdaDlA!ed416ce8ecdd9fad@example.com"}) diff --git a/acme/challenge.go b/acme/challenge.go index 7b8c7254..7d67a4d2 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -355,8 +355,6 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK type wireOidcPayload struct { // IDToken contains the OIDC identity token IDToken string `json:"id_token"` - // KeyAuth ({challenge-token}.{jwk-thumbprint}) - KeyAuth string `json:"keyauth"` } func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, payload []byte) error { @@ -381,16 +379,6 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO return WrapErrorISE(err, "failed getting Wire options") } - // TODO(hs): move this into validation below? - expectedKeyAuth, err := KeyAuthorization(ch.Token, jwk) - if err != nil { - return WrapErrorISE(err, "error determining key authorization") - } - if expectedKeyAuth != oidcPayload.KeyAuth { - return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, - "keyAuthorization does not match; expected %q, but got %q", expectedKeyAuth, oidcPayload.KeyAuth)) - } - oidcOptions := wireOptions.GetOIDCOptions() verifier := oidcOptions.GetProvider(ctx).Verifier(oidcOptions.GetConfig()) idToken, err := verifier.Verify(ctx, oidcPayload.IDToken) @@ -404,13 +392,23 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO Handle string `json:"name"` Issuer string `json:"iss,omitempty"` GivenName string `json:"given_name,omitempty"` - KeyAuth string `json:"keyauth"` // TODO(hs): use this property instead of the one in the payload after https://github.com/wireapp/rusty-jwt-tools/tree/fix/keyauth is done + KeyAuth string `json:"keyauth"` } if err := idToken.Claims(&claims); err != nil { return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err, "error retrieving claims from ID token")) } + // TODO(hs): move this into validation below? + expectedKeyAuth, err := KeyAuthorization(ch.Token, jwk) + if err != nil { + return WrapErrorISE(err, "error determining key authorization") + } + if expectedKeyAuth != claims.KeyAuth { + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, + "keyAuthorization does not match; expected %q, but got %q", expectedKeyAuth, claims.KeyAuth)) + } + transformedIDToken, err := validateWireOIDCClaims(oidcOptions, idToken, wireID) if err != nil { return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err, "claims in OIDC ID token don't match")) @@ -459,12 +457,12 @@ func validateWireOIDCClaims(o *wireprovisioner.OIDCOptions, token *oidc.IDToken, return nil, fmt.Errorf("invalid 'name' %q after transformation", name) } - handle, ok := transformed["handle"] + preferredUsername, ok := transformed["preferred_username"] if !ok { - return nil, fmt.Errorf("transformed OIDC ID token does not contain 'handle'") + return nil, fmt.Errorf("transformed OIDC ID token does not contain 'preferred_username'") } - if wireID.Handle != handle { - return nil, fmt.Errorf("invalid 'handle' %q after transformation", handle) + if wireID.Handle != preferredUsername { + return nil, fmt.Errorf("invalid 'preferred_username' %q after transformation", preferredUsername) } return transformed, nil diff --git a/acme/challenge_test.go b/acme/challenge_test.go index 5bddfa13..4d63928e 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -891,6 +891,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -899,6 +900,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -907,10 +909,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -929,17 +929,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -978,7 +975,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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)) + assert.Equal(t, "wireapp://%40alice_wire@wire.com", idToken["preferred_username"].(string)) return nil }, }, @@ -1079,16 +1076,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuerexample.com", + IssuerURL: "http://issuerexample.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, diff --git a/acme/challenge_wire_test.go b/acme/challenge_wire_test.go index 2ed33b38..65f7be51 100644 --- a/acme/challenge_wire_test.go +++ b/acme/challenge_wire_test.go @@ -163,7 +163,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ ClientID: "test", @@ -322,7 +323,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ ClientID: "test", @@ -463,16 +465,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -612,16 +611,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -761,16 +757,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -917,16 +910,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: "http://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1106,12 +1096,38 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, "fail/keyauth-mismatch": func(t *testing.T) test { jwk, _ := 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 := mustJWKServer(t, signerJWK.Public()) + tokenBytes, err := json.Marshal(struct { + jose.Claims + Name string `json:"name,omitempty"` + PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` + }{ + Claims: jose.Claims{ + Issuer: srv.URL, + Audience: []string{"test"}, + Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), + }, + Name: "Alice Smith", + PreferredUsername: "wireapp://%40bob@wire.com", + KeyAuth: "wrong-keyauth", + }) + 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: "some-token", - KeyAuth: "wrong-key-authorization", + IDToken: idToken, }) require.NoError(t, err) valueBytes, err := json.Marshal(struct { @@ -1130,7 +1146,9 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://issuer.example.com", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ ClientID: "test", @@ -1154,6 +1172,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Status: StatusPending, Value: string(valueBytes), }, + srv: srv, payload: payload, ctx: ctx, jwk: jwk, @@ -1170,7 +1189,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= assert.Equal(t, "urn:ietf:params:acme:error:rejectedIdentifier", k.Type) assert.Equal(t, "The server will not issue certificates for the identifier", k.Detail) assert.Equal(t, 400, k.Status) - assert.Contains(t, k.Err.Error(), `keyAuthorization does not match; expected`) + assert.Contains(t, k.Err.Error(), "keyAuthorization does not match") } } return nil @@ -1194,6 +1213,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1201,7 +1221,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), }, Name: "Alice Smith", - PreferredUsername: "wireapp://%40alice_wire@wire.com", + PreferredUsername: "wireapp://%40bob@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1210,10 +1231,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1232,17 +1251,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1300,6 +1316,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1308,6 +1325,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40bob@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1316,10 +1334,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1338,17 +1354,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1384,7 +1397,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= assert.Equal(t, "urn:ietf:params:acme:error:rejectedIdentifier", k.Type) assert.Equal(t, "The server will not issue certificates for the identifier", k.Detail) assert.Equal(t, 400, k.Status) - assert.Equal(t, `claims in OIDC ID token don't match: invalid 'handle' "wireapp://%40bob@wire.com" after transformation`, k.Err.Error()) + assert.Equal(t, `claims in OIDC ID token don't match: invalid 'preferred_username' "wireapp://%40bob@wire.com" after transformation`, k.Err.Error()) } } return nil @@ -1406,6 +1419,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1414,6 +1428,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1422,10 +1437,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1444,17 +1457,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1509,6 +1519,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1517,6 +1528,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1525,10 +1537,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1547,17 +1557,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1616,6 +1623,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1624,6 +1632,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1632,10 +1641,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1654,17 +1661,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1723,6 +1727,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1731,6 +1736,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1739,10 +1745,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1761,17 +1765,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1810,7 +1811,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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)) + assert.Equal(t, "wireapp://%40alice_wire@wire.com", idToken["preferred_username"].(string)) return errors.New("fail") }, }, @@ -1836,6 +1837,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= jose.Claims Name string `json:"name,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"` + KeyAuth string `json:"keyauth"` }{ Claims: jose.Claims{ Issuer: srv.URL, @@ -1844,6 +1846,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= }, Name: "Alice Smith", PreferredUsername: "wireapp://%40alice_wire@wire.com", + KeyAuth: keyAuth, }) require.NoError(t, err) signed, err := signer.Sign(tokenBytes) @@ -1852,10 +1855,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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 { @@ -1874,17 +1875,14 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= Wire: &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: srv.URL, - JWKSURL: srv.URL + "/keys", + IssuerURL: srv.URL, + JWKSURL: srv.URL + "/keys", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "test", - SignatureAlgorithms: []string{"ES256"}, - SkipClientIDCheck: false, - SkipExpiryCheck: false, - SkipIssuerCheck: false, - InsecureSkipSignatureCheck: false, - Now: time.Now, + ClientID: "test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: "", }, @@ -1923,7 +1921,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= 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)) + assert.Equal(t, "wireapp://%40alice_wire@wire.com", idToken["preferred_username"].(string)) return nil }, }, @@ -2034,16 +2032,18 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= opts := &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "http://dex:15818/dex", + IssuerURL: "http://dex:15818/dex", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "wireapp", + ClientID: "wireapp", + SignatureAlgorithms: []string{"ES256"}, 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, + InsecureSkipSignatureCheck: true, // skipping signature check for this specific test }, - TransformTemplate: `{"name": "{{ .preferred_username }}", "handle": "{{ .name }}"}`, + TransformTemplate: `{"name": "{{ .preferred_username }}", "preferred_username": "{{ .name }}"}`, }, DPOP: &wireprovisioner.DPOPOptions{ SigningKey: []byte(fakeKey), @@ -2069,7 +2069,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= got, err := validateWireOIDCClaims(o, idToken, wireID) assert.NoError(t, err) - assert.Equal(t, "wireapp://%40alice_wire@wire.com", got["handle"].(string)) + assert.Equal(t, "wireapp://%40alice_wire@wire.com", got["preferred_username"].(string)) assert.Equal(t, "Alice Smith", got["name"].(string)) assert.Equal(t, "http://dex:15818/dex", got["iss"].(string)) } @@ -2083,11 +2083,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k= opts := &wireprovisioner.Options{ OIDC: &wireprovisioner.OIDCOptions{ Provider: &wireprovisioner.Provider{ - IssuerURL: "https://issuer.example.com", + IssuerURL: "https://issuer.example.com", + Algorithms: []string{"ES256"}, }, Config: &wireprovisioner.Config{ - ClientID: "unit test", - Now: time.Now, + ClientID: "unit test", + SignatureAlgorithms: []string{"ES256"}, + Now: time.Now, }, TransformTemplate: transformTemplate, }, @@ -2130,18 +2132,18 @@ func Test_idTokenTransformation(t *testing.T) { 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, "Alice Smith", result["preferred_username"].(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 }}"}` + swap := `{"name": "{{ .preferred_username }}", "preferred_username": "{{ .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, "wireapp://%40alice_wire@wire.com", result["preferred_username"].(string)) assert.Equal(t, "Alice Smith", result["name"].(string)) assert.Equal(t, "http://dex:15818/dex", result["iss"].(string)) } diff --git a/acme/db/nosql/wire_test.go b/acme/db/nosql/wire_test.go index 136db3a0..6759f420 100644 --- a/acme/db/nosql/wire_test.go +++ b/acme/db/nosql/wire_test.go @@ -270,7 +270,7 @@ func TestDB_GetOidcToken(t *testing.T) { require.NoError(t, err) token := dbOidcToken{ ID: "orderID", - Content: []byte(`{"name": "Alice Smith", "handle": "@alice.smith"}`), + Content: []byte(`{"name": "Alice Smith", "preferred_username": "@alice.smith"}`), CreatedAt: time.Now(), } b, err := json.Marshal(token) @@ -283,8 +283,8 @@ func TestDB_GetOidcToken(t *testing.T) { }, orderID: "orderID", expected: map[string]any{ - "name": "Alice Smith", - "handle": "@alice.smith", + "name": "Alice Smith", + "preferred_username": "@alice.smith", }, } }, @@ -335,8 +335,8 @@ func TestDB_CreateOidcToken(t *testing.T) { }, orderID: "orderID", oidc: map[string]any{ - "name": "Alice Smith", - "handle": "@alice.smith", + "name": "Alice Smith", + "preferred_username": "@alice.smith", }, expectedErr: errors.New("failed saving oidc token: error saving acme oidc: fail"), } @@ -351,8 +351,8 @@ func TestDB_CreateOidcToken(t *testing.T) { }, orderID: "orderID", oidc: map[string]any{ - "name": "Alice Smith", - "handle": "@alice.smith", + "name": "Alice Smith", + "preferred_username": "@alice.smith", }, } }, diff --git a/authority/provisioner/wire/oidc_options.go b/authority/provisioner/wire/oidc_options.go index 5040fa07..5bbcbc7a 100644 --- a/authority/provisioner/wire/oidc_options.go +++ b/authority/provisioner/wire/oidc_options.go @@ -68,7 +68,7 @@ func (o *OIDCOptions) GetConfig() *oidc.Config { } } -const defaultTemplate = `{"name": "{{ .name }}", "handle": "{{ .preferred_username }}"}` +const defaultTemplate = `{"name": "{{ .name }}", "preferred_username": "{{ .preferred_username }}"}` func (o *OIDCOptions) validateAndInitialize() (err error) { if o.Provider == nil { diff --git a/authority/provisioner/wire/oidc_options_test.go b/authority/provisioner/wire/oidc_options_test.go index 6346bcd8..8b3eaa75 100644 --- a/authority/provisioner/wire/oidc_options_test.go +++ b/authority/provisioner/wire/oidc_options_test.go @@ -11,9 +11,9 @@ import ( func TestOIDCOptions_Transform(t *testing.T) { defaultTransform, err := parseTransform(``) require.NoError(t, err) - swapTransform, err := parseTransform(`{"name": "{{ .preferred_username }}", "handle": "{{ .name }}"}`) + swapTransform, err := parseTransform(`{"name": "{{ .preferred_username }}", "preferred_username": "{{ .name }}"}`) require.NoError(t, err) - funcTransform, err := parseTransform(`{"name": "{{ .name }}", "handle": "{{ first .usernames }}"}`) + funcTransform, err := parseTransform(`{"name": "{{ .name }}", "preferred_username": "{{ first .usernames }}"}`) require.NoError(t, err) type fields struct { transform *template.Template @@ -67,7 +67,6 @@ func TestOIDCOptions_Transform(t *testing.T) { }, want: map[string]any{ "name": "Example", - "handle": "Preferred", "preferred_username": "Preferred", }, }, @@ -84,8 +83,7 @@ func TestOIDCOptions_Transform(t *testing.T) { }, want: map[string]any{ "name": "Preferred", - "handle": "Example", - "preferred_username": "Preferred", + "preferred_username": "Example", }, }, { @@ -100,9 +98,9 @@ func TestOIDCOptions_Transform(t *testing.T) { }, }, want: map[string]any{ - "name": "Example", - "handle": "name-1", - "usernames": []string{"name-1", "name-2", "name-3"}, + "name": "Example", + "preferred_username": "name-1", + "usernames": []string{"name-1", "name-2", "name-3"}, }, }, }