Add audience validation to access, dpop and id token

pull/1681/head
Herman Slatman 5 months ago
parent 2f3819aa4e
commit 19dbd02451
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -362,6 +362,10 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
if !ok { if !ok {
return NewErrorISE("missing provisioner") return NewErrorISE("missing provisioner")
} }
linker, ok := LinkerFromContext(ctx)
if !ok {
return NewErrorISE("missing linker")
}
var oidcPayload wireOidcPayload var oidcPayload wireOidcPayload
err := json.Unmarshal(payload, &oidcPayload) err := json.Unmarshal(payload, &oidcPayload)
@ -388,11 +392,12 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
} }
var claims struct { var claims struct {
Name string `json:"preferred_username,omitempty"` Name string `json:"preferred_username,omitempty"`
Handle string `json:"name"` Handle string `json:"name"`
Issuer string `json:"iss,omitempty"` Issuer string `json:"iss,omitempty"`
GivenName string `json:"given_name,omitempty"` GivenName string `json:"given_name,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud,omitempty"`
} }
if err := idToken.Claims(&claims); err != nil { if err := idToken.Claims(&claims); err != nil {
return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err, return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err,
@ -409,6 +414,13 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
"keyAuthorization does not match; expected %q, but got %q", expectedKeyAuth, claims.KeyAuth)) "keyAuthorization does not match; expected %q, but got %q", expectedKeyAuth, claims.KeyAuth))
} }
// audience is the full URL to the challenge
acmeAudience := linker.GetLink(ctx, ChallengeLinkType, ch.AuthorizationID, ch.ID)
if claims.ACMEAudience != acmeAudience {
return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType,
"invalid 'acme_aud' %q", claims.ACMEAudience))
}
transformedIDToken, err := validateWireOIDCClaims(oidcOptions, idToken, wireID) transformedIDToken, err := validateWireOIDCClaims(oidcOptions, idToken, wireID)
if err != nil { if err != nil {
return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err, "claims in OIDC ID token don't match")) return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err, "claims in OIDC ID token don't match"))
@ -478,6 +490,10 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
if !ok { if !ok {
return NewErrorISE("missing provisioner") return NewErrorISE("missing provisioner")
} }
linker, ok := LinkerFromContext(ctx)
if !ok {
return NewErrorISE("missing linker")
}
var dpopPayload wireDpopPayload var dpopPayload wireDpopPayload
if err := json.Unmarshal(payload, &dpopPayload); err != nil { if err := json.Unmarshal(payload, &dpopPayload); err != nil {
@ -505,12 +521,16 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, accountJWK *j
return WrapErrorISE(err, "invalid Go template registered for 'target'") return WrapErrorISE(err, "invalid Go template registered for 'target'")
} }
// audience is the full URL to the challenge
audience := linker.GetLink(ctx, ChallengeLinkType, ch.AuthorizationID, ch.ID)
params := wireVerifyParams{ params := wireVerifyParams{
token: dpopPayload.AccessToken, token: dpopPayload.AccessToken,
tokenKey: dpopOptions.GetSigningKey(), tokenKey: dpopOptions.GetSigningKey(),
dpopKey: accountJWK.Public(), dpopKey: accountJWK.Public(),
dpopKeyID: accountJWK.KeyID, dpopKeyID: accountJWK.KeyID,
issuer: issuer, issuer: issuer,
audience: audience,
wireID: wireID, wireID: wireID,
chToken: ch.Token, chToken: ch.Token,
t: clock.Now().UTC(), t: clock.Now().UTC(),
@ -577,6 +597,7 @@ type wireVerifyParams struct {
dpopKey crypto.PublicKey dpopKey crypto.PublicKey
dpopKeyID string dpopKeyID string
issuer string issuer string
audience string
wireID wire.ID wireID wire.ID
chToken string chToken string
t time.Time t time.Time
@ -611,8 +632,9 @@ func parseAndVerifyWireAccessToken(v wireVerifyParams) (*wireAccessToken, *wireD
} }
if err := accessToken.ValidateWithLeeway(jose.Expected{ if err := accessToken.ValidateWithLeeway(jose.Expected{
Time: v.t, Time: v.t,
Issuer: v.issuer, Issuer: v.issuer,
Audience: jose.Audience{v.audience},
}, 1*time.Minute); err != nil { }, 1*time.Minute); err != nil {
return nil, nil, fmt.Errorf("failed validation: %w", err) return nil, nil, fmt.Errorf("failed validation: %w", err)
} }
@ -626,7 +648,7 @@ func parseAndVerifyWireAccessToken(v wireVerifyParams) (*wireAccessToken, *wireD
if accessToken.ClientID != v.wireID.ClientID { if accessToken.ClientID != v.wireID.ClientID {
return nil, nil, fmt.Errorf("invalid Wire client ID %q", accessToken.ClientID) return nil, nil, fmt.Errorf("invalid Wire client ID %q", accessToken.ClientID)
} }
if accessToken.Expiry.Time().After(v.t.Add(time.Hour * 24 * 365)) { if accessToken.Expiry.Time().After(v.t.Add(time.Hour)) {
return nil, nil, fmt.Errorf("'exp' %s is too far into the future", accessToken.Expiry.Time().String()) return nil, nil, fmt.Errorf("'exp' %s is too far into the future", accessToken.Expiry.Time().String())
} }
if accessToken.Scope != "wire_client_id" { if accessToken.Scope != "wire_client_id" {
@ -656,14 +678,15 @@ func parseAndVerifyWireAccessToken(v wireVerifyParams) (*wireAccessToken, *wireD
} }
if err := wireDpop.ValidateWithLeeway(jose.Expected{ if err := wireDpop.ValidateWithLeeway(jose.Expected{
Time: v.t, Time: v.t,
Audience: jose.Audience{v.audience},
}, 1*time.Minute); err != nil { }, 1*time.Minute); err != nil {
return nil, nil, fmt.Errorf("failed DPoP validation: %w", err) return nil, nil, fmt.Errorf("failed DPoP validation: %w", err)
} }
if wireDpop.HTU == "" || wireDpop.HTU != v.issuer { // DPoP doesn't contains "iss" claim, but has it in the "htu" claim if wireDpop.HTU == "" || wireDpop.HTU != v.issuer { // DPoP doesn't contains "iss" claim, but has it in the "htu" claim
return nil, nil, fmt.Errorf("DPoP contains invalid issuer (htu) %q", wireDpop.HTU) return nil, nil, fmt.Errorf("DPoP contains invalid issuer (htu) %q", wireDpop.HTU)
} }
if wireDpop.Expiry.Time().After(v.t.Add(time.Hour * 24 * 365)) { if wireDpop.Expiry.Time().After(v.t.Add(time.Hour)) {
return nil, nil, fmt.Errorf("'exp' %s is too far into the future", wireDpop.Expiry.Time().String()) return nil, nil, fmt.Errorf("'exp' %s is too far into the future", wireDpop.Expiry.Time().String())
} }
if wireDpop.Subject != v.wireID.ClientID { if wireDpop.Subject != v.wireID.ClientID {

@ -202,7 +202,7 @@ func newWireProvisionerWithOptions(t *testing.T, options *provisioner.Options) *
t.Helper() t.Helper()
prov := &provisioner.ACME{ prov := &provisioner.ACME{
Type: "ACME", Type: "ACME",
Name: "acme", Name: "wire",
Options: options, Options: options,
Challenges: []provisioner.ACMEChallenge{ Challenges: []provisioner.ACMEChallenge{
provisioner.WIREOIDC_01, provisioner.WIREOIDC_01,
@ -892,6 +892,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -901,6 +902,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -945,6 +947,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -999,7 +1002,6 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
signerPEMBlock, err := pemutil.Serialize(signerJWK.Public().Key) signerPEMBlock, err := pemutil.Serialize(signerJWK.Public().Key)
require.NoError(t, err) require.NoError(t, err)
signerPEMBytes := pem.EncodeToMemory(signerPEMBlock) signerPEMBytes := pem.EncodeToMemory(signerPEMBlock)
dpopBytes, err := json.Marshal(struct { dpopBytes, err := json.Marshal(struct {
jose.Claims jose.Claims
Challenge string `json:"chal,omitempty"` Challenge string `json:"chal,omitempty"`
@ -1008,7 +1010,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -1034,7 +1037,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -1092,6 +1095,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",

@ -46,8 +46,21 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
} }
}, },
"fail/no-linker": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
return test{
ctx: ctx,
expectedErr: &Error{
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New("missing linker"),
},
}
},
"fail/unmarshal": func(t *testing.T) test { "fail/unmarshal": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ctx: ctx, ctx: ctx,
payload: []byte("?!"), payload: []byte("?!"),
@ -70,6 +83,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
"fail/wire-parse-id": func(t *testing.T) test { "fail/wire-parse-id": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ctx: ctx, ctx: ctx,
payload: []byte("{}"), payload: []byte("{}"),
@ -92,6 +106,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
"fail/wire-parse-client-id": func(t *testing.T) test { "fail/wire-parse-client-id": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
valueBytes, err := json.Marshal(struct { valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"` Domain string `json:"domain,omitempty"`
@ -126,6 +141,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
"fail/no-wire-options": func(t *testing.T) test { "fail/no-wire-options": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
valueBytes, err := json.Marshal(struct { valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"` Domain string `json:"domain,omitempty"`
@ -179,6 +195,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
valueBytes, err := json.Marshal(struct { valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"` Domain string `json:"domain,omitempty"`
@ -254,7 +271,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -280,7 +298,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -339,6 +357,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -396,7 +415,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -422,7 +442,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -481,6 +501,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -542,7 +563,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -568,7 +590,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -627,6 +649,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -688,7 +711,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -714,7 +738,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -773,6 +797,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -841,7 +866,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
HTU string `json:"htu,omitempty"` HTU string `json:"htu,omitempty"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com", Subject: "wireapp://CzbfFjDOQrenCbDxVmgnFw!594930e9d50bb175@wire.com",
Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
}, },
Challenge: "token", Challenge: "token",
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
@ -867,7 +893,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: "http://issuer.example.com", Issuer: "http://issuer.example.com",
Audience: []string{"test"}, Audience: jose.Audience{"https://ca.example.com/acme/wire/challenge/azID/chID"},
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Challenge: "token", Challenge: "token",
@ -882,7 +908,6 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
APIVersion: 5, APIVersion: 5,
Scope: "wire_client_id", Scope: "wire_client_id",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
require.NoError(t, err) require.NoError(t, err)
@ -926,6 +951,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1010,8 +1036,21 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
} }
}, },
"fail/no-linker": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
return test{
ctx: ctx,
expectedErr: &Error{
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New("missing linker"),
},
}
},
"fail/unmarshal": func(t *testing.T) test { "fail/unmarshal": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ctx: ctx, ctx: ctx,
payload: []byte("?!"), payload: []byte("?!"),
@ -1040,6 +1079,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
"fail/wire-parse-id": func(t *testing.T) test { "fail/wire-parse-id": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ctx: ctx, ctx: ctx,
payload: []byte("{}"), payload: []byte("{}"),
@ -1062,6 +1102,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
"fail/no-wire-options": func(t *testing.T) test { "fail/no-wire-options": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{})) ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
valueBytes, err := json.Marshal(struct { valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
Domain string `json:"domain,omitempty"` Domain string `json:"domain,omitempty"`
@ -1094,8 +1135,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
} }
}, },
"fail/keyauth-mismatch": func(t *testing.T) test { "fail/verify": func(t *testing.T) test {
jwk, _ := mustAccountAndKeyAuthorization(t, "token") jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
require.NoError(t, err) require.NoError(t, err)
signer, err := jose.NewSigner(jose.SigningKey{ signer, err := jose.NewSigner(jose.SigningKey{
@ -1103,12 +1144,15 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Key: signerJWK, Key: signerJWK,
}, new(jose.SignerOptions)) }, new(jose.SignerOptions))
require.NoError(t, err) require.NoError(t, err)
srv := mustJWKServer(t, signerJWK.Public()) anotherSignerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
require.NoError(t, err)
srv := mustJWKServer(t, anotherSignerJWK.Public())
tokenBytes, err := json.Marshal(struct { tokenBytes, err := json.Marshal(struct {
jose.Claims jose.Claims
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1116,8 +1160,9 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40bob@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: "wrong-keyauth", KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1162,6 +1207,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1189,7 +1235,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
assert.Equal(t, "urn:ietf:params:acme:error:rejectedIdentifier", k.Type) 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, "The server will not issue certificates for the identifier", k.Detail)
assert.Equal(t, 400, k.Status) assert.Equal(t, 400, k.Status)
assert.Contains(t, k.Err.Error(), "keyAuthorization does not match") assert.Equal(t, `error verifying ID token signature: failed to verify signature: failed to verify id token signature`, k.Err.Error())
} }
} }
return nil return nil
@ -1197,8 +1243,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
} }
}, },
"fail/verify": func(t *testing.T) test { "fail/keyauth-mismatch": func(t *testing.T) test {
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token") jwk, _ := mustAccountAndKeyAuthorization(t, "token")
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
require.NoError(t, err) require.NoError(t, err)
signer, err := jose.NewSigner(jose.SigningKey{ signer, err := jose.NewSigner(jose.SigningKey{
@ -1206,14 +1252,13 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Key: signerJWK, Key: signerJWK,
}, new(jose.SignerOptions)) }, new(jose.SignerOptions))
require.NoError(t, err) require.NoError(t, err)
anotherSignerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) srv := mustJWKServer(t, signerJWK.Public())
require.NoError(t, err)
srv := mustJWKServer(t, anotherSignerJWK.Public())
tokenBytes, err := json.Marshal(struct { tokenBytes, err := json.Marshal(struct {
jose.Claims jose.Claims
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1221,8 +1266,9 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)), Expiry: jose.NewNumericDate(time.Now().Add(1 * time.Minute)),
}, },
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40bob@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: "wrong-keyauth",
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1267,6 +1313,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1294,7 +1341,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
assert.Equal(t, "urn:ietf:params:acme:error:rejectedIdentifier", k.Type) 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, "The server will not issue certificates for the identifier", k.Detail)
assert.Equal(t, 400, k.Status) assert.Equal(t, 400, k.Status)
assert.Equal(t, `error verifying ID token signature: failed to verify signature: failed to verify id token signature`, k.Err.Error()) assert.Contains(t, k.Err.Error(), "keyAuthorization does not match")
} }
} }
return nil return nil
@ -1317,6 +1364,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1326,6 +1374,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40bob@wire.com", PreferredUsername: "wireapp://%40bob@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1370,6 +1419,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1420,6 +1470,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1429,6 +1480,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1473,6 +1525,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1520,6 +1573,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1529,6 +1583,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1573,6 +1628,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1624,6 +1680,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1633,6 +1690,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1677,6 +1735,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1728,6 +1787,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1737,6 +1797,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1781,6 +1842,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1838,6 +1900,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
PreferredUsername string `json:"preferred_username,omitempty"` PreferredUsername string `json:"preferred_username,omitempty"`
KeyAuth string `json:"keyauth"` KeyAuth string `json:"keyauth"`
ACMEAudience string `json:"acme_aud"`
}{ }{
Claims: jose.Claims{ Claims: jose.Claims{
Issuer: srv.URL, Issuer: srv.URL,
@ -1847,6 +1910,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
Name: "Alice Smith", Name: "Alice Smith",
PreferredUsername: "wireapp://%40alice_wire@wire.com", PreferredUsername: "wireapp://%40alice_wire@wire.com",
KeyAuth: keyAuth, KeyAuth: keyAuth,
ACMEAudience: "https://ca.example.com/acme/wire/challenge/azID/chID",
}) })
require.NoError(t, err) require.NoError(t, err)
signed, err := signer.Sign(tokenBytes) signed, err := signer.Sign(tokenBytes)
@ -1891,6 +1955,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}, },
}, },
})) }))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{ return test{
ch: &Challenge{ ch: &Challenge{
ID: "chID", ID: "chID",
@ -1954,6 +2019,7 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
} }
func Test_parseAndVerifyWireAccessToken(t *testing.T) { func Test_parseAndVerifyWireAccessToken(t *testing.T) {
t.Skip("skip until we can retrieve public key from e2e test, so that we can actually verify the token")
key := ` key := `
-----BEGIN PUBLIC KEY----- -----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA= MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
@ -1969,7 +2035,7 @@ MCowBQYDK2VwAyEAB2IYqBWXAouDt3WcCZgCM3t9gumMEKMlgMsGenSu+fA=
Handle: "wireapp://%40alice_wire@wire.com", Handle: "wireapp://%40alice_wire@wire.com",
} }
token := `eyJhbGciOiJFZERTQSIsInR5cCI6ImF0K2p3dCIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6IkIySVlxQldYQW91RHQzV2NDWmdDTTN0OWd1bU1FS01sZ01zR2VuU3UtZkEifX0.eyJpYXQiOjE3MDQ5ODUyMDUsImV4cCI6MTcwNDk4OTE2NSwibmJmIjoxNzA0OTg1MjA1LCJpc3MiOiJodHRwOi8vd2lyZS5jb206MTk5ODMvY2xpZW50cy83YTQxY2Y1Yjc5NjgzNDEwL2FjY2Vzcy10b2tlbiIsInN1YiI6IndpcmVhcHA6Ly9ndVZYNXhlRlMzZVRhdG1YQkl5QTRBITdhNDFjZjViNzk2ODM0MTBAd2lyZS5jb20iLCJhdWQiOiJodHRwOi8vd2lyZS5jb206MTk5ODMvY2xpZW50cy83YTQxY2Y1Yjc5NjgzNDEwL2FjY2Vzcy10b2tlbiIsImp0aSI6IjQyYzQ2ZDRjLWU1MTAtNDE3NS05ZmI1LWQwNTVlMTI1YTQ5ZCIsIm5vbmNlIjoiVUVKeVIyZHFPRWh6WkZKRVlXSkJhVGt5T0RORVlURTJhRXMwZEhJeGNFYyIsImNoYWwiOiJiWFVHTnBVZmNSeDNFaEIzNHhQM3k2MmFRWm9HWlM2aiIsImNuZiI6eyJraWQiOiJvTVdmTkRKUXNJNWNQbFhONVVvQk5uY0t0YzRmMmRxMnZ3Q2pqWHNxdzdRIn0sInByb29mIjoiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNkltUndiM0FyYW5kMElpd2lhbmRySWpwN0ltdDBlU0k2SWs5TFVDSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSjRJam9pTVV3eFpVZ3lZVFpCWjFaMmVsUndOVnBoYkV0U1puRTJjRlpRVDNSRmFrazNhRGhVVUhwQ1dVWm5UU0o5ZlEuZXlKcFlYUWlPakUzTURRNU9EVXlNRFVzSW1WNGNDSTZNVGN3TkRrNU1qUXdOU3dpYm1KbUlqb3hOekEwT1RnMU1qQTFMQ0p6ZFdJaU9pSjNhWEpsWVhCd09pOHZaM1ZXV0RWNFpVWlRNMlZVWVhSdFdFSkplVUUwUVNFM1lUUXhZMlkxWWpjNU5qZ3pOREV3UUhkcGNtVXVZMjl0SWl3aWFuUnBJam9pTldVMk5qZzBZMkl0Tm1JME9DMDBOamhrTFdJd09URXRabVl3TkdKbFpEWmxZekpsSWl3aWJtOXVZMlVpT2lKVlJVcDVVakprY1U5RmFIcGFSa3BGV1ZkS1FtRlVhM2xQUkU1RldWUkZNbUZGY3pCa1NFbDRZMFZqSWl3aWFIUnRJam9pVUU5VFZDSXNJbWgwZFNJNkltaDBkSEE2THk5M2FYSmxMbU52YlRveE9UazRNeTlqYkdsbGJuUnpMemRoTkRGalpqVmlOemsyT0RNME1UQXZZV05qWlhOekxYUnZhMlZ1SWl3aVkyaGhiQ0k2SW1KWVZVZE9jRlZtWTFKNE0wVm9Rak0wZUZBemVUWXlZVkZhYjBkYVV6WnFJaXdpYUdGdVpHeGxJam9pZDJseVpXRndjRG92THlVME1HRnNhV05sWDNkcGNtVkFkMmx5WlM1amIyMGlMQ0owWldGdElqb2lkMmx5WlNKOS52bkN1T2JURFRLVFhCYXpyX3Z2X0xyZDBZT1Rac2xteHQtM2xKNWZKSU9iRVRidUVCTGlEaS1JVWZHcFJHTm1Dbm9IZjVocHNsWW5HeFMzSjloUmVDZyIsImNsaWVudF9pZCI6IndpcmVhcHA6Ly9ndVZYNXhlRlMzZVRhdG1YQkl5QTRBITdhNDFjZjViNzk2ODM0MTBAd2lyZS5jb20iLCJhcGlfdmVyc2lvbiI6NSwic2NvcGUiOiJ3aXJlX2NsaWVudF9pZCJ9.uCVYhmvCJm7nM1NxJQKl_XZJcSqm9eFmNmbRJkA5Wpsw70ZF1YANYC9nQ91QgsnuAbaRZMJiJt3P8ZntR2ozDQ` token := `eyJhbGciOiJFZERTQSIsInR5cCI6ImF0K2p3dCIsImp3ayI6eyJrdHkiOiJPS1AiLCJjcnYiOiJFZDI1NTE5IiwieCI6Im8zcWZhQ045a2FzSnZJRlhPdFNMTGhlYW0wTE5jcVF5MHdBMk9PeFRRNW8ifX0.eyJpYXQiOjE3MDU0OTc3MzksImV4cCI6MTcwNTUwMTY5OSwibmJmIjoxNzA1NDk3NzM5LCJpc3MiOiJodHRwOi8vd2lyZS5jb206MTY4MjQvY2xpZW50cy8zN2ZlOThiZDQwZDBkZmUvYWNjZXNzLXRva2VuIiwic3ViIjoid2lyZWFwcDovLzE4NXdIUmtRVHdTOTVGODhaZTQ1SlEhMzdmZTk4YmQ0MGQwZGZlQHdpcmUuY29tIiwiYXVkIjoiaHR0cHM6Ly9zdGVwY2E6NTUwMjMvYWNtZS93aXJlL2NoYWxsZW5nZS9SeEdSWGVoRGxCcHcxNTJQTVUzem0xY2M0cEtGcHVWRi9RWnRFazdQNUVFRXhadHBSYngydjVoYlc3QXB1S2NOSSIsImp0aSI6ImU1MzllODYzLTRkNTgtNGMwMS1iYjk3LTYwODdiNTEzOWIyMCIsIm5vbmNlIjoiUzJKYWVWcExkV28wUkZKaFFrWndXR0ZKY0VoVlFrNUxXVGd4WkhkRFVqQSIsImNoYWwiOiIyaDFPdUdxbTBKUXd6bHVsWGtLSTJEMGZiRDgzRUIxdyIsImNuZiI6eyJraWQiOiJhSEY3MVhYeG0tTWE5Q05zSjNaU1RKTjlYS0ZxOFFmOGh2UTJLN3NLQmQ4In0sInByb29mIjoiZXlKaGJHY2lPaUpGWkVSVFFTSXNJblI1Y0NJNkltUndiM0FyYW5kMElpd2lhbmRySWpwN0ltdDBlU0k2SWs5TFVDSXNJbU55ZGlJNklrVmtNalUxTVRraUxDSjRJam9pWVVsaVMwcFBha0poWXpZeVF6TnRhVmhHVjAxb09ITTJkRXQzUkROaGNHRnVSMHBQZURaVVFYVklRU0o5ZlEuZXlKcFlYUWlPakUzTURVME9UYzNNemtzSW1WNGNDSTZNVGN3TlRVd05Ea3pPU3dpYm1KbUlqb3hOekExTkRrM056TTVMQ0p6ZFdJaU9pSjNhWEpsWVhCd09pOHZNVGcxZDBoU2ExRlVkMU01TlVZNE9GcGxORFZLVVNFek4yWmxPVGhpWkRRd1pEQmtabVZBZDJseVpTNWpiMjBpTENKaGRXUWlPaUpvZEhSd2N6b3ZMM04wWlhCallUbzFOVEF5TXk5aFkyMWxMM2RwY21VdlkyaGhiR3hsYm1kbEwxSjRSMUpZWldoRWJFSndkekUxTWxCTlZUTjZiVEZqWXpSd1MwWndkVlpHTDFGYWRFVnJOMUExUlVWRmVGcDBjRkppZURKMk5XaGlWemRCY0hWTFkwNUpJaXdpYW5ScElqb2lNV1kxTUdRM1lUQXRaamt6WmkwME5XWXdMV0V3TWpBdE1ETm1NREJpTlRreVlUUmtJaXdpYm05dVkyVWlPaUpUTWtwaFpWWndUR1JYYnpCU1JrcG9VV3RhZDFkSFJrcGpSV2hXVVdzMVRGZFVaM2hhU0dSRVZXcEJJaXdpYUhSdElqb2lVRTlUVkNJc0ltaDBkU0k2SW1oMGRIQTZMeTkzYVhKbExtTnZiVG94TmpneU5DOWpiR2xsYm5Sekx6TTNabVU1T0dKa05EQmtNR1JtWlM5aFkyTmxjM010ZEc5clpXNGlMQ0pqYUdGc0lqb2lNbWd4VDNWSGNXMHdTbEYzZW14MWJGaHJTMGt5UkRCbVlrUTRNMFZDTVhjaUxDSm9ZVzVrYkdVaU9pSjNhWEpsWVhCd09pOHZKVFF3WVd4cFkyVmZkMmx5WlVCM2FYSmxMbU52YlNJc0luUmxZVzBpT2lKM2FYSmxJbjAuZlNmQnFuWWlfMTRhZEc5MDAyZ0RJdEgybXNyYW55eXVnR0g5bHpFcmprdmRGbkRPOFRVWWRYUXJKUzdlX3BlU0lzcGxlRUVkaGhzc0gwM3FBWHY2QXciLCJjbGllbnRfaWQiOiJ3aXJlYXBwOi8vMTg1d0hSa1FUd1M5NUY4OFplNDVKUSEzN2ZlOThiZDQwZDBkZmVAd2lyZS5jb20iLCJhcGlfdmVyc2lvbiI6NSwic2NvcGUiOiJ3aXJlX2NsaWVudF9pZCJ9.GKK7ZsJ8EWJjeaHqf8P48H9mluJhxyXUmI0FO3xstda3XDJIK7Z5Ur4hi1OIJB0ZsS5BqRVT2q5whL4KP9hZCA`
ch := &Challenge{ ch := &Challenge{
Token: "bXUGNpUfcRx3EhB34xP3y62aQZoGZS6j", Token: "bXUGNpUfcRx3EhB34xP3y62aQZoGZS6j",
} }

Loading…
Cancel
Save