Skip to content

Commit d069f76

Browse files
authored
[public-api] Refactor JWT Sign/Verify to be reusable for OIDC - WEB-206 (#17327)
* [public-api] Refactor JWT Sign/Verify to be reusable for OIDC * fix
1 parent 90b7d65 commit d069f76

File tree

10 files changed

+424
-251
lines changed

10 files changed

+424
-251
lines changed

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

Lines changed: 0 additions & 170 deletions
This file was deleted.

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

Lines changed: 0 additions & 76 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package auth
6+
7+
import (
8+
"fmt"
9+
"time"
10+
11+
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
12+
"github.com/golang-jwt/jwt/v5"
13+
"github.com/google/uuid"
14+
)
15+
16+
type SessionClaims struct {
17+
jwt.RegisteredClaims
18+
}
19+
20+
func NewSessionJWT(subject uuid.UUID, issuer string, issuedAt, expiry time.Time) *jwt.Token {
21+
return jwt.NewWithClaims(jwt.SigningMethodRS256, &SessionClaims{
22+
RegisteredClaims: jwt.RegisteredClaims{
23+
Issuer: issuer,
24+
Subject: subject.String(),
25+
IssuedAt: jwt.NewNumericDate(issuedAt),
26+
ExpiresAt: jwt.NewNumericDate(expiry),
27+
},
28+
})
29+
}
30+
31+
func VerifySessionJWT(token string, verifier jws.Verifier, expectedIssuer string) (*SessionClaims, error) {
32+
parsed, err := verifier.Verify(token, &SessionClaims{}, jwt.WithIssuer(expectedIssuer))
33+
if err != nil {
34+
return nil, fmt.Errorf("failed to parse jwt: %w", err)
35+
}
36+
37+
claims, ok := parsed.Claims.(*SessionClaims)
38+
if !ok {
39+
return nil, fmt.Errorf("unknown jwt claims: %w", jwt.ErrTokenInvalidClaims)
40+
}
41+
42+
return claims, nil
43+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package auth
6+
7+
import (
8+
"testing"
9+
"time"
10+
11+
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
12+
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws/jwstest"
13+
"github.com/golang-jwt/jwt/v5"
14+
"github.com/google/uuid"
15+
"github.com/stretchr/testify/require"
16+
)
17+
18+
func TestNewSessionJWT(t *testing.T) {
19+
var (
20+
subject = uuid.New()
21+
issuer = "some-issuer"
22+
issuedAt = time.Now()
23+
expiry = issuedAt.Add(time.Hour)
24+
)
25+
token := NewSessionJWT(subject, issuer, issuedAt, expiry)
26+
require.Equal(t, &SessionClaims{
27+
RegisteredClaims: jwt.RegisteredClaims{
28+
Issuer: issuer,
29+
Subject: subject.String(),
30+
ExpiresAt: jwt.NewNumericDate(expiry),
31+
IssuedAt: jwt.NewNumericDate(issuedAt),
32+
},
33+
}, token.Claims)
34+
}
35+
36+
func TestVerifySessionJWT(t *testing.T) {
37+
var (
38+
subject = uuid.New()
39+
issuer = "some-issuer"
40+
issuedAt = time.Now()
41+
expiry = issuedAt.Add(time.Hour)
42+
)
43+
token := NewSessionJWT(subject, issuer, issuedAt, expiry)
44+
45+
keyset := jwstest.GenerateKeySet(t)
46+
rsa256, err := jws.NewRSA256(keyset)
47+
require.NoError(t, err)
48+
49+
signed, err := rsa256.Sign(token)
50+
require.NoError(t, err)
51+
52+
claims, err := VerifySessionJWT(signed, rsa256, issuer)
53+
require.NoError(t, err)
54+
require.Equal(t, &SessionClaims{
55+
RegisteredClaims: jwt.RegisteredClaims{
56+
Issuer: issuer,
57+
Subject: subject.String(),
58+
ExpiresAt: jwt.NewNumericDate(expiry),
59+
IssuedAt: jwt.NewNumericDate(issuedAt),
60+
},
61+
}, claims)
62+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
2+
// Licensed under the GNU Affero General Public License (AGPL).
3+
// See License.AGPL.txt in the project root for license information.
4+
5+
package jwstest
6+
7+
import (
8+
"crypto/rand"
9+
"crypto/rsa"
10+
"testing"
11+
12+
"github.com/gitpod-io/gitpod/public-api-server/pkg/jws"
13+
"github.com/stretchr/testify/require"
14+
)
15+
16+
func GenerateKeySet(t *testing.T) jws.KeySet {
17+
return jws.KeySet{
18+
Signing: GenerateRSAPrivateKey(t, "0001"),
19+
Validating: []jws.Key{
20+
GenerateRSAPrivateKey(t, "0002"),
21+
GenerateRSAPrivateKey(t, "0003"),
22+
},
23+
}
24+
}
25+
26+
func GenerateRSAPrivateKey(t *testing.T, id string) jws.Key {
27+
t.Helper()
28+
29+
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
30+
require.NoError(t, err)
31+
return jws.Key{
32+
ID: id,
33+
Private: privateKey,
34+
}
35+
}

0 commit comments

Comments
 (0)