Skip to content

Commit 3b75276

Browse files
committed
[papi] OIDC service signs state with RSA256
1 parent 0846e66 commit 3b75276

File tree

3 files changed

+34
-65
lines changed

3 files changed

+34
-65
lines changed

components/public-api-server/pkg/oidc/service.go

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@ import (
1414
"io"
1515
"io/ioutil"
1616
"net/http"
17+
"time"
1718

1819
"github.com/coreos/go-oidc/v3/oidc"
1920
goidc "github.com/coreos/go-oidc/v3/oidc"
2021
"github.com/gitpod-io/gitpod/common-go/log"
2122
db "github.com/gitpod-io/gitpod/components/gitpod-db/go"
23+
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
2224
"github.com/google/uuid"
2325
"golang.org/x/oauth2"
2426
"google.golang.org/grpc/codes"
@@ -27,11 +29,13 @@ import (
2729
)
2830

2931
type Service struct {
30-
dbConn *gorm.DB
31-
cipher db.Cipher
32-
stateJWT *StateJWT
32+
dbConn *gorm.DB
33+
cipher db.Cipher
34+
35+
// jwts
36+
stateExpiry time.Duration
37+
signerVerifier jws.SignerVerifier
3338

34-
// verifierByIssuer map[string]*goidc.IDTokenVerifier
3539
sessionServiceAddress string
3640

3741
// TODO(at) remove by enhancing test setups
@@ -57,14 +61,15 @@ type AuthFlowResult struct {
5761
Claims map[string]interface{} `json:"claims"`
5862
}
5963

60-
func NewService(sessionServiceAddress string, dbConn *gorm.DB, cipher db.Cipher, stateJWT *StateJWT) *Service {
64+
func NewService(sessionServiceAddress string, dbConn *gorm.DB, cipher db.Cipher, signerVerifier jws.SignerVerifier, stateExpiry time.Duration) *Service {
6165
return &Service{
6266
sessionServiceAddress: sessionServiceAddress,
6367

6468
dbConn: dbConn,
6569
cipher: cipher,
6670

67-
stateJWT: stateJWT,
71+
signerVerifier: signerVerifier,
72+
stateExpiry: stateExpiry,
6873
}
6974
}
7075

@@ -98,18 +103,24 @@ func (s *Service) GetStartParams(config *ClientConfig, redirectURL string, retur
98103
}
99104

100105
func (s *Service) encodeStateParam(state StateParam) (string, error) {
101-
encodedState, err := s.stateJWT.Encode(StateClaims{
102-
ClientConfigID: state.ClientConfigID,
103-
ReturnToURL: state.ReturnToURL,
104-
})
105-
return encodedState, err
106+
now := time.Now().UTC()
107+
expiry := now.Add(s.stateExpiry)
108+
token := NewStateJWT(state.ClientConfigID, state.ReturnToURL, now, expiry)
109+
110+
signed, err := s.signerVerifier.Sign(token)
111+
if err != nil {
112+
return "", fmt.Errorf("failed to sign jwt: %w", err)
113+
}
114+
return signed, nil
106115
}
107116

108117
func (s *Service) decodeStateParam(encodedToken string) (StateParam, error) {
109-
claims, err := s.stateJWT.Decode(encodedToken)
118+
claims := &StateClaims{}
119+
_, err := s.signerVerifier.Verify(encodedToken, claims)
110120
if err != nil {
111-
return StateParam{}, err
121+
return StateParam{}, fmt.Errorf("failed to verify state token: %w", err)
112122
}
123+
113124
return StateParam{
114125
ClientConfigID: claims.ClientConfigID,
115126
ReturnToURL: claims.ReturnToURL,

components/public-api-server/pkg/oidc/state_jwt.go

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,24 +10,6 @@ import (
1010
"github.com/golang-jwt/jwt/v5"
1111
)
1212

13-
type StateJWT struct {
14-
key []byte
15-
expiresIn time.Duration
16-
}
17-
18-
func NewStateJWT(key []byte) *StateJWT {
19-
return &StateJWT{
20-
key: key,
21-
expiresIn: 5 * time.Minute,
22-
}
23-
}
24-
25-
func newTestStateJWT(key []byte, expiresIn time.Duration) *StateJWT {
26-
thing := NewStateJWT(key)
27-
thing.expiresIn = expiresIn
28-
return thing
29-
}
30-
3113
type StateClaims struct {
3214
// Internal client ID
3315
ClientConfigID string `json:"clientId"`
@@ -36,26 +18,13 @@ type StateClaims struct {
3618
jwt.RegisteredClaims
3719
}
3820

39-
func (s *StateJWT) Encode(claims StateClaims) (string, error) {
40-
41-
expirationTime := time.Now().Add(s.expiresIn)
42-
claims.ExpiresAt = jwt.NewNumericDate(expirationTime)
43-
44-
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
45-
encodedToken, err := token.SignedString(s.key)
46-
47-
return encodedToken, err
48-
}
49-
50-
func (s *StateJWT) Decode(tokenString string) (*StateClaims, error) {
51-
claims := &StateClaims{}
52-
_, err := jwt.ParseWithClaims(
53-
tokenString,
54-
claims,
55-
func(token *jwt.Token) (interface{}, error) {
56-
return []byte(s.key), nil
21+
func NewStateJWT(clientConfigID string, returnURL string, issuedAt, expiry time.Time) *jwt.Token {
22+
return jwt.NewWithClaims(jwt.SigningMethodRS256, &StateClaims{
23+
ClientConfigID: clientConfigID,
24+
ReturnToURL: returnURL,
25+
RegisteredClaims: jwt.RegisteredClaims{
26+
ExpiresAt: jwt.NewNumericDate(expiry),
27+
IssuedAt: jwt.NewNumericDate(issuedAt),
5728
},
58-
)
59-
60-
return claims, err
29+
})
6130
}

components/public-api-server/pkg/server/server.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,11 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
9797
if err != nil {
9898
return fmt.Errorf("failed to setup JWS Keyset: %w", err)
9999
}
100-
_, err = jws.NewRSA256(keyset)
100+
rsa256, err := jws.NewRSA256(keyset)
101101
if err != nil {
102102
return fmt.Errorf("failed to setup jws.RSA256: %w", err)
103103
}
104104

105-
var stateJWT *oidc.StateJWT
106-
if cfg.OIDCClientJWTSigningSecretPath != "" {
107-
oidcClientJWTSigningSecret, err := readSecretFromFile(cfg.OIDCClientJWTSigningSecretPath)
108-
if err != nil {
109-
return fmt.Errorf("failed to read JWT signing secret for OIDC flows: %w", err)
110-
}
111-
stateJWT = oidc.NewStateJWT([]byte(oidcClientJWTSigningSecret))
112-
} else {
113-
log.Info("No JWT signing secret for OIDC flows is configured.")
114-
}
115-
116105
var stripeWebhookHandler http.Handler = webhooks.NewNoopWebhookHandler()
117106
if cfg.StripeWebhookSigningSecretPath != "" {
118107
stripeWebhookSecret, err := readSecretFromFile(cfg.StripeWebhookSigningSecretPath)
@@ -138,7 +127,7 @@ func Start(logger *logrus.Entry, version string, cfg *config.Configuration) erro
138127

139128
srv.HTTPMux().Handle("/stripe/invoices/webhook", handlers.ContentTypeHandler(stripeWebhookHandler, "application/json"))
140129

141-
oidcService := oidc.NewService(cfg.SessionServiceAddress, dbConn, cipherSet, stateJWT)
130+
oidcService := oidc.NewService(cfg.SessionServiceAddress, dbConn, cipherSet, rsa256, 5*time.Minute)
142131

143132
if redisClient == nil {
144133
return fmt.Errorf("no Redis configiured")

0 commit comments

Comments
 (0)