summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/sessions/controller.go20
-rw-r--r--app/controllers/sessions/controller_test.go9
-rw-r--r--pkg/oidc/tokens.go41
-rw-r--r--pkg/oidc/tokens_test.go92
4 files changed, 149 insertions, 13 deletions
diff --git a/app/controllers/sessions/controller.go b/app/controllers/sessions/controller.go
index 70d5631..d526a86 100644
--- a/app/controllers/sessions/controller.go
+++ b/app/controllers/sessions/controller.go
@@ -1,8 +1,6 @@
package sessions
import (
- "encoding/base64"
- "encoding/json"
"net/http"
"gitlab.com/gitlab-org/software-supply-chain-security/authorization/sparkled/pkg/oidc"
@@ -31,23 +29,21 @@ func (c *Controller) New(w http.ResponseWriter, r *http.Request) {
func (c *Controller) Create(w http.ResponseWriter, r *http.Request) {
token, err := c.cfg.Config.Exchange(r.Context(), r.URL.Query().Get("code"))
if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
return
}
- tokens := map[string]interface{}{
- "access_token": token.AccessToken,
- "token_type": token.TokenType,
- "refresh_token": token.RefreshToken,
- "expiry": token.Expiry,
- "expires_in": token.ExpiresIn,
+ tokens := &oidc.Tokens{Token: token}
+ if rawIDToken, ok := token.Extra("id_token").(string); ok {
+ tokens.IDToken = rawIDToken
}
- if rawIDToken, ok := token.Extra("id_token").(string); ok {
- tokens["id_token"] = rawIDToken
+ encoded, err := tokens.ToBase64String()
+ if err != nil {
+ w.WriteHeader(http.StatusBadRequest)
+ return
}
- data, err := json.Marshal(tokens)
- encoded := base64.URLEncoding.EncodeToString(data)
http.SetCookie(w, &http.Cookie{Name: "session", Value: encoded})
http.Redirect(w, r, "/dashboard", http.StatusFound)
}
diff --git a/app/controllers/sessions/controller_test.go b/app/controllers/sessions/controller_test.go
index 1c4637d..bbe82bc 100644
--- a/app/controllers/sessions/controller_test.go
+++ b/app/controllers/sessions/controller_test.go
@@ -102,7 +102,14 @@ func TestSessions(t *testing.T) {
t.Run("GET /session/callback", func(t *testing.T) {
t.Run("with an invalid csrf token", func(t *testing.T) {})
- t.Run("with an invalid authorization code grant", func(t *testing.T) {})
+ t.Run("with an invalid authorization code grant", func(t *testing.T) {
+ r, w := test.RequestResponse("GET", "/session/callback?code=invalid")
+
+ mux.ServeHTTP(w, r)
+
+ assert.Equal(t, http.StatusBadRequest, w.Code)
+ })
+
t.Run("with a valid authorization code grant", func(t *testing.T) {
r, w := test.RequestResponse("GET", "/session/callback?code=code")
diff --git a/pkg/oidc/tokens.go b/pkg/oidc/tokens.go
new file mode 100644
index 0000000..a326ff2
--- /dev/null
+++ b/pkg/oidc/tokens.go
@@ -0,0 +1,41 @@
+package oidc
+
+import (
+ "bytes"
+ "encoding/base64"
+ "encoding/json"
+
+ "github.com/xlgmokha/x/pkg/serde"
+ "golang.org/x/oauth2"
+)
+
+type Tokens struct {
+ *oauth2.Token
+ IDToken string `json:"id_token"`
+}
+
+func (t *Tokens) ToBase64String() (string, error) {
+ data, err := json.Marshal(t)
+ if err != nil {
+ return "", err
+ }
+ 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 {
+ return nil, err
+ }
+
+ tokens, err := serde.FromJSON[*Tokens](bytes.NewBuffer(decoded))
+ if err != nil {
+ return nil, err
+ }
+
+ return tokens, nil
+}
diff --git a/pkg/oidc/tokens_test.go b/pkg/oidc/tokens_test.go
new file mode 100644
index 0000000..3191f97
--- /dev/null
+++ b/pkg/oidc/tokens_test.go
@@ -0,0 +1,92 @@
+package oidc
+
+import (
+ "bytes"
+ "encoding/json"
+ "testing"
+ "time"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "github.com/xlgmokha/x/pkg/serde"
+ "golang.org/x/oauth2"
+)
+
+func TestTokens(t *testing.T) {
+ t.Run("serializes to JSON", func(t *testing.T) {
+ tokens := &Tokens{
+ Token: &oauth2.Token{
+ AccessToken: "access_token",
+ TokenType: "Bearer",
+ RefreshToken: "refresh_token",
+ ExpiresIn: 60 * 60,
+ },
+ IDToken: "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g",
+ }
+
+ b, err := json.Marshal(tokens)
+ require.NoError(t, err)
+
+ result, err := serde.FromJSON[map[string]interface{}](bytes.NewBuffer(b))
+ require.NoError(t, err)
+
+ assert.Equal(t, "access_token", result["access_token"])
+ assert.Equal(t, "Bearer", result["token_type"])
+ assert.Equal(t, "refresh_token", result["refresh_token"])
+ assert.Equal(t, float64(60*60), result["expires_in"])
+ })
+
+ t.Run("ToBase64String", func(t *testing.T) {
+ t.Run("serializes to Base64", func(t *testing.T) {
+ tokens := &Tokens{
+ Token: &oauth2.Token{
+ AccessToken: "access_token",
+ TokenType: "Bearer",
+ RefreshToken: "refresh_token",
+ ExpiresIn: 60 * 60,
+ },
+ IDToken: "eyJ0eXAiOiJKV1QiLCJraWQiOiJ0ZDBTbWRKUTRxUGg1cU5Lek0yNjBDWHgyVWgtd2hHLU1Eam9PS1dmdDhFIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMCIsInN1YiI6IjEiLCJhdWQiOiJlMzFlMWRhMGI4ZjZiNmUzNWNhNzBjNzkwYjEzYzA0MDZlNDRhY2E2YjJiZjY3ZjU1ZGU3MzU1YTk3OWEyMjRmIiwiZXhwIjoxNzQ0NzM3NDI3LCJpYXQiOjE3NDQ3MzczMDcsImF1dGhfdGltZSI6MTc0NDczNDY0OSwic3ViX2xlZ2FjeSI6IjI0NzRjZjBiMjIxMTY4OGE1NzI5N2FjZTBlMjYwYTE1OTQ0NzU0ZDE2YjFiZDQyYzlkNjc3OWM5MDAzNjc4MDciLCJuYW1lIjoiQWRtaW5pc3RyYXRvciIsIm5pY2tuYW1lIjoicm9vdCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvb3QiLCJlbWFpbCI6ImFkbWluQGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByb2ZpbGUiOiJodHRwOi8vZ2RrLnRlc3Q6MzAwMC9yb290IiwicGljdHVyZSI6Imh0dHBzOi8vd3d3LmdyYXZhdGFyLmNvbS9hdmF0YXIvMjU4ZDhkYzkxNmRiOGNlYTJjYWZiNmMzY2QwY2IwMjQ2ZWZlMDYxNDIxZGJkODNlYzNhMzUwNDI4Y2FiZGE0Zj9zPTgwJmQ9aWRlbnRpY29uIiwiZ3JvdXBzX2RpcmVjdCI6WyJ0b29sYm94IiwiZ2l0bGFiLW9yZyIsImdudXdnZXQiLCJDb21taXQ0NTEiLCJqYXNoa2VuYXMiLCJmbGlnaHRqcyIsInR3aXR0ZXIiLCJnaXRsYWItZXhhbXBsZXMiLCJnaXRsYWItZXhhbXBsZXMvc2VjdXJpdHkiLCI0MTI3MDgiLCJnaXRsYWItZXhhbXBsZXMvZGVtby1ncm91cCIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwIiwiNDM0MDQ0LWdyb3VwLTEiLCI0MzQwNDQtZ3JvdXAtMiIsImdpdGxhYi1vcmcxIiwiZ2l0bGFiLW9yZy9zZWN1cmUiLCJnaXRsYWItb3JnL3NlY3VyZS9tYW5hZ2VycyIsImdpdGxhYi1vcmcvc2VjdXJpdHktcHJvZHVjdHMiLCJnaXRsYWItb3JnL3NlY3VyaXR5LXByb2R1Y3RzL2FuYWx5emVycyIsImN1c3RvbS1yb2xlcy1yb290LWdyb3VwL2FhIiwiY3VzdG9tLXJvbGVzLXJvb3QtZ3JvdXAvYWEvYWFhIiwibWFzc19pbnNlcnRfZ3JvdXBfXzBfMTAwIl19.SZu_l7tQ2Kkeogq0z8cRaDWPfv52JTo-RkiExbnud_lrfrXXneS77BIzaGKX_bzq4SM_oO_Q63AzK66B1r6Gp7ACo4DjOUEIWETg7ZBKcDzEZnresB7kmI_MJ5rfIJTmnH75GOfc_pl5l8T896TbaShN6zSpaXXIVEfhyUrflSWb4hhA7Hbwy2b6laXiaDv0qpcn1udPVYMTsll8I5ni_2yzuEPSVRgrcQoQ46OwVDZIi9tlfdT2qNVjH6FxJ3mkBcxtIVjf3_JYAawFEscg2uvQYwFWj9T6LleMknAh3QFJJMrS6mPqlXJGPUE5pTQgsBInfEikfm9PXxezA-IY6g",
+ }
+
+ result, err := tokens.ToBase64String()
+ require.NoError(t, err)
+
+ assert.Equal(t, "eyJhY2Nlc3NfdG9rZW4iOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwicmVmcmVzaF90b2tlbiI6InJlZnJlc2hfdG9rZW4iLCJleHBpcnkiOiIwMDAxLTAxLTAxVDAwOjAwOjAwWiIsImV4cGlyZXNfaW4iOjM2MDAsImlkX3Rva2VuIjoiZXlKMGVYQWlPaUpLVjFRaUxDSnJhV1FpT2lKMFpEQlRiV1JLVVRSeFVHZzFjVTVMZWsweU5qQkRXSGd5VldndGQyaEhMVTFFYW05UFMxZG1kRGhGSWl3aVlXeG5Jam9pVWxNeU5UWWlmUS5leUpwYzNNaU9pSm9kSFJ3T2k4dloyUnJMblJsYzNRNk16QXdNQ0lzSW5OMVlpSTZJakVpTENKaGRXUWlPaUpsTXpGbE1XUmhNR0k0WmpaaU5tVXpOV05oTnpCak56a3dZakV6WXpBME1EWmxORFJoWTJFMllqSmlaalkzWmpVMVpHVTNNelUxWVRrM09XRXlNalJtSWl3aVpYaHdJam94TnpRME56TTNOREkzTENKcFlYUWlPakUzTkRRM016Y3pNRGNzSW1GMWRHaGZkR2x0WlNJNk1UYzBORGN6TkRZME9Td2ljM1ZpWDJ4bFoyRmplU0k2SWpJME56UmpaakJpTWpJeE1UWTRPR0UxTnpJNU4yRmpaVEJsTWpZd1lURTFPVFEwTnpVMFpERTJZakZpWkRReVl6bGtOamMzT1dNNU1EQXpOamM0TURjaUxDSnVZVzFsSWpvaVFXUnRhVzVwYzNSeVlYUnZjaUlzSW01cFkydHVZVzFsSWpvaWNtOXZkQ0lzSW5CeVpXWmxjbkpsWkY5MWMyVnlibUZ0WlNJNkluSnZiM1FpTENKbGJXRnBiQ0k2SW1Ga2JXbHVRR1Y0WVcxd2JHVXVZMjl0SWl3aVpXMWhhV3hmZG1WeWFXWnBaV1FpT25SeWRXVXNJbkJ5YjJacGJHVWlPaUpvZEhSd09pOHZaMlJyTG5SbGMzUTZNekF3TUM5eWIyOTBJaXdpY0dsamRIVnlaU0k2SW1oMGRIQnpPaTh2ZDNkM0xtZHlZWFpoZEdGeUxtTnZiUzloZG1GMFlYSXZNalU0WkRoa1l6a3hObVJpT0dObFlUSmpZV1ppTm1NelkyUXdZMkl3TWpRMlpXWmxNRFl4TkRJeFpHSmtPRE5sWXpOaE16VXdOREk0WTJGaVpHRTBaajl6UFRnd0ptUTlhV1JsYm5ScFkyOXVJaXdpWjNKdmRYQnpYMlJwY21WamRDSTZXeUowYjI5c1ltOTRJaXdpWjJsMGJHRmlMVzl5WnlJc0ltZHVkWGRuWlhRaUxDSkRiMjF0YVhRME5URWlMQ0pxWVhOb2EyVnVZWE1pTENKbWJHbG5hSFJxY3lJc0luUjNhWFIwWlhJaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNdmMyVmpkWEpwZEhraUxDSTBNVEkzTURnaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNdlpHVnRieTFuY205MWNDSXNJbU4xYzNSdmJTMXliMnhsY3kxeWIyOTBMV2R5YjNWd0lpd2lORE0wTURRMExXZHliM1Z3TFRFaUxDSTBNelF3TkRRdFozSnZkWEF0TWlJc0ltZHBkR3hoWWkxdmNtY3hJaXdpWjJsMGJHRmlMVzl5Wnk5elpXTjFjbVVpTENKbmFYUnNZV0l0YjNKbkwzTmxZM1Z5WlM5dFlXNWhaMlZ5Y3lJc0ltZHBkR3hoWWkxdmNtY3ZjMlZqZFhKcGRIa3RjSEp2WkhWamRITWlMQ0puYVhSc1lXSXRiM0puTDNObFkzVnlhWFI1TFhCeWIyUjFZM1J6TDJGdVlXeDVlbVZ5Y3lJc0ltTjFjM1J2YlMxeWIyeGxjeTF5YjI5MExXZHliM1Z3TDJGaElpd2lZM1Z6ZEc5dExYSnZiR1Z6TFhKdmIzUXRaM0p2ZFhBdllXRXZZV0ZoSWl3aWJXRnpjMTlwYm5ObGNuUmZaM0p2ZFhCZlh6QmZNVEF3SWwxOS5TWnVfbDd0UTJLa2VvZ3EwejhjUmFEV1BmdjUySlRvLVJraUV4Ym51ZF9scmZyWFhuZVM3N0JJemFHS1hfYnpxNFNNX29PX1E2M0F6SzY2QjFyNkdwN0FDbzREak9VRUlXRVRnN1pCS2NEekVabnJlc0I3a21JX01KNXJmSUpUbW5INzVHT2ZjX3BsNWw4VDg5NlRiYVNoTjZ6U3BhWFhJVkVmaHlVcmZsU1diNGhoQTdIYnd5MmI2bGFYaWFEdjBxcGNuMXVkUFZZTVRzbGw4STVuaV8yeXp1RVBTVlJncmNRb1E0Nk93VkRaSWk5dGxmZFQycU5Wakg2RnhKM21rQmN4dElWamYzX0pZQWF3RkVzY2cydXZRWXdGV2o5VDZMbGVNa25BaDNRRkpKTXJTNm1QcWxYSkdQVUU1cFRRZ3NCSW5mRWlrZm05UFh4ZXpBLUlZNmcifQ==", result)
+ })
+ })
+
+ t.Run("TokensFromBase64String", func(t *testing.T) {
+ t.Run("deserializes from Base64", func(t *testing.T) {
+ s := "eyJhY2Nlc3NfdG9rZW4iOiJhY2Nlc3NfdG9rZW4iLCJ0b2tlbl90eXBlIjoiQmVhcmVyIiwicmVmcmVzaF90b2tlbiI6InJlZnJlc2hfdG9rZW4iLCJleHBpcnkiOiIwMDAxLTAxLTAxVDAwOjAwOjAwWiIsImV4cGlyZXNfaW4iOjM2MDAsImlkX3Rva2VuIjoiZXlKMGVYQWlPaUpLVjFRaUxDSnJhV1FpT2lKMFpEQlRiV1JLVVRSeFVHZzFjVTVMZWsweU5qQkRXSGd5VldndGQyaEhMVTFFYW05UFMxZG1kRGhGSWl3aVlXeG5Jam9pVWxNeU5UWWlmUS5leUpwYzNNaU9pSm9kSFJ3T2k4dloyUnJMblJsYzNRNk16QXdNQ0lzSW5OMVlpSTZJakVpTENKaGRXUWlPaUpsTXpGbE1XUmhNR0k0WmpaaU5tVXpOV05oTnpCak56a3dZakV6WXpBME1EWmxORFJoWTJFMllqSmlaalkzWmpVMVpHVTNNelUxWVRrM09XRXlNalJtSWl3aVpYaHdJam94TnpRME56TTNOREkzTENKcFlYUWlPakUzTkRRM016Y3pNRGNzSW1GMWRHaGZkR2x0WlNJNk1UYzBORGN6TkRZME9Td2ljM1ZpWDJ4bFoyRmplU0k2SWpJME56UmpaakJpTWpJeE1UWTRPR0UxTnpJNU4yRmpaVEJsTWpZd1lURTFPVFEwTnpVMFpERTJZakZpWkRReVl6bGtOamMzT1dNNU1EQXpOamM0TURjaUxDSnVZVzFsSWpvaVFXUnRhVzVwYzNSeVlYUnZjaUlzSW01cFkydHVZVzFsSWpvaWNtOXZkQ0lzSW5CeVpXWmxjbkpsWkY5MWMyVnlibUZ0WlNJNkluSnZiM1FpTENKbGJXRnBiQ0k2SW1Ga2JXbHVRR1Y0WVcxd2JHVXVZMjl0SWl3aVpXMWhhV3hmZG1WeWFXWnBaV1FpT25SeWRXVXNJbkJ5YjJacGJHVWlPaUpvZEhSd09pOHZaMlJyTG5SbGMzUTZNekF3TUM5eWIyOTBJaXdpY0dsamRIVnlaU0k2SW1oMGRIQnpPaTh2ZDNkM0xtZHlZWFpoZEdGeUxtTnZiUzloZG1GMFlYSXZNalU0WkRoa1l6a3hObVJpT0dObFlUSmpZV1ppTm1NelkyUXdZMkl3TWpRMlpXWmxNRFl4TkRJeFpHSmtPRE5sWXpOaE16VXdOREk0WTJGaVpHRTBaajl6UFRnd0ptUTlhV1JsYm5ScFkyOXVJaXdpWjNKdmRYQnpYMlJwY21WamRDSTZXeUowYjI5c1ltOTRJaXdpWjJsMGJHRmlMVzl5WnlJc0ltZHVkWGRuWlhRaUxDSkRiMjF0YVhRME5URWlMQ0pxWVhOb2EyVnVZWE1pTENKbWJHbG5hSFJxY3lJc0luUjNhWFIwWlhJaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNdmMyVmpkWEpwZEhraUxDSTBNVEkzTURnaUxDSm5hWFJzWVdJdFpYaGhiWEJzWlhNdlpHVnRieTFuY205MWNDSXNJbU4xYzNSdmJTMXliMnhsY3kxeWIyOTBMV2R5YjNWd0lpd2lORE0wTURRMExXZHliM1Z3TFRFaUxDSTBNelF3TkRRdFozSnZkWEF0TWlJc0ltZHBkR3hoWWkxdmNtY3hJaXdpWjJsMGJHRmlMVzl5Wnk5elpXTjFjbVVpTENKbmFYUnNZV0l0YjNKbkwzTmxZM1Z5WlM5dFlXNWhaMlZ5Y3lJc0ltZHBkR3hoWWkxdmNtY3ZjMlZqZFhKcGRIa3RjSEp2WkhWamRITWlMQ0puYVhSc1lXSXRiM0puTDNObFkzVnlhWFI1TFhCeWIyUjFZM1J6TDJGdVlXeDVlbVZ5Y3lJc0ltTjFjM1J2YlMxeWIyeGxjeTF5YjI5MExXZHliM1Z3TDJGaElpd2lZM1Z6ZEc5dExYSnZiR1Z6TFhKdmIzUXRaM0p2ZFhBdllXRXZZV0ZoSWl3aWJXRnpjMTlwYm5ObGNuUmZaM0p2ZFhCZlh6QmZNVEF3SWwxOS5TWnVfbDd0UTJLa2VvZ3EwejhjUmFEV1BmdjUySlRvLVJraUV4Ym51ZF9scmZyWFhuZVM3N0JJemFHS1hfYnpxNFNNX29PX1E2M0F6SzY2QjFyNkdwN0FDbzREak9VRUlXRVRnN1pCS2NEekVabnJlc0I3a21JX01KNXJmSUpUbW5INzVHT2ZjX3BsNWw4VDg5NlRiYVNoTjZ6U3BhWFhJVkVmaHlVcmZsU1diNGhoQTdIYnd5MmI2bGFYaWFEdjBxcGNuMXVkUFZZTVRzbGw4STVuaV8yeXp1RVBTVlJncmNRb1E0Nk93VkRaSWk5dGxmZFQycU5Wakg2RnhKM21rQmN4dElWamYzX0pZQWF3RkVzY2cydXZRWXdGV2o5VDZMbGVNa25BaDNRRkpKTXJTNm1QcWxYSkdQVUU1cFRRZ3NCSW5mRWlrZm05UFh4ZXpBLUlZNmcifQ=="
+
+ result, err := TokensFromBase64String(s)
+ require.NoError(t, err)
+
+ require.NotNil(t, result)
+ assert.Equal(t, "access_token", result.AccessToken)
+ assert.Equal(t, "Bearer", result.TokenType)
+ assert.Equal(t, "refresh_token", result.RefreshToken)
+ assert.Equal(t, int64(3600), result.ExpiresIn)
+ 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)
+ })
+}