summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/cfg/cfg.go2
-rw-r--r--app/controllers/sessions/controller.go15
-rw-r--r--app/controllers/sessions/controller_test.go54
-rw-r--r--app/middleware/id_token.go2
-rw-r--r--app/middleware/id_token_test.go9
-rw-r--r--app/middleware/token_parser.go13
-rw-r--r--pkg/web/cookie.go44
7 files changed, 42 insertions, 97 deletions
diff --git a/app/cfg/cfg.go b/app/cfg/cfg.go
index d8ca805..1dffa16 100644
--- a/app/cfg/cfg.go
+++ b/app/cfg/cfg.go
@@ -14,9 +14,7 @@ var OAuthClientID string = env.Fetch("OAUTH_CLIENT_ID", "client_id")
var OAuthClientSecret string = env.Fetch("OAUTH_CLIENT_SECRET", "client_secret")
var OAuthRedirectURL string = env.Fetch("OAUTH_REDIRECT_URL", "")
-var SessionCookie string = "__s"
var CSRFCookie string = "__csrf"
-
var IDTokenCookie string = "id_token"
var BearerTokenCookie string = "bearer_token"
var RefreshTokenCookie string = "refresh_token"
diff --git a/app/controllers/sessions/controller.go b/app/controllers/sessions/controller.go
index bf7d813..2853358 100644
--- a/app/controllers/sessions/controller.go
+++ b/app/controllers/sessions/controller.go
@@ -100,22 +100,19 @@ func (c *Controller) Create(w http.ResponseWriter, r *http.Request) {
return
}
- encoded, err := tokens.ToBase64String()
- if err != nil {
- pls.LogError(r.Context(), err)
- w.WriteHeader(http.StatusBadRequest)
- return
- }
-
web.ExpireCookie(w, cfg.CSRFCookie)
- web.WriteCookie(w, web.NewCookie(cfg.SessionCookie, encoded))
+ web.WriteCookie(w, web.NewCookie(cfg.IDTokenCookie, tokens.IDToken.String()))
+ web.WriteCookie(w, web.NewCookie(cfg.BearerTokenCookie, tokens.AccessToken))
+ web.WriteCookie(w, web.NewCookie(cfg.RefreshTokenCookie, tokens.RefreshToken))
c.redirectTo(w, r, "/dashboard")
}
func (c *Controller) Destroy(w http.ResponseWriter, r *http.Request) {
web.ExpireCookie(w, cfg.CSRFCookie)
- web.ExpireCookie(w, cfg.SessionCookie)
+ web.ExpireCookie(w, cfg.IDTokenCookie)
+ web.ExpireCookie(w, cfg.BearerTokenCookie)
+ web.ExpireCookie(w, cfg.RefreshTokenCookie)
c.redirectTo(w, r, "/")
}
diff --git a/app/controllers/sessions/controller_test.go b/app/controllers/sessions/controller_test.go
index 9b701d6..00e3f4e 100644
--- a/app/controllers/sessions/controller_test.go
+++ b/app/controllers/sessions/controller_test.go
@@ -1,9 +1,6 @@
package sessions
import (
- "encoding/base64"
- "encoding/json"
- "fmt"
"net/http"
"net/url"
"testing"
@@ -133,29 +130,28 @@ func TestSessions(t *testing.T) {
return ck
})
- cookie := x.Find(cookies, func(item *http.Cookie) bool {
- return item.Name == xcfg.SessionCookie
- })
- data, err := base64.URLEncoding.DecodeString(web.CookieValueFrom(cookie))
- require.NoError(t, err)
- tokens := map[string]interface{}{}
- require.NoError(t, json.Unmarshal(data, &tokens))
-
t.Run("stores the id token in a session cookie", func(t *testing.T) {
- require.NotEmpty(t, tokens["id_token"])
+ cookie := x.Find(cookies, func(item *http.Cookie) bool {
+ return item.Name == xcfg.IDTokenCookie
+ })
- idToken := srv.Verify(tokens["id_token"].(string))
+ require.NotNil(t, cookie)
+
+ idToken := srv.Verify(cookie.Value)
assert.Equal(t, user.Subject, idToken.Subject)
})
t.Run("stores the access token in a session cookie", func(t *testing.T) {
- assert.NotEmpty(t, tokens["access_token"])
- assert.Equal(t, "bearer", tokens["token_type"])
+ cookie := x.Find(cookies, func(item *http.Cookie) bool {
+ return item.Name == xcfg.BearerTokenCookie
+ })
+
+ require.NotNil(t, cookie)
keypair, err := mockoidc.DefaultKeypair()
require.NoError(t, err)
- token, err := keypair.VerifyJWT(tokens["access_token"].(string), nil)
+ token, err := keypair.VerifyJWT(cookie.Value, nil)
require.NoError(t, err)
sub, err := token.Claims.GetSubject()
@@ -164,12 +160,15 @@ func TestSessions(t *testing.T) {
})
t.Run("stores the refresh token in a session cookie", func(t *testing.T) {
- assert.NotEmpty(t, tokens["refresh_token"])
+ cookie := x.Find(cookies, func(item *http.Cookie) bool {
+ return item.Name == xcfg.RefreshTokenCookie
+ })
+ require.NotNil(t, cookie)
keypair, err := mockoidc.DefaultKeypair()
require.NoError(t, err)
- token, err := keypair.VerifyJWT(tokens["refresh_token"].(string), nil)
+ token, err := keypair.VerifyJWT(cookie.Value, nil)
require.NoError(t, err)
sub, err := token.Claims.GetSubject()
@@ -183,11 +182,13 @@ func TestSessions(t *testing.T) {
})
t.Run("applies the appropriate cookie settings", func(t *testing.T) {
- assert.Equal(t, "/", cookie.Path)
- assert.Equal(t, xcfg.SessionCookie, cookie.Name)
- assert.True(t, cookie.HttpOnly)
- assert.True(t, cookie.Secure)
- assert.NotEmpty(t, cookie.Value)
+ x.Each(cookies, func(cookie *http.Cookie) {
+ t.Logf("%v: %v\n", cookie.Name, cookie.Value)
+ assert.Equal(t, "/", cookie.Path)
+ assert.NotEmpty(t, cookie.Name)
+ assert.True(t, cookie.HttpOnly)
+ assert.True(t, cookie.Secure)
+ })
})
})
})
@@ -204,7 +205,6 @@ func TestSessions(t *testing.T) {
require.Equal(t, http.StatusOK, w.Code)
items, err := serde.FromJSON[map[string]interface{}](w.Body)
require.NoError(t, err)
- fmt.Printf("%v\n", items)
assert.Equal(t, srv.Issuer(), items["iss"])
})
@@ -219,7 +219,7 @@ func TestSessions(t *testing.T) {
t.Run("POST /session/destroy", func(t *testing.T) {
t.Run("clears the session cookie", func(t *testing.T) {
- cookie := web.NewCookie(xcfg.SessionCookie, "value")
+ cookie := web.NewCookie(xcfg.IDTokenCookie, "value")
r, w := test.RequestResponse("POST", "/session/destroy", test.WithCookie(cookie))
mux.ServeHTTP(w, r)
@@ -229,7 +229,9 @@ func TestSessions(t *testing.T) {
expected := []string{
"__csrf=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Secure",
- "__s=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Secure",
+ "id_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Secure",
+ "bearer_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Secure",
+ "refresh_token=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT; Max-Age=0; HttpOnly; Secure",
}
assert.ElementsMatch(t, expected, w.Header().Values("Set-Cookie"))
})
diff --git a/app/middleware/id_token.go b/app/middleware/id_token.go
index e0b5b0d..dbaf691 100644
--- a/app/middleware/id_token.go
+++ b/app/middleware/id_token.go
@@ -21,7 +21,7 @@ func IDToken(cfg *oidc.OpenID, parsers ...TokenParser) func(http.Handler) http.H
if err != nil {
pls.LogError(r.Context(), err)
- web.ExpireCookie(w, xcfg.SessionCookie)
+ web.ExpireCookie(w, xcfg.IDTokenCookie)
} else {
log.WithFields(r.Context(), log.Fields{"id_token": idToken})
next.ServeHTTP(
diff --git a/app/middleware/id_token_test.go b/app/middleware/id_token_test.go
index 31a4333..45221ff 100644
--- a/app/middleware/id_token_test.go
+++ b/app/middleware/id_token_test.go
@@ -11,7 +11,6 @@ import (
"github.com/stretchr/testify/require"
"github.com/xlgmokha/x/pkg/log"
"github.com/xlgmokha/x/pkg/test"
- "github.com/xlgmokha/x/pkg/x"
xcfg "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/cfg"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/web"
@@ -40,9 +39,7 @@ func TestIDToken(t *testing.T) {
t.Run("attaches the token to the request context", func(t *testing.T) {
user := mockoidc.DefaultUser()
- token, rawIDToken := srv.CreateTokensFor(user)
- tokens := &oidc.Tokens{Token: token, IDToken: oidc.RawToken(rawIDToken)}
- encoded := x.Must(tokens.ToBase64String())
+ _, rawIDToken := srv.CreateTokensFor(user)
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := xcfg.IDToken.From(r.Context())
@@ -55,7 +52,7 @@ func TestIDToken(t *testing.T) {
r, w := test.RequestResponse(
"GET",
"/example",
- test.WithCookie(web.NewCookie(xcfg.SessionCookie, encoded)),
+ test.WithCookie(web.NewCookie(xcfg.IDTokenCookie, rawIDToken)),
)
server.ServeHTTP(w, r)
@@ -74,7 +71,7 @@ func TestIDToken(t *testing.T) {
r, w := test.RequestResponse(
"GET",
"/example",
- test.WithCookie(web.NewCookie(xcfg.SessionCookie, "invalid")),
+ test.WithCookie(web.NewCookie(xcfg.IDTokenCookie, "invalid")),
)
server.ServeHTTP(w, r)
diff --git a/app/middleware/token_parser.go b/app/middleware/token_parser.go
index 08219b4..22a7af9 100644
--- a/app/middleware/token_parser.go
+++ b/app/middleware/token_parser.go
@@ -6,25 +6,16 @@ import (
"github.com/xlgmokha/x/pkg/x"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/cfg"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
- "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/pls"
- "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/web"
)
type TokenParser x.Mapper[*http.Request, oidc.RawToken]
func IDTokenFromSessionCookie(r *http.Request) oidc.RawToken {
- cookies := r.CookiesNamed(cfg.SessionCookie)
+ cookies := r.CookiesNamed(cfg.IDTokenCookie)
if len(cookies) != 1 {
return ""
}
- value := web.CookieValueFrom(cookies[0])
- tokens, err := oidc.TokensFromBase64String(value)
- if err != nil {
- pls.LogError(r.Context(), err)
- return ""
- }
-
- return tokens.IDToken
+ return oidc.RawToken(cookies[0].Value)
}
diff --git a/pkg/web/cookie.go b/pkg/web/cookie.go
index 7a2426f..c5391e9 100644
--- a/pkg/web/cookie.go
+++ b/pkg/web/cookie.go
@@ -1,48 +1,23 @@
package web
import (
- "crypto/sha256"
- "encoding/base64"
- "fmt"
"net/http"
- "strings"
"github.com/xlgmokha/x/pkg/cookie"
- "github.com/xlgmokha/x/pkg/crypt"
- "github.com/xlgmokha/x/pkg/env"
- "github.com/xlgmokha/x/pkg/pls"
"github.com/xlgmokha/x/pkg/x"
)
-// TODO:: https://gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/-/issues/2
-var key []byte = []byte(env.Fetch("HMAC_SESSION_SECRET", string(x.Must(pls.GenerateRandomBytes(32)))))
-var Signer *crypt.HMACSigner = x.New[*crypt.HMACSigner](crypt.WithKey(key), crypt.WithAlgorithm(sha256.New))
-var delimiter string = "--"
-
func NewCookie(name, value string, options ...x.Option[*http.Cookie]) *http.Cookie {
return x.New[*http.Cookie](x.Prepend[x.Option[*http.Cookie]](
options,
cookie.WithName(name),
- withSignedValue(value),
+ cookie.WithValue(value),
cookie.WithPath("/"),
cookie.WithHttpOnly(true),
cookie.WithSecure(true),
)...)
}
-func withSignedValue(value string) x.Option[*http.Cookie] {
- signature, err := Signer.Sign([]byte(value))
- if err != nil {
- return cookie.WithValue(value)
- }
- return cookie.WithValue(fmt.Sprintf(
- "%v%v%v",
- value,
- delimiter,
- base64.URLEncoding.EncodeToString(signature),
- ))
-}
-
func ExpireCookie(w http.ResponseWriter, name string) error {
return WriteCookie(w, cookie.Reset(name,
cookie.WithPath("/"),
@@ -52,22 +27,7 @@ func ExpireCookie(w http.ResponseWriter, name string) error {
}
func CookieValueFrom(c *http.Cookie) string {
- segments := strings.SplitN(c.Value, delimiter, 2)
- if len(segments) != 2 {
- return ""
- }
-
- data := segments[0]
- signature, err := base64.URLEncoding.DecodeString(segments[1])
- if err != nil {
- return ""
- }
-
- if !Signer.Verify([]byte(data), []byte(signature)) {
- return ""
- }
-
- return data
+ return c.Value
}
func WriteCookie(w http.ResponseWriter, c *http.Cookie) error {