Refactor OIDC verifier instantation to happen only once

pull/1691/head
Herman Slatman 5 months ago
parent 19feae520b
commit cd21f8d51f
No known key found for this signature in database
GPG Key ID: F4D8A44EA0A75A4F

@ -383,7 +383,11 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO
}
oidcOptions := wireOptions.GetOIDCOptions()
verifier := oidcOptions.GetProvider(ctx).Verifier(oidcOptions.GetConfig())
verifier, err := oidcOptions.GetVerifier(ctx)
if err != nil {
return WrapErrorISE(err, "no OIDC verifier available")
}
idToken, err := verifier.Verify(ctx, oidcPayload.IDToken)
if err != nil {
return storeError(ctx, db, ch, true, WrapError(ErrorRejectedIdentifierType, err,

@ -47,7 +47,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"fail/no-linker": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
return test{
ctx: ctx,
expectedErr: &Error{
@ -59,7 +77,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"fail/unmarshal": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{
ctx: ctx,
@ -82,7 +118,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"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{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{
ctx: ctx,
@ -105,7 +159,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"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{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
valueBytes, err := json.Marshal(struct {
Name string `json:"name,omitempty"`
@ -139,41 +211,6 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
},
}
},
"fail/no-wire-options": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
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)
return test{
ctx: ctx,
payload: []byte("{}"),
ch: &Challenge{
ID: "chID",
AuthorizationID: "azID",
AccountID: "accID",
Token: "token",
Type: "wire-dpop-01",
Status: StatusPending,
Value: string(valueBytes),
},
expectedErr: &Error{
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New(`failed getting Wire options: no Wire options available`),
},
}
},
"fail/parse-and-verify": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
@ -1037,7 +1074,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"fail/no-linker": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
return test{
ctx: ctx,
expectedErr: &Error{
@ -1049,7 +1104,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"fail/unmarshal": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{
ctx: ctx,
@ -1078,7 +1151,25 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
}
},
"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{
Wire: &wireprovisioner.Options{
OIDC: &wireprovisioner.OIDCOptions{
Provider: &wireprovisioner.Provider{
IssuerURL: "https://issuer.example.com",
Algorithms: []string{"ES256"},
},
Config: &wireprovisioner.Config{
ClientID: "test",
SignatureAlgorithms: []string{"ES256"},
Now: time.Now,
},
TransformTemplate: "",
},
DPOP: &wireprovisioner.DPOPOptions{
SigningKey: []byte(fakeKey),
},
},
}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
return test{
ctx: ctx,
@ -1100,41 +1191,6 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
},
}
},
"fail/no-wire-options": func(t *testing.T) test {
ctx := NewProvisionerContext(context.Background(), newWireProvisionerWithOptions(t, &provisioner.Options{}))
ctx = NewLinkerContext(ctx, NewLinker("ca.example.com", "acme"))
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)
return test{
ctx: ctx,
payload: []byte("{}"),
ch: &Challenge{
ID: "chID",
AuthorizationID: "azID",
AccountID: "accID",
Token: "token",
Type: "wire-oidc-01",
Status: StatusPending,
Value: string(valueBytes),
},
expectedErr: &Error{
Type: "urn:ietf:params:acme:error:serverInternal",
Detail: "The server experienced an internal error",
Status: 500,
Err: errors.New(`failed getting Wire options: no Wire options available`),
},
}
},
"fail/verify": func(t *testing.T) test {
jwk, keyAuth := mustAccountAndKeyAuthorization(t, "token")
signerJWK, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0)
@ -2122,8 +2178,8 @@ MCowBQYDK2VwAyEA5c+4NKZSNQcR1T8qN6SjwgdPZQ0Ge12Ylx/YeGAJ35k=
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)
verifier, err := o.GetVerifier(ctx)
require.NoError(t, err)
idToken, err := verifier.Verify(ctx, idTokenString)
require.NoError(t, err)

@ -40,19 +40,22 @@ type OIDCOptions struct {
Config *Config `json:"config,omitempty"`
TransformTemplate string `json:"transform,omitempty"`
oidcProviderConfig *oidc.ProviderConfig
target *template.Template
transform *template.Template
oidcProviderConfig *oidc.ProviderConfig
verifier *oidc.IDTokenVerifier
}
func (o *OIDCOptions) GetProvider(ctx context.Context) *oidc.Provider {
if o == nil || o.Provider == nil || o.oidcProviderConfig == nil {
return nil
func (o *OIDCOptions) GetVerifier(ctx context.Context) (*oidc.IDTokenVerifier, error) {
if o.verifier == nil {
provider := o.oidcProviderConfig.NewProvider(ctx) // TODO: support the OIDC discovery flow
o.verifier = provider.Verifier(o.getConfig())
}
return o.oidcProviderConfig.NewProvider(ctx)
return o.verifier, nil
}
func (o *OIDCOptions) GetConfig() *oidc.Config {
func (o *OIDCOptions) getConfig() *oidc.Config {
if o == nil || o.Config == nil {
return &oidc.Config{}
}
@ -105,6 +108,9 @@ func parseTransform(transformTemplate string) (*template.Template, error) {
}
func (o *OIDCOptions) EvaluateTarget(deviceID string) (string, error) {
if deviceID == "" {
return "", errors.New("deviceID must not be empty")
}
buf := new(bytes.Buffer)
if err := o.target.Execute(buf, struct{ DeviceID string }{DeviceID: deviceID}); err != nil {
return "", fmt.Errorf("failed executing OIDC template: %w", err)

Loading…
Cancel
Save