13 "github.com/golang-jwt/jwt/v5"
14 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
15 . "github.com/onsi/gomega"
19 dttSSOTestClientID = "redash-gate"
20 // Shorter than 32 bytes, and contains characters golang.org/x/oauth2 would
21 // URL-escape in Basic auth.
22 dttSSOTestSecret = "s3cr+t/abc=="
25// dttSSOTestServer mimics the SSO's token and check_token endpoints.
26type dttSSOTestServer struct {
28 claims map[string]interface{}
31 validTokens map[string]bool
35func newDTTSSOTestServer() *dttSSOTestServer {
36 s := &dttSSOTestServer{
37 signingKey: []byte(dttSSOTestSecret),
38 validTokens: map[string]bool{},
40 s.Server = httptest.NewServer(http.HandlerFunc(s.handle))
41 u, _ := url.Parse(s.URL)
42 s.claims = map[string]interface{}{
43 "iss": "http://" + u.Host,
44 "aud": dttSSOTestClientID,
45 "sub": "0b5c1e1a-1111-2222-3333-444455556666",
46 "user_id": "0b5c1e1a-1111-2222-3333-444455556666",
47 "email": "admin@example.com",
49 "exp": time.Now().Add(time.Hour).Unix(),
54func (s *dttSSOTestServer) handle(w http.ResponseWriter, r *http.Request) {
57 want := "Basic " + base64.StdEncoding.EncodeToString([]byte(dttSSOTestClientID+":"+dttSSOTestSecret))
58 if r.Header.Get("Authorization") != want {
59 w.WriteHeader(http.StatusUnauthorized)
63 s.lastForm = r.PostForm
64 if r.PostForm.Get("grant_type") == "refresh_token" && !s.validTokens["refresh-"+r.PostForm.Get("refresh_token")] {
65 w.WriteHeader(http.StatusBadRequest)
66 _, _ = w.Write([]byte(`{"error":"invalid_grant","error_description":"Invalid refresh token"}`))
70 access := "access-" + string(rune('0'+s.tokenCounter))
71 refresh := "refresh-" + string(rune('0'+s.tokenCounter))
72 s.validTokens[access] = true
73 s.validTokens["refresh-"+refresh] = true
74 w.Header().Set("Content-Type", "application/json")
75 _ = json.NewEncoder(w).Encode(map[string]interface{}{
76 "access_token": access,
77 "token_type": "bearer",
79 "refresh_token": refresh,
80 "id_token": s.idToken(),
83 case "/oauth/check_token":
84 token := r.Header.Get("Authorization")
85 if len(token) > len("Bearer ") && s.validTokens[token[len("Bearer "):]] {
86 _, _ = w.Write([]byte(`{"user_id":"0b5c1e1a-1111-2222-3333-444455556666","real_user_id":null,"issued_by":"redash-gate"}`))
89 w.WriteHeader(http.StatusUnauthorized)
90 _, _ = w.Write([]byte("Invalid access token"))
92 w.WriteHeader(http.StatusNotFound)
96func (s *dttSSOTestServer) idToken() string {
100 raw, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(s.claims)).SignedString(s.signingKey)
107func testDTTSSOProvider(server *dttSSOTestServer) *DTTSSOProvider {
108 p := NewDTTSSOProvider(&ProviderData{ClientID: dttSSOTestClientID, ClientSecret: dttSSOTestSecret})
109 u, _ := url.Parse(server.URL)
110 updateURL(p.LoginURL, u.Host)
111 updateURL(p.RedeemURL, u.Host)
112 updateURL(p.ValidateURL, u.Host)
116func TestNewDTTSSOProvider(t *testing.T) {
119 providerData := NewDTTSSOProvider(&ProviderData{}).Data()
120 g.Expect(providerData.ProviderName).To(Equal("DTT SSO"))
121 g.Expect(providerData.LoginURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/authorize"))
122 g.Expect(providerData.RedeemURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/token"))
123 g.Expect(providerData.ValidateURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/check_token"))
124 g.Expect(providerData.Scope).To(Equal("openid"))
127func TestDTTSSOProviderRedeem(t *testing.T) {
129 server := newDTTSSOTestServer()
131 p := testDTTSSOProvider(server)
133 s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
134 g.Expect(err).ToNot(HaveOccurred())
135 g.Expect(s.Email).To(Equal("admin@example.com"))
136 g.Expect(s.User).To(Equal("0b5c1e1a-1111-2222-3333-444455556666"))
137 g.Expect(s.Groups).To(Equal([]string{"dtt_admin"}))
138 g.Expect(s.AccessToken).To(Equal("access-1"))
139 g.Expect(s.RefreshToken).To(Equal("refresh-1"))
140 g.Expect(s.IDToken).ToNot(BeEmpty())
141 g.Expect(s.ExpiresOn).ToNot(BeNil())
142 g.Expect(*s.ExpiresOn).To(BeTemporally("~", time.Now().Add(time.Hour), time.Minute))
144 g.Expect(server.lastForm.Get("grant_type")).To(Equal("authorization_code"))
145 g.Expect(server.lastForm.Get("code")).To(Equal("the-code"))
146 g.Expect(server.lastForm.Get("redirect_uri")).To(Equal("https://redash.example.com/oauth2/callback"))
149func TestDTTSSOProviderRedeemRejectsBadIDTokens(t *testing.T) {
150 testCases := map[string]func(s *dttSSOTestServer){
151 "signed with another secret": func(s *dttSSOTestServer) { s.signingKey = []byte("some-other-client-secret") },
152 "other audience": func(s *dttSSOTestServer) { s.claims["aud"] = "frappe-gate" },
153 "other issuer": func(s *dttSSOTestServer) { s.claims["iss"] = "https://evil.example.com" },
154 "expired": func(s *dttSSOTestServer) { s.claims["exp"] = time.Now().Add(-time.Minute).Unix() },
155 "no email": func(s *dttSSOTestServer) { delete(s.claims, "email") },
156 "no id_token": func(s *dttSSOTestServer) { s.claims = nil },
158 for name, tamper := range testCases {
159 t.Run(name, func(t *testing.T) {
161 server := newDTTSSOTestServer()
165 _, err := testDTTSSOProvider(server).Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
166 g.Expect(err).To(HaveOccurred())
171func TestDTTSSOProviderRedeemRejectsUnsignedIDToken(t *testing.T) {
173 server := newDTTSSOTestServer()
175 p := testDTTSSOProvider(server)
177 header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`))
178 payload, _ := json.Marshal(server.claims)
179 unsigned := header + "." + base64.RawURLEncoding.EncodeToString(payload) + "."
181 _, err := p.verifyIDToken(unsigned, dttSSOTestSecret)
182 g.Expect(err).To(HaveOccurred())
185func TestDTTSSOProviderRefreshSession(t *testing.T) {
187 server := newDTTSSOTestServer()
189 p := testDTTSSOProvider(server)
191 s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
192 g.Expect(err).ToNot(HaveOccurred())
193 nonce := []byte("keep-me")
196 server.claims["role"] = "agent"
197 server.claims["email"] = "renamed@example.com"
198 refreshed, err := p.RefreshSession(context.Background(), s)
199 g.Expect(err).ToNot(HaveOccurred())
200 g.Expect(refreshed).To(BeTrue())
201 g.Expect(s.AccessToken).To(Equal("access-2"))
202 g.Expect(s.RefreshToken).To(Equal("refresh-2"))
203 g.Expect(s.Groups).To(Equal([]string{"agent"}))
204 g.Expect(s.Email).To(Equal("renamed@example.com"))
205 g.Expect(s.Nonce).To(Equal(nonce))
206 g.Expect(server.lastForm.Get("grant_type")).To(Equal("refresh_token"))
207 g.Expect(server.lastForm.Get("refresh_token")).To(Equal("refresh-1"))
210func TestDTTSSOProviderRefreshSessionFailures(t *testing.T) {
212 server := newDTTSSOTestServer()
214 p := testDTTSSOProvider(server)
216 refreshed, err := p.RefreshSession(context.Background(), &sessions.SessionState{})
217 g.Expect(err).ToNot(HaveOccurred())
218 g.Expect(refreshed).To(BeFalse())
220 refreshed, err = p.RefreshSession(context.Background(), &sessions.SessionState{RefreshToken: "revoked"})
221 g.Expect(err).To(HaveOccurred())
222 g.Expect(refreshed).To(BeFalse())
225func TestDTTSSOProviderValidateSession(t *testing.T) {
227 server := newDTTSSOTestServer()
229 p := testDTTSSOProvider(server)
231 s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
232 g.Expect(err).ToNot(HaveOccurred())
233 g.Expect(p.ValidateSession(context.Background(), s)).To(BeTrue())
235 delete(server.validTokens, s.AccessToken)
236 g.Expect(p.ValidateSession(context.Background(), s)).To(BeFalse())
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list