diff --git a/acme/challenge.go b/acme/challenge.go index b3a694e7..48aaf2de 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -431,6 +431,25 @@ func wireOIDC01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO if err = db.UpdateChallenge(ctx, ch); err != nil { return WrapErrorISE(err, "error updating challenge") } + + parsedIdToken, err := jwt.ParseSigned(wireChallengePayload.IdToken) + if err != nil { + return WrapErrorISE(err, "Invalid OIDC id token") + } + oidcToken := make(map[string]interface{}) + if err := parsedIdToken.UnsafeClaimsWithoutVerification(&oidcToken); err != nil { + return WrapErrorISE(err, "Failed parsing OIDC id token") + } + + orders, err := db.GetAllOrdersByAccountID(ctx, ch.AccountID) + if err != nil { + return WrapErrorISE(err, "Could not find current order by account id") + } + + if err := db.CreateOidcToken(ctx, orders[0], oidcToken); err != nil { + return WrapErrorISE(err, "Failed storing OIDC id token") + } + return nil } @@ -568,7 +587,7 @@ func wireDPOP01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSO return WrapErrorISE(err, "Could not find current order by account id") } - if err := db.CreateDpop(ctx, orders[0], dpop); err != nil { + if err := db.CreateDpopToken(ctx, orders[0], dpop); err != nil { return WrapErrorISE(err, "Failed storing DPoP token") } diff --git a/acme/db.go b/acme/db.go index b514ce2e..7065286a 100644 --- a/acme/db.go +++ b/acme/db.go @@ -55,8 +55,10 @@ type DB interface { GetAllOrdersByAccountID(ctx context.Context, accountID string) ([]string, error) UpdateOrder(ctx context.Context, o *Order) error - CreateDpop(ctx context.Context, orderId string, dpop map[string]interface{}) error - GetDpop(ctx context.Context, orderId string) (map[string]interface{}, error) + CreateDpopToken(ctx context.Context, orderId string, dpop map[string]interface{}) error + GetDpopToken(ctx context.Context, orderId string) (map[string]interface{}, error) + CreateOidcToken(ctx context.Context, orderId string, idToken map[string]interface{}) error + GetOidcToken(ctx context.Context, orderId string) (map[string]interface{}, error) } type dbKey struct{} diff --git a/acme/db/nosql/nosql.go b/acme/db/nosql/nosql.go index 5a0cb1f6..dc11584b 100644 --- a/acme/db/nosql/nosql.go +++ b/acme/db/nosql/nosql.go @@ -23,7 +23,8 @@ var ( externalAccountKeyTable = []byte("acme_external_account_keys") externalAccountKeyIDsByReferenceTable = []byte("acme_external_account_keyID_reference_index") externalAccountKeyIDsByProvisionerIDTable = []byte("acme_external_account_keyID_provisionerID_index") - dpopTable = []byte("acme_dpop") + dpopTokenTable = []byte("acme_dpop_token") + oidcTokenTable = []byte("oidc_token") ) // DB is a struct that implements the AcmeDB interface. diff --git a/acme/db/nosql/wire.go b/acme/db/nosql/wire.go index 093c1d0c..5c659579 100644 --- a/acme/db/nosql/wire.go +++ b/acme/db/nosql/wire.go @@ -10,30 +10,31 @@ import ( "github.com/smallstep/nosql" ) -type dbDpop struct { +type dbDpopToken struct { ID string `json:"id"` Content []byte `json:"content"` CreatedAt time.Time `json:"createdAt"` } -// getDBDpop retrieves and unmarshals an DPoP type from the database. -func (db *DB) getDBDpop(ctx context.Context, orderId string) (*dbDpop, error) { - b, err := db.db.Get(dpopTable, []byte(orderId)) +// getDBDpopToken retrieves and unmarshals an DPoP type from the database. +func (db *DB) getDBDpopToken(ctx context.Context, orderId string) (*dbDpopToken, error) { + b, err := db.db.Get(dpopTokenTable, []byte(orderId)) if nosql.IsErrNotFound(err) { return nil, acme.NewError(acme.ErrorMalformedType, "dpop %s not found", orderId) } else if err != nil { return nil, errors.Wrapf(err, "error loading dpop %s", orderId) } - o := new(dbDpop) - if err := json.Unmarshal(b, &o); err != nil { - return nil, errors.Wrapf(err, "error unmarshaling dpop %s into dbDpop", orderId) + + d := new(dbDpopToken) + if err := json.Unmarshal(b, &d); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling dpop %s into dbDpopToken", orderId) } - return o, nil + return d, nil } -// GetDpop retrieves an DPoP from the database. -func (db *DB) GetDpop(ctx context.Context, orderId string) (map[string]interface{}, error) { - dbDpop, err := db.getDBDpop(ctx, orderId) +// GetDpopToken retrieves an DPoP from the database. +func (db *DB) GetDpopToken(ctx context.Context, orderId string) (map[string]interface{}, error) { + dbDpop, err := db.getDBDpopToken(ctx, orderId) if err != nil { return nil, err } @@ -44,20 +45,73 @@ func (db *DB) GetDpop(ctx context.Context, orderId string) (map[string]interface return dpop, err } -// CreateDpop creates DPoP resources and saves them to the DB. -func (db *DB) CreateDpop(ctx context.Context, orderId string, dpop map[string]interface{}) error { +// CreateDpopToken creates DPoP resources and saves them to the DB. +func (db *DB) CreateDpopToken(ctx context.Context, orderId string, dpop map[string]interface{}) error { content, err := json.Marshal(dpop) if err != nil { return err } now := clock.Now() - dbDpop := &dbDpop{ + dbDpop := &dbDpopToken{ ID: orderId, Content: content, CreatedAt: now, } - if err := db.save(ctx, orderId, dbDpop, nil, "dpop", dpopTable); err != nil { + if err := db.save(ctx, orderId, dbDpop, nil, "dpop", dpopTokenTable); err != nil { + return err + } + return nil +} + +type dbOidcToken struct { + ID string `json:"id"` + Content []byte `json:"content"` + CreatedAt time.Time `json:"createdAt"` +} + +// getDBOidcToken retrieves and unmarshals an OIDC id token type from the database. +func (db *DB) getDBOidcToken(ctx context.Context, orderId string) (*dbOidcToken, error) { + b, err := db.db.Get(oidcTokenTable, []byte(orderId)) + if nosql.IsErrNotFound(err) { + return nil, acme.NewError(acme.ErrorMalformedType, "oidc token %s not found", orderId) + } else if err != nil { + return nil, errors.Wrapf(err, "error loading oidc token %s", orderId) + } + o := new(dbOidcToken) + if err := json.Unmarshal(b, &o); err != nil { + return nil, errors.Wrapf(err, "error unmarshaling oidc token %s into dbOidcToken", orderId) + } + return o, nil +} + +// GetOidcToken retrieves an oidc token from the database. +func (db *DB) GetOidcToken(ctx context.Context, orderId string) (map[string]interface{}, error) { + dbOidc, err := db.getDBOidcToken(ctx, orderId) + if err != nil { + return nil, err + } + + idToken := make(map[string]interface{}) + err = json.Unmarshal(dbOidc.Content, &idToken) + + return idToken, err +} + +// CreateOidcToken creates oidc token resources and saves them to the DB. +func (db *DB) CreateOidcToken(ctx context.Context, orderId string, idToken map[string]interface{}) error { + content, err := json.Marshal(idToken) + if err != nil { + return err + } + + now := clock.Now() + dbOidc := &dbOidcToken{ + ID: orderId, + Content: content, + CreatedAt: now, + } + if err := db.save(ctx, orderId, dbOidc, nil, "oidc", oidcTokenTable); err != nil { return err } return nil diff --git a/acme/order.go b/acme/order.go index d6bc211b..fca9482a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -210,43 +210,11 @@ func (o *Order) Finalize(ctx context.Context, db DB, csr *x509.CertificateReques } data.SetSubject(subject) - dpop, err := db.GetDpop(ctx, o.ID) - if err != nil { - return err - } + // Inject Wire's custom challenges into the template once they have been validated + dpop, err := db.GetDpopToken(ctx, o.ID) data.Set("Dpop", dpop) - - // inject the raw access token as template variable - /* - log.Printf(">> json raw content %v", ctx.Value(wire.AccessTokenKey{})) - access, ok := ctx.Value(wire.AccessTokenKey{}).(map[string]interface{}) - if !ok { - return WrapErrorISE(err, "Invalid or absent access in context") - } - if access == nil { - return WrapErrorISE(err, "Access was nil in context") - }*/ - - /*// inject the raw dpop token as template variable - dpop, ok := ctx.Value("dpop").(map[string]interface{}) - if !ok { - return WrapErrorISE(err, "Invalid or absent dpop in context") - } - data.Set("dpop", dpop)*/ - - // inject the raw access token as template variable - /*access, ok := ctx.Value("access").(map[string]interface{}) - if !ok { - return WrapErrorISE(err, "Invalid or absent access in context") - } - data.Set("access", access)*/ - - /*// inject the raw OIDC id token as template variable - oidc, ok := ctx.Value("oidc").(map[string]interface{}) - if !ok { - return WrapErrorISE(err, "Invalid or absent oidc in context") - } - data.Set("oidc", oidc)*/ + oidc, err := db.GetOidcToken(ctx, o.ID) + data.Set("Oidc", oidc) // Custom sign options passed to authority.Sign var extraOptions []provisioner.SignOption