Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/public-api-server/pkg/oidc/router_test.go
2500 views
1
// Copyright (c) 2022 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 oidc
6
7
import (
8
"net/http"
9
"net/http/httptest"
10
"testing"
11
"time"
12
13
"github.com/go-chi/chi/v5"
14
"github.com/golang-jwt/jwt/v5"
15
"github.com/stretchr/testify/require"
16
"golang.org/x/oauth2"
17
18
goidc "github.com/coreos/go-oidc/v3/oidc"
19
)
20
21
func TestRoute_start(t *testing.T) {
22
// setup fake OIDC service
23
idpUrl := newFakeIdP(t)
24
25
// setup test server with client routes
26
baseUrl, _, configId, _ := newTestServer(t, testServerParams{
27
issuer: idpUrl,
28
state: StateParams{
29
ReturnToURL: "",
30
},
31
})
32
33
// go to /start
34
// don't follow redirect
35
client := &http.Client{
36
Timeout: 10 * time.Second,
37
CheckRedirect: func(req *http.Request, via []*http.Request) error {
38
return http.ErrUseLastResponse
39
},
40
}
41
resp, err := client.Get(baseUrl + "/oidc/start?id=" + configId + "&activate=true")
42
require.NoError(t, err)
43
defer resp.Body.Close()
44
45
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
46
redirectUrl, err := resp.Location()
47
require.NoError(t, err)
48
require.Contains(t, redirectUrl.String(), idpUrl, "should redirect to IdP")
49
50
state := redirectUrl.Query().Get("state")
51
require.NotEmpty(t, state, "should contain state param")
52
53
token, _, err := new(jwt.Parser).ParseUnverified(state, jwt.MapClaims{})
54
require.NoError(t, err, "state param should be a JWT")
55
claims, ok := token.Claims.(jwt.MapClaims)
56
require.True(t, ok)
57
58
stateParams, ok := claims["stateParams"].(map[string]interface{})
59
require.True(t, ok, "JWT is missing 'stateParams'")
60
require.Equal(t, true, stateParams["activate"], "`activate` is missing in state")
61
require.Equal(t, "/", stateParams["returnTo"], "`returnTo` is missing in state")
62
}
63
64
func TestRoute_callback(t *testing.T) {
65
// setup fake OIDC service
66
idpUrl := newFakeIdP(t)
67
68
// setup test server with client routes
69
baseUrl, stateParam, _, service := newTestServer(t, testServerParams{
70
clientID: "client-id",
71
issuer: idpUrl,
72
state: StateParams{
73
ReturnToURL: "/relative/url/to/some/page",
74
},
75
})
76
state, err := service.encodeStateParam(*stateParam)
77
require.NoError(t, err)
78
79
// hit the /callback endpoint
80
client := &http.Client{
81
Timeout: 10 * time.Second,
82
CheckRedirect: func(req *http.Request, via []*http.Request) error {
83
return http.ErrUseLastResponse
84
},
85
}
86
req, err := http.NewRequest("GET", baseUrl+"/oidc/callback?code=123&state="+state, nil)
87
require.NoError(t, err)
88
req.AddCookie(&http.Cookie{
89
Name: "state", Value: state, MaxAge: 60,
90
})
91
req.AddCookie(&http.Cookie{
92
Name: "nonce", Value: "111", MaxAge: 60,
93
})
94
resp, err := client.Do(req)
95
require.NoError(t, err)
96
defer resp.Body.Close()
97
98
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode, "callback should response with redirect (307)")
99
require.NotEmpty(t, resp.Cookies(), "missing cookies on redirect")
100
require.Equal(t, "test-cookie", resp.Cookies()[0].Name, "missing cookie on redirect")
101
102
url, err := resp.Location()
103
require.NoError(t, err)
104
require.Equal(t, "/relative/url/to/some/page", url.Path, "callback redirects properly")
105
106
}
107
108
func TestRoute_callback_verify_only(t *testing.T) {
109
// setup fake OIDC service
110
idpUrl := newFakeIdP(t)
111
112
// setup test server with client routes
113
baseUrl, stateParam, _, service := newTestServer(t, testServerParams{
114
clientID: "client-id",
115
issuer: idpUrl,
116
state: StateParams{
117
ReturnToURL: "/relative/url/to/some/page",
118
Verify: true,
119
},
120
})
121
state, err := service.encodeStateParam(*stateParam)
122
require.NoError(t, err)
123
124
// hit the /callback endpoint
125
client := &http.Client{
126
Timeout: 10 * time.Second,
127
CheckRedirect: func(req *http.Request, via []*http.Request) error {
128
return http.ErrUseLastResponse
129
},
130
}
131
req, err := http.NewRequest("GET", baseUrl+"/oidc/callback?code=123&state="+state, nil)
132
require.NoError(t, err)
133
req.AddCookie(&http.Cookie{
134
Name: "state", Value: state, MaxAge: 60,
135
})
136
req.AddCookie(&http.Cookie{
137
Name: "nonce", Value: "111", MaxAge: 60,
138
})
139
resp, err := client.Do(req)
140
require.NoError(t, err)
141
defer resp.Body.Close()
142
143
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode, "callback should response with redirect (307)")
144
require.Len(t, resp.Cookies(), 0, "unexpected session cookie on verify request")
145
146
url, err := resp.Location()
147
require.NoError(t, err)
148
require.Equal(t, "/relative/url/to/some/page", url.Path, "callback redirects properly")
149
}
150
151
type testServerParams struct {
152
issuer string
153
clientID string
154
state StateParams
155
}
156
157
func newTestServer(t *testing.T, params testServerParams) (url string, state *StateParams, configId string, oidcService *Service) {
158
router := chi.NewRouter()
159
oidcService, dbConn := setupOIDCServiceForTests(t)
160
router.Mount("/oidc", Router(oidcService))
161
162
ts := httptest.NewServer(router)
163
url = ts.URL
164
165
oidcConfig := &goidc.Config{
166
ClientID: params.clientID,
167
SkipClientIDCheck: true,
168
SkipIssuerCheck: true,
169
SkipExpiryCheck: true,
170
InsecureSkipSignatureCheck: true,
171
}
172
oauth2Config := &oauth2.Config{
173
ClientID: params.clientID,
174
ClientSecret: "secret",
175
Scopes: []string{goidc.ScopeOpenID, "profile", "email"},
176
}
177
clientConfig := &ClientConfig{
178
Issuer: params.issuer,
179
OAuth2Config: oauth2Config,
180
VerifierConfig: oidcConfig,
181
}
182
config, _ := createConfig(t, dbConn, clientConfig)
183
configId = config.ID.String()
184
185
stateParam := &StateParams{
186
ClientConfigID: configId,
187
ReturnToURL: params.state.ReturnToURL,
188
Activate: params.state.Activate,
189
Verify: params.state.Verify,
190
}
191
192
return url, stateParam, configId, oidcService
193
}
194
195