summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/app.go4
-rw-r--r--app/controllers/sessions/controller_test.go51
-rw-r--r--go.sum2
-rw-r--r--pkg/oidc/id_token.go60
-rw-r--r--pkg/oidc/oidc.go11
-rw-r--r--pkg/oidc/oidc_test.go33
-rw-r--r--pkg/oidc/tokens.go4
-rw-r--r--pkg/oidc/tokens_test.go20
-rw-r--r--pkg/test/oidc_server.go61
-rw-r--r--pkg/web/middleware/unpack_token.go13
-rw-r--r--pkg/web/middleware/unpack_token_test.go17
11 files changed, 123 insertions, 153 deletions
diff --git a/app/app.go b/app/app.go
index f76f4a7..51979a4 100644
--- a/app/app.go
+++ b/app/app.go
@@ -10,6 +10,7 @@ import (
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/health"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/sessions"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/app/controllers/sparkles"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/web"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/web/middleware"
)
@@ -30,5 +31,6 @@ func New() http.Handler {
mux.Handle("GET /", http.FileServer(http.Dir("public")))
logger := ioc.MustResolve[*zerolog.Logger](ioc.Default)
- return log.HTTP(logger)(middleware.UnpackToken()(mux))
+ oidc := ioc.MustResolve[*oidc.OpenID](ioc.Default)
+ return log.HTTP(logger)(middleware.UnpackToken(oidc)(mux))
}
diff --git a/app/controllers/sessions/controller_test.go b/app/controllers/sessions/controller_test.go
index 71f9311..1b829bf 100644
--- a/app/controllers/sessions/controller_test.go
+++ b/app/controllers/sessions/controller_test.go
@@ -4,68 +4,19 @@ import (
"encoding/base64"
"encoding/json"
"net/http"
- "net/http/httptest"
"net/url"
"strings"
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/xlgmokha/x/pkg/serde"
"github.com/xlgmokha/x/pkg/x"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/test"
)
func TestSessions(t *testing.T) {
- srv := httptest.NewServer(nil)
- srv.Config = &http.Server{
- Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- switch r.URL.Path {
- case "/.well-known/openid-configuration":
- require.NoError(t, serde.ToJSON(w, &oidc.Metadata{
- AuthorizationEndpoint: srv.URL + "/oauth/authorize",
- ClaimsSupported: []string{"aud"},
- CodeChallengeMethodsSupported: []string{"plain"},
- DeviceAuthorizationEndpoint: srv.URL + "/device/authorize",
- IDTokenSigningAlgValuesSupported: []string{"RS256"},
- Issuer: srv.URL,
- JWKSURI: srv.URL + "/jwks",
- MFAChallengeEndpoint: srv.URL + "/mfa",
- RegistrationEndpoint: srv.URL + "/users/new",
- RequestURIParameterSupported: false,
- ResponseModesSupported: []string{"query"},
- ResponseTypeSupported: []string{"code"},
- RevocationEndpoint: srv.URL + "/revoke",
- ScopesSupported: []string{"oidc"},
- SubjectTypesSupported: []string{"public"},
- TokenEndpoint: srv.URL + "/token",
- TokenEndpointAuthMethodsSupported: []string{"client_secret_post"},
- UserInfoEndpoint: srv.URL + "/users/me",
- }))
- case "/token":
- if err := r.ParseForm(); err != nil {
- w.WriteHeader(http.StatusBadRequest)
- return
- }
- if r.Form["grant_type"][0] == "authorization_code" && r.Form["code"][0] == "code" {
- w.Header().Add("Content-Type", "application/json")
- require.NoError(t, serde.ToJSON(w, map[string]string{
- "access_token": "14fa6e71afaabbe5e31ef2b47ccab7ca7a3c26f8dfdb74acce3eca30099af028",
- "token_type": "Bearer",
- "refresh_token": "365b261d4b25ba37e7c1e14e6501902aeecfb7fffc4602c44d6ac22b4c715b0f",
- // "expiry": "2025-04-15T19:16:38.78960504-06:00"
- "expiry": time.Now().Add(1 * time.Hour).Format(time.RFC3339),
- "id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g",
- }))
- }
- default:
- t.Logf("404: %v", r.URL.Path)
- w.WriteHeader(http.StatusNotFound)
- }
- }),
- }
+ srv := test.OIDCServer()
defer srv.Close()
cfg, err := oidc.New(t.Context(), srv.URL, "client_id", "client_secret", "callback_url")
diff --git a/go.sum b/go.sum
index 66b8c1a..9a2b90d 100644
--- a/go.sum
+++ b/go.sum
@@ -71,12 +71,10 @@ github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM=
github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
diff --git a/pkg/oidc/id_token.go b/pkg/oidc/id_token.go
index 5fc1e63..ce4ae22 100644
--- a/pkg/oidc/id_token.go
+++ b/pkg/oidc/id_token.go
@@ -1,39 +1,33 @@
package oidc
-import (
- "bytes"
- "encoding/base64"
- "errors"
- "strings"
- "time"
+import "github.com/coreos/go-oidc/v3/oidc"
- "github.com/xlgmokha/x/pkg/serde"
-)
+type IDToken = oidc.IDToken
-type IDToken struct {
- Audience string `json:"aud"`
- Email string `json:"email"`
- EmailVerified bool `json:"email_verified"`
- ExpiredAt int64 `json:"exp"`
- IssuedAt int64 `json:"iat"`
- Issuer string `json:"iss"`
- Name string `json:"name"`
- Nickname string `json:"nickname"`
- Picture string `json:"picture"`
- Subject string `json:"sub"`
- UpdatedAt time.Time `json:"updated_at"`
-}
+// type IDToken struct {
+// Audience string `json:"aud"`
+// Email string `json:"email"`
+// EmailVerified bool `json:"email_verified"`
+// ExpiredAt int64 `json:"exp"`
+// IssuedAt int64 `json:"iat"`
+// Issuer string `json:"iss"`
+// Name string `json:"name"`
+// Nickname string `json:"nickname"`
+// Picture string `json:"picture"`
+// Subject string `json:"sub"`
+// UpdatedAt time.Time `json:"updated_at"`
+// }
-func NewIDToken(raw string) (*IDToken, error) {
- sections := strings.SplitN(raw, ".", 3)
- if len(sections) != 3 {
- return nil, errors.New("Invalid token")
- }
- b, err := base64.RawURLEncoding.DecodeString(sections[1])
- if err != nil {
- return nil, err
- }
+// func NewIDToken(raw string) (*IDToken, error) {
+// sections := strings.SplitN(raw, ".", 3)
+// if len(sections) != 3 {
+// return nil, errors.New("Invalid token")
+// }
+// b, err := base64.RawURLEncoding.DecodeString(sections[1])
+// if err != nil {
+// return nil, err
+// }
- token, err := serde.FromJSON[*IDToken](bytes.NewReader(b))
- return token, err
-}
+// token, err := serde.FromJSON[*IDToken](bytes.NewReader(b))
+// return token, err
+// }
diff --git a/pkg/oidc/oidc.go b/pkg/oidc/oidc.go
index 0526142..200db69 100644
--- a/pkg/oidc/oidc.go
+++ b/pkg/oidc/oidc.go
@@ -4,14 +4,18 @@ import (
"context"
"github.com/coreos/go-oidc/v3/oidc"
+ xcontext "github.com/xlgmokha/x/pkg/context"
"golang.org/x/oauth2"
)
type OpenID struct {
- Provider *oidc.Provider
- Config *oauth2.Config
+ Provider *oidc.Provider
+ Config *oauth2.Config
+ OIDCConfig *oidc.Config
}
+var IDTokenKey xcontext.Key[*IDToken] = xcontext.Key[*IDToken]("id_token")
+
func New(ctx context.Context, issuer string, clientID, clientSecret, callbackURL string) (*OpenID, error) {
provider, err := oidc.NewProvider(ctx, issuer)
if err != nil {
@@ -27,5 +31,8 @@ func New(ctx context.Context, issuer string, clientID, clientSecret, callbackURL
Endpoint: provider.Endpoint(),
Scopes: []string{oidc.ScopeOpenID, "profile", "email"},
},
+ OIDCConfig: &oidc.Config{
+ ClientID: clientID,
+ },
}, nil
}
diff --git a/pkg/oidc/oidc_test.go b/pkg/oidc/oidc_test.go
index ef6a4f6..b7715cf 100644
--- a/pkg/oidc/oidc_test.go
+++ b/pkg/oidc/oidc_test.go
@@ -2,48 +2,21 @@ package oidc
import (
"context"
- "net/http"
- "net/http/httptest"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/xlgmokha/x/pkg/serde"
+ "gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/test"
)
func TestOpenID(t *testing.T) {
t.Run("GET /.well-known/openid-configuration", func(t *testing.T) {
- srv := httptest.NewServer(nil)
- srv.Config = &http.Server{
- Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- require.Equal(t, "/.well-known/openid-configuration", r.URL.Path)
- require.NoError(t, serde.ToJSON(w, &Metadata{
- AuthorizationEndpoint: srv.URL + "/authorize",
- ClaimsSupported: []string{"aud"},
- CodeChallengeMethodsSupported: []string{"plain"},
- DeviceAuthorizationEndpoint: srv.URL + "/device/authorize",
- IDTokenSigningAlgValuesSupported: []string{"RS256"},
- Issuer: srv.URL,
- JWKSURI: srv.URL + "/jwks",
- MFAChallengeEndpoint: srv.URL + "/mfa",
- RegistrationEndpoint: srv.URL + "/users/new",
- RequestURIParameterSupported: false,
- ResponseModesSupported: []string{"query"},
- ResponseTypeSupported: []string{"code"},
- RevocationEndpoint: srv.URL + "/revoke",
- ScopesSupported: []string{"oidc"},
- SubjectTypesSupported: []string{"public"},
- TokenEndpoint: srv.URL + "/token",
- TokenEndpointAuthMethodsSupported: []string{"client_secret_post"},
- UserInfoEndpoint: srv.URL + "/users/me",
- }))
- }),
- }
+ srv := test.OIDCServer()
defer srv.Close()
openID, err := New(context.Background(), srv.URL, "client_id", "client_secret", "https://example.com/oauth/callback")
require.NoError(t, err)
- assert.Equal(t, srv.URL+"/authorize", openID.Provider.Endpoint().AuthURL)
+ assert.Equal(t, srv.URL+"/oauth/authorize", openID.Provider.Endpoint().AuthURL)
})
}
diff --git a/pkg/oidc/tokens.go b/pkg/oidc/tokens.go
index a326ff2..7ff3c07 100644
--- a/pkg/oidc/tokens.go
+++ b/pkg/oidc/tokens.go
@@ -22,10 +22,6 @@ func (t *Tokens) ToBase64String() (string, error) {
return base64.URLEncoding.EncodeToString(data), nil
}
-func (t *Tokens) ParseIDToken() (*IDToken, error) {
- return NewIDToken(t.IDToken)
-}
-
func TokensFromBase64String(encoded string) (*Tokens, error) {
decoded, err := base64.URLEncoding.DecodeString(encoded)
if err != nil {
diff --git a/pkg/oidc/tokens_test.go b/pkg/oidc/tokens_test.go
index 3191f97..a7c5e13 100644
--- a/pkg/oidc/tokens_test.go
+++ b/pkg/oidc/tokens_test.go
@@ -4,7 +4,6 @@ import (
"bytes"
"encoding/json"
"testing"
- "time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -70,23 +69,4 @@ func TestTokens(t *testing.T) {
assert.Equal(t, "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g", result.IDToken)
})
})
-
- t.Run("ParseIDToken", func(t *testing.T) {
- tokens := &Tokens{IDToken: "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g"}
-
- idToken, err := tokens.ParseIDToken()
- require.NoError(t, err)
-
- assert.Equal(t, "e31e1da0b8f6b6e35ca70c790b13c0406e44aca6b2bf67f55de7355a979a224f", idToken.Audience)
- assert.Equal(t, "admin@example.com", idToken.Email)
- assert.Equal(t, true, idToken.EmailVerified)
- assert.Equal(t, int64(1744737427), idToken.ExpiredAt)
- assert.Equal(t, int64(1744737307), idToken.IssuedAt)
- assert.Equal(t, "http://gdk.test:3000", idToken.Issuer)
- assert.Equal(t, "Administrator", idToken.Name)
- assert.Equal(t, "root", idToken.Nickname)
- assert.Equal(t, "https://www.gravatar.com/avatar/258d8dc916db8cea2cafb6c3cd0cb0246efe061421dbd83ec3a350428cabda4f?s=80&d=identicon", idToken.Picture)
- assert.Equal(t, "1", idToken.Subject)
- assert.Equal(t, time.Time{}, idToken.UpdatedAt)
- })
}
diff --git a/pkg/test/oidc_server.go b/pkg/test/oidc_server.go
new file mode 100644
index 0000000..142d143
--- /dev/null
+++ b/pkg/test/oidc_server.go
@@ -0,0 +1,61 @@
+package test
+
+import (
+ "net/http"
+ "net/http/httptest"
+ "time"
+
+ "github.com/xlgmokha/x/pkg/serde"
+)
+
+func OIDCServer() *httptest.Server {
+ srv := httptest.NewServer(nil)
+ srv.Config = &http.Server{
+ Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/.well-known/openid-configuration":
+ metadata := map[string]interface{}{
+ "authorization_endpoint": srv.URL + "/oauth/authorize",
+ "claims_supported": []string{"aud"},
+ "code_challenge_methods_supported": []string{"plain"},
+ "device_authorization_endpoint": srv.URL + "/device/authorize",
+ "id_token_signing_alg_values_supported": []string{"RS256"},
+ "issuer": srv.URL,
+ "jwks_uri": srv.URL + "/jwks",
+ "mfa_challenge_endpoint": srv.URL + "/mfa",
+ "registration_endpoint": srv.URL + "/users/new",
+ "request_uri_parameter_supported": false,
+ "response_modes_supported": []string{"query"},
+ "response_types_supported": []string{"code"},
+ "revocation_endpoint": srv.URL + "/revoke",
+ "scopes_supported": []string{"oidc"},
+ "subject_types_supported": []string{"public"},
+ "token_endpoint": srv.URL + "/token",
+ "token_endpoint_auth_methods_supported": []string{"client_secret_post"},
+ "userinfo_endpoint": srv.URL + "/users/me",
+ }
+
+ serde.ToJSON(w, metadata)
+ case "/token":
+ if err := r.ParseForm(); err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+ if r.Form["grant_type"][0] == "authorization_code" && r.Form["code"][0] == "code" {
+ w.Header().Add("Content-Type", "application/json")
+ serde.ToJSON(w, map[string]string{
+ "access_token": "14fa6e71afaabbe5e31ef2b47ccab7ca7a3c26f8dfdb74acce3eca30099af028",
+ "token_type": "Bearer",
+ "refresh_token": "365b261d4b25ba37e7c1e14e6501902aeecfb7fffc4602c44d6ac22b4c715b0f",
+ // "expiry": "2025-04-15T19:16:38.78960504-06:00"
+ "expiry": time.Now().Add(1 * time.Hour).Format(time.RFC3339),
+ "id_token": "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g",
+ })
+ }
+ default:
+ w.WriteHeader(http.StatusNotFound)
+ }
+ }),
+ }
+ return srv
+}
diff --git a/pkg/web/middleware/unpack_token.go b/pkg/web/middleware/unpack_token.go
index b53d5d3..f3d20a0 100644
--- a/pkg/web/middleware/unpack_token.go
+++ b/pkg/web/middleware/unpack_token.go
@@ -1,16 +1,14 @@
package middleware
import (
+ "fmt"
"net/http"
- "github.com/xlgmokha/x/pkg/context"
"github.com/xlgmokha/x/pkg/log"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
)
-var IDTokenContextKey context.Key[*oidc.IDToken] = context.Key[*oidc.IDToken]("id_token")
-
-func UnpackToken() func(http.Handler) http.Handler {
+func UnpackToken(cfg *oidc.OpenID) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookies := r.CookiesNamed("session")
@@ -21,8 +19,11 @@ func UnpackToken() func(http.Handler) http.Handler {
return
}
- idToken, err := tokens.ParseIDToken()
+ ctx := r.Context()
+ verifier := cfg.Provider.VerifierContext(ctx, cfg.OIDCConfig)
+ idToken, err := verifier.Verify(ctx, tokens.IDToken)
if err != nil {
+ fmt.Printf("%v\n", err)
next.ServeHTTP(w, r)
return
}
@@ -30,7 +31,7 @@ func UnpackToken() func(http.Handler) http.Handler {
log.WithFields(r.Context(), log.Fields{"id_token": idToken})
next.ServeHTTP(
w,
- r.WithContext(IDTokenContextKey.With(r.Context(), idToken)),
+ r.WithContext(oidc.IDTokenKey.With(r.Context(), idToken)),
)
} else {
next.ServeHTTP(w, r)
diff --git a/pkg/web/middleware/unpack_token_test.go b/pkg/web/middleware/unpack_token_test.go
index a6f591e..ac3d50c 100644
--- a/pkg/web/middleware/unpack_token_test.go
+++ b/pkg/web/middleware/unpack_token_test.go
@@ -1,6 +1,7 @@
package middleware
import (
+ "context"
"net/http"
"testing"
"time"
@@ -14,7 +15,13 @@ import (
)
func TestUnpackToken(t *testing.T) {
- middleware := UnpackToken()
+ srv := test.OIDCServer()
+ defer srv.Close()
+
+ openID, err := oidc.New(context.Background(), srv.URL, "client_id", "client_secret", "https://example.com/oauth/callback")
+ require.NoError(t, err)
+
+ middleware := UnpackToken(openID)
t.Run("when an active session cookie is provided", func(t *testing.T) {
t.Run("attaches the token to the request context", func(t *testing.T) {
@@ -22,9 +29,9 @@ func TestUnpackToken(t *testing.T) {
encoded := x.Must(tokens.ToBase64String())
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- token := IDTokenContextKey.From(r.Context())
+ token := oidc.IDTokenKey.From(r.Context())
require.NotNil(t, token)
- assert.Equal(t, "root", token.Nickname)
+ assert.Equal(t, "root", token.Issuer)
w.WriteHeader(http.StatusTeapot)
}))
@@ -43,7 +50,7 @@ func TestUnpackToken(t *testing.T) {
t.Run("when an invalid session cookie is provided", func(t *testing.T) {
t.Run("forwards the request", func(t *testing.T) {
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- require.Nil(t, IDTokenContextKey.From(r.Context()))
+ require.Nil(t, oidc.IDTokenKey.From(r.Context()))
w.WriteHeader(http.StatusTeapot)
}))
@@ -62,7 +69,7 @@ func TestUnpackToken(t *testing.T) {
t.Run("when no cookies are provided", func(t *testing.T) {
t.Run("forwards the request", func(t *testing.T) {
server := middleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- require.Nil(t, IDTokenContextKey.From(r.Context()))
+ require.Nil(t, oidc.IDTokenKey.From(r.Context()))
w.WriteHeader(http.StatusTeapot)
}))