package okta import ( "fmt" "net/http" "net/http/httptest" "net/url" "strings" "time" "edge-infra.dev/pkg/edge/api/types" "edge-infra.dev/pkg/edge/api/utils" ) const ( oktaIntrospectionPath = "/v1/introspect?client_id=%s" oktaUserInfoPath = "/v1/userinfo" oktaTokenRefreshPath = "/v1/token" badOktaToken = "bad-okta-token" //nolint:gosec invalidOktaToken = "invalid-okta-token" //nolint:gosec ) func MockServer(cfg *types.OktaConfig) *httptest.Server { server := utils.NewMockHTTPTestServer().AddAllowedContentType(formContentType, jsonContentType).DefaultNotFound() // Okta Introspection API mock server.Post("Okta Introspection", fmt.Sprintf(oktaIntrospectionPath, cfg.ClientID), func(w http.ResponseWriter, r *http.Request) { requestBody := make(map[string]string) err := utils.ReadRequestBody(r, requestBody) if err != nil { panic(err) } if _, exists := requestBody["token"]; !exists || requestBody["token"] == badOktaToken { utils.WriteCustomResponse(w, http.StatusUnauthorized, []byte("Invalid Credentials")) } else if _, exists := requestBody["token"]; !exists || requestBody["token"] == invalidOktaToken { response := MockIntrospectionInactiveResponse(server.Server.URL, cfg.ClientID) utils.WriteJSON(w, response) } else { response := MockIntrospectionResponse(server.Server.URL, cfg.ClientID) utils.WriteJSON(w, response) } }, func(_ http.ResponseWriter, r *http.Request) bool { queries, err := url.ParseQuery(r.URL.RawQuery) if err != nil { return false } val, exists := queries["client_id"] if exists { return val[0] == cfg.ClientID } return exists }) // Okta UserInfo API mock server.Get("Okta UserInfo", oktaUserInfoPath, func(w http.ResponseWriter, r *http.Request) { if _, exists := r.Header["Authorization"]; !exists || strings.Contains(r.Header["Authorization"][0], badOktaToken) { utils.WriteCustomResponse(w, http.StatusUnauthorized, []byte("Invalid Credentials")) } else { response := MockUserInfoResponse() utils.WriteJSON(w, response) } }, func(_ http.ResponseWriter, r *http.Request) bool { return r.Method == http.MethodGet }) server.Post("Okta Token Refresh", oktaTokenRefreshPath, func(w http.ResponseWriter, _ *http.Request) { response := MockTokenRefreshResponse() utils.WriteJSON(w, response) }, func(_ http.ResponseWriter, r *http.Request) bool { return r.Method == http.MethodPost && strings.Contains(r.RequestURI, oktaTokenRefreshPath) }) cfg.OktaIssuer = server.Server.URL return server.Server } func MockIntrospectionResponse(issuer, clientID string) *IntrospectionResponse { currentTime := time.Now() return &IntrospectionResponse{ Active: true, Scope: "profile email openid", Username: "test-user", Expires: int(currentTime.Add(15 * time.Minute).UnixNano()), IssuedAt: int(currentTime.UnixNano()), Sub: "test@ncr.com", Aud: "api://default", Issuer: issuer, Jti: "AT.IR2_7zD62pLUDqsddXeccrcpf98LvhjnsqKlsIgnXcNFFK2s", TokenType: "Bearer", ClientID: clientID, UID: "vguhjsbxvksbwvdid", Groups: []string{"Everyone"}, Type: "user", Email: "test@ncr.com", } } func MockIntrospectionInactiveResponse(issuer, clientID string) *IntrospectionResponse { currentTime := time.Now() return &IntrospectionResponse{ Active: false, Scope: "profile email openid", Username: "test-user", Expires: int(currentTime.Add(15 * time.Minute).UnixNano()), IssuedAt: int(currentTime.UnixNano()), Sub: "test@ncr.com", Aud: "api://default", Issuer: issuer, Jti: "AT.IR2_7zD62pLUDqsddXeccrcpf98LvhjnsqKlsIgnXcNFFK2s", TokenType: "Bearer", ClientID: clientID, UID: "vguhjsbxvksbwvdid", Groups: []string{"Everyone"}, Type: "user", Email: "test@ncr.com", } } func MockUserInfoResponse() *UserInfo { return &UserInfo{ Sub: "test@ncr.com", Name: "John Doe", Locale: "en_US", Email: "test@ncr.com", PreferredUsername: "test@ncr.com", GivenName: "John", FamilyName: "Doe", ZoneInfo: "America/Los_Angeles", EmailVerified: true, } } func MockTokenRefreshResponse() *RefreshResponse { return &RefreshResponse{ TokenType: "refresh-token", ExpiresIn: float64(15 * time.Minute), AccessToken: "good-okta-token", Scope: "offline_access openid", RefreshToken: "refresh-token", IDToken: "id-token", } }