diff --git a/context/auth_test.go b/context/auth_test.go index 8cb37132342..5b6a0e104a7 100644 --- a/context/auth_test.go +++ b/context/auth_test.go @@ -3,8 +3,10 @@ package context import ( "database/sql" "fmt" + "github.com/dgrijalva/jwt-go" "github.com/ory-am/dockertest" - "github.com/ory-am/hydra/jwt" + hjwt "github.com/ory-am/hydra/jwt" + "github.com/ory-am/ladon/policy" ladon "github.com/ory-am/ladon/policy/postgres" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -47,32 +49,32 @@ func TestNewContextFromAuthorization(t *testing.T) { }{ { "1", - []byte(jwt.TestCertificates[0][1]), - []byte(jwt.TestCertificates[1][1]), + []byte(hjwt.TestCertificates[0][1]), + []byte(hjwt.TestCertificates[1][1]), // {"foo": "bar"} "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJmb28iOiJiYXIifQ.FhkiHkoESI_cG3NPigFrxEk9Z60_oXrOT2vGm9Pn6RDgYNovYORQmmA0zs1AoAOf09ly2Nx2YAg6ABqAYga1AcMFkJljwxTT5fYphTuqpWdy4BELeSYJx5Ty2gmr8e7RonuUztrdD5WfPqLKMm1Ozp_T6zALpRmwTIW0QPnaBXaQD90FplAg46Iy1UlDKr-Eupy0i5SLch5Q-p2ZpaL_5fnTIUDlxC3pWhJTyx_71qDI-mAA_5lE_VdroOeflG56sSmDxopPEG3bFlSu1eowyBfxtu0_CuVd-M42RU75Zc4Gsj6uV77MBtbMrf4_7M_NUTSgoIF3fRqxrj0NzihIBg", false, }, { "2", - []byte(jwt.TestCertificates[0][1]), - []byte(jwt.TestCertificates[1][1]), + []byte(hjwt.TestCertificates[0][1]), + []byte(hjwt.TestCertificates[1][1]), // {"subject": "nonexistent"} "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWJqZWN0Ijoibm9uZXhpc3RlbnQifQ.jDUnvVMQHrhuIRUr8qAJ0g-ZKArdiJ21LAPDktmV56KFknX712Yxdder78YjEjxvGOvgtxLpCiay0cV5pvcWLuFW65Ys1P1SwdmdebtWfiGQwBy2Ggm3MrHjD_-r5JNAxFZjFZfZ1Fk-JlSZ97r8S7gYfDSAkxhpDmDy5Bm8e5_xsGDNp8dByuXop7QEtJb_igaa0APWa2ZOp3oTgxjD4CP6ZX6N5fGjtwjJWx5wHt7JaKXq8CRG8elm7LnNezYyJxeHECVctQGVv3HUjJxKf0l7wZXbG87BrG2M7otT8Py2sJP8X4wYL0DEsbErkEieV4D-KEBqpkvfXOrDGMFNRQ", false, }, { "3", - []byte(jwt.TestCertificates[0][1]), - []byte(jwt.TestCertificates[1][1]), + []byte(hjwt.TestCertificates[0][1]), + []byte(hjwt.TestCertificates[1][1]), // not a valid token "Bearer eyJ0eXAaOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJzdWJqZWN0IjoiMTMyIn0.WDC51GK5wIQPd00MqLjFy3AU3gNsvsCpWk5e8RObVxBqcYAdv-UwMfEUAFE6Y50C5pQ1t8_LHfxJYNfcW3fj_x5FXckdbqvpXHi-psxuDwk_rancpjZQegcutqYRH37_lnJ8lIq65ZgxnyYnQKGOMl3w7etK1gOvqEcP_eHn8HG0jeVk0SDZm82x0JXSk0lrVEEjWmWYtXEsLz0E4clNPUW37K9eyjYFKnyVCIPfmGwTlkDLjANsyu0P6kFiV28V1_XedtJXDI3MmG2SxSHogDhZJLb298JBwod0d6wTyygI9mUbX-C0PklTJTxIhSs7Pc6unNlWnbyL8Z4FJrdSEw", false, }, { "4", - []byte(jwt.TestCertificates[0][1]), - []byte(jwt.TestCertificates[1][1]), + []byte(hjwt.TestCertificates[0][1]), + []byte(hjwt.TestCertificates[1][1]), // { // "exp": "2099-10-31T15:03:52.4620974+01:00", // "iat": "2014-10-31T13:03:52.4620974+01:00", @@ -84,8 +86,8 @@ func TestNewContextFromAuthorization(t *testing.T) { }, { "5", - []byte(jwt.TestCertificates[0][1]), - []byte(jwt.TestCertificates[1][1]), + []byte(hjwt.TestCertificates[0][1]), + []byte(hjwt.TestCertificates[1][1]), "", false, }, @@ -93,7 +95,7 @@ func TestNewContextFromAuthorization(t *testing.T) { message := "ok" ctx := context.Background() - j := jwt.New(c.privateKey, c.publicKey) + j := hjwt.New(c.privateKey, c.publicKey) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx = NewContextFromAuthorization(ctx, r, j, ladonStore) @@ -116,3 +118,32 @@ func TestNewContextFromAuthorization(t *testing.T) { assert.Equal(t, message+"\n", string(result)) } } + +func TestGetters(t *testing.T) { + assert.False(t, IsAuthenticatedFromContext(context.Background())) + _, err := PoliciesFromContext(context.Background()) + assert.NotNil(t, err) + _, err = SubjectFromContext(context.Background()) + assert.NotNil(t, err) + _, err = TokenFromContext(context.Background()) + assert.NotNil(t, err) + + ctx := context.Background() + claims := hjwt.ClaimsCarrier{"sub": "peter"} + token := &jwt.Token{Valid: true} + policies := []policy.Policy{} + ctx = NewContextFromAuthValues(ctx, claims, token, policies) + + assert.True(t, IsAuthenticatedFromContext(ctx)) + policiesContext, err := PoliciesFromContext(ctx) + assert.Nil(t, err) + assert.Equal(t, policies, policiesContext) + + subjectContext, err := SubjectFromContext(ctx) + assert.Nil(t, err) + assert.Equal(t, claims.GetSubject(), subjectContext) + + tokenContext, err := TokenFromContext(ctx) + assert.Nil(t, err) + assert.Equal(t, token, tokenContext) +} diff --git a/jwt/claims_test.go b/jwt/claims_test.go index fc36c1088cb..ae6f4dded41 100644 --- a/jwt/claims_test.go +++ b/jwt/claims_test.go @@ -1,7 +1,6 @@ -package jwt_test +package jwt import ( - . "github.com/ory-am/hydra/jwt" "github.com/pborman/uuid" "github.com/stretchr/testify/assert" "testing" @@ -26,5 +25,8 @@ func TestClaimsCarrier(t *testing.T) { assert.Equal(t, c.audience, carrier.GetAudience(), "Case %d", k) assert.Equal(t, c.notBefore, carrier.GetNotBefore(), "Case %d", k) assert.Equal(t, c.issuedAt, carrier.GetIssuedAt(), "Case %d", k) + assert.Empty(t, carrier.getAsString("doesnotexist"), "Case %d", k) + assert.Equal(t, time.Time{}, carrier.getAsTime("doesnotexist"), "Case %d", k) + assert.NotEmpty(t, carrier.String(), "Case %d", k) } } diff --git a/jwt/jwt_test.go b/jwt/jwt_test.go index bbc869ae018..46d27b9bf9a 100644 --- a/jwt/jwt_test.go +++ b/jwt/jwt_test.go @@ -58,6 +58,12 @@ func TestSignRejectsAlgAndTypHeader(t *testing.T) { } } +func TestVerifyPassesHeaderAlgInjection(t *testing.T) { + j := New([]byte(TestCertificates[0][1]), []byte(TestCertificates[1][1])) + _, err := j.VerifyToken([]byte("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.e30.BZXqpeQKnhMtyln2NnoNTUoz_BmyNR-vPHmCxfEpnzCegPZJeCPQiFmn6k7hYhYeWFhH0NhH7-c22-bAf656Esy5qdcxCrwgYSyXAbGQ4C9YsinGcliXeQYcYgOmj8gS2K5Xbj4g9StOB7KywZ_QTJc6FVOqqcgikYVtVA6bMKRrYB4ZS6ZFPdWYTWZ-qOyEg6V7o6-IWmCpEZXlyBgyfAanQkTISMyYuJFPCnFhjnmBUyz0JrWE4gQutOk1-Yw2ikym4GQDrkxrKnnmC_lSJ5I1daxq09oMNj4WRsckktOU64Wuk0PRq_CEpSIA7uHE-Ecgn4ZvRgyLaR1B8S2pAw")) + assert.NotNil(t, err) +} + func TestSignAndVerify(t *testing.T) { for i, c := range []struct { private []byte diff --git a/oauth/connection/handler/handler.go b/oauth/connection/handler/handler.go index 2797be92d5b..5133ad8c108 100644 --- a/oauth/connection/handler/handler.go +++ b/oauth/connection/handler/handler.go @@ -1,7 +1,6 @@ package handler import ( - "code.google.com/p/go-uuid/uuid" "encoding/json" "fmt" "github.com/asaskevich/govalidator" @@ -10,6 +9,7 @@ import ( "github.com/ory-am/hydra/middleware" . "github.com/ory-am/hydra/oauth/connection" . "github.com/ory-am/hydra/pkg" + "github.com/pborman/uuid" "golang.org/x/net/context" "net/http" ) diff --git a/oauth/connection/handler/handler_test.go b/oauth/connection/handler/handler_test.go index 456f90fd71e..3f05c7cef98 100644 --- a/oauth/connection/handler/handler_test.go +++ b/oauth/connection/handler/handler_test.go @@ -104,6 +104,11 @@ var data = map[string]*DefaultConnection{ LocalSubject: "steve", }, "fail": &DefaultConnection{}, + "fail-validation": &DefaultConnection{ + Provider: "", + RemoteSubject: "", + LocalSubject: "", + }, } func TestCreateGetDeleteGet(t *testing.T) { @@ -112,6 +117,7 @@ func TestCreateGetDeleteGet(t *testing.T) { {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["fail"]}, createData: data["fail"], statusCreate: http.StatusForbidden}, {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["ok-max"], statusCreate: http.StatusOK, statusGet: http.StatusForbidden}, {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["fail"], statusCreate: http.StatusBadRequest}, + {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"]}, createData: data["fail-validation"], statusCreate: http.StatusBadRequest}, {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-create"], policies["pass-get"]}, createData: data["ok-zac"], statusCreate: http.StatusOK, statusGet: http.StatusOK, statusDelete: http.StatusForbidden}, {subject: "peter", token: jwt.Token{Valid: true}, policies: []policy.Policy{policies["pass-all"]}, createData: data["ok-steve"], statusCreate: http.StatusOK, statusGet: http.StatusOK, statusDelete: http.StatusAccepted, statusGetAfterDelete: http.StatusNotFound}, } { @@ -146,6 +152,9 @@ func TestCreateGetDeleteGet(t *testing.T) { return } + resp, body, _ = request.Post(connectionsURL).Send(*c.createData).End() + require.Equal(t, http.StatusInternalServerError, resp.StatusCode, "case %d: %s", k, body) + resp, body, _ = request.Delete(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End() require.Equal(t, c.statusDelete, resp.StatusCode, "case %d: %s", k, body) if resp.StatusCode != http.StatusAccepted { @@ -154,6 +163,9 @@ func TestCreateGetDeleteGet(t *testing.T) { resp, body, _ = request.Get(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End() require.Equal(t, c.statusGetAfterDelete, resp.StatusCode, "case %d: %s", k, body) + + resp, body, _ = request.Delete(fmt.Sprintf("%s/oauth2/connections/%s", ts.URL, conn.ID)).End() + require.Equal(t, http.StatusNotFound, resp.StatusCode, "case %d: %s", k, body) }() } } diff --git a/oauth/handler/handler.go b/oauth/handler/handler.go index cbc2435d68c..a85810876b9 100644 --- a/oauth/handler/handler.go +++ b/oauth/handler/handler.go @@ -70,11 +70,11 @@ func (h *Handler) IntrospectHandler(w http.ResponseWriter, r *http.Request) { bearer := osin.CheckBearerAuth(r) if bearer == nil { log.WithField("introspect", "fail").Warn("No authorization header given.") - http.Error(w, "No bearer given.", http.StatusForbidden) + http.Error(w, "No bearer given.", http.StatusUnauthorized) return } else if bearer.Code == "" { log.WithField("introspect", "fail").Warn("No authorization bearer is empty.") - http.Error(w, "No bearer token given.", http.StatusForbidden) + http.Error(w, "No bearer token given.", http.StatusUnauthorized) return } diff --git a/oauth/handler/handler_test.go b/oauth/handler/handler_test.go index bb4a251d623..67394a39840 100644 --- a/oauth/handler/handler_test.go +++ b/oauth/handler/handler_test.go @@ -276,7 +276,7 @@ func TestIntrospect(t *testing.T) { ts := httptest.NewServer(router) defer ts.Close() - config := *configs["working"] + config := configs["working"] user := logins["working"] clientConfig := clientcredentials.Config{ ClientID: config.ClientID, @@ -286,28 +286,71 @@ func TestIntrospect(t *testing.T) { } config.Endpoint = oauth2.Endpoint{AuthURL: ts.URL + "/oauth2/auth", TokenURL: ts.URL + "/oauth2/token"} - access, err := clientConfig.Token(oauth2.NoContext) - require.Nil(t, err) - verify, err := config.PasswordCredentialsToken(oauth2.NoContext, user.Username, user.Password) - require.Nil(t, err) + access, _ := clientConfig.Token(oauth2.NoContext) + verify, _ := config.PasswordCredentialsToken(oauth2.NoContext, user.Username, user.Password) - client := &http.Client{} - form := url.Values{} - form.Add("token", access.AccessToken) - - req, err := http.NewRequest("POST", ts.URL+"/oauth2/introspect", strings.NewReader(form.Encode())) - require.Nil(t, err) + for k, c := range []*struct { + accessToken string + code int + pass bool + }{ + {"Bearer " + verify.AccessToken, http.StatusOK, true}, + {"", http.StatusUnauthorized, false}, + {"Bearer ", http.StatusUnauthorized, false}, + {"Bearer invalid", http.StatusForbidden, false}, + {"Bearer invalid", http.StatusForbidden, false}, + {"Bearer invalid", http.StatusForbidden, false}, + + // "exp": "2012-04-23T18:25:43.511Z" + {"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDEyLTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.YPCfgNDs-UT6vNqh6095cXiMe0jcA9HjHuLi6hK6YBPsEHwHFniFGXAYt1PpPabBHAz7lQQ8zZao6LrVXkfz7PLbeQZl3KY0SUb-Wb0eEDjX4naEdm20whrYMZQ36VcTMT-FsGk5MB-nIYKq3iX6FMhumV8StjpC0jrM14488lPwLXihC1uITQBNVFEyXV_emhfuyojWEcEq899oE_vVRd7pTOmIhU8dFEAonoLZyPTKzSfvqaurPeySA5ttA-TTMTxZNzGVxWV4cwYHlhTXfS57zoSF_EN_PULTqMepUe8RC9AFnwyvNAa5e4nxQG5yO6b7cUGa0vSCD5FPbNBh-w", http.StatusForbidden, false}, + + // { + // "exp": "2099-04-23T18:25:43.511Z", + // "nbf": "2099-04-23T18:25:43.511Z" + // } + {"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.hCuvBuiwEjjTbL8NMfEe6exDaRUeQIHodTNc5uBdY1lxmJWfFPh2zykuEvinqTprQe2CPRmL3Dk6jX3pcnigg7IjMX-EZueOnJc229gwjmJJiIGuUJOV3bLc-0xQ3cu6FCRc2NgOEh6Nq6Jh8G7ko4Du4gGrFsn97kbzAUYyns98T8442p0YXdQF-KVCc87fCkdr6OTsbfomy7jUDLCWptyJqREOoBll-nzyFWTxGHgoH_DmHft64SwvsvRafqZv9Q48bRzr857ps6OjEPncjRTriAsJa-p7aPKO2e7LXLKpopcaNwC09RNteAO4XPc2_M-IrYf6a02UzgSmOkIZUg", http.StatusForbidden, false}, + + // { + // "exp": "2099-04-23T18:25:43.511Z", + // "iat": "2000-04-23T18:25:43.511Z", + // "nbf": "2099-04-23T18:25:43.511Z" + // } + {"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoifQ.WtRurXoCy4kHPxnaL5ccPaeHIaDogXRFE6mqyF8nVTSsv6E7FaJg4IiYylxa44ty8GRMYn7c2CSyQefTVauqjJm8b0Rpu4biIeyCQRzwTZZzqZbc6irdWYsJu4DkwfAU0yP2EaLEtQOG3scnDpmtyCp7NvDAi8XlVeytOSHjqyJMWzqO_z5eU4e2Ap-3wkLo4P9_W1W3Tx_V0xQR2VaOXtVjEa_VS36rAMBy6WAvYQrYNlvBAA6OBfqg2uvKUfmEoE6MchkFxHFTSGBmI2boDfF2XGlyLn0di7gIBG-udXDv_zaVp4BtuswygTskV5d2i3pvLGP6UuJJhc7VVOAoPw", http.StatusForbidden, false}, + + // { + // "exp": "2099-04-23T18:25:43.511Z", + // "iat": "2000-04-23T18:25:43.511Z", + // "nbf": "2000-04-23T18:25:43.511Z", + // "aud": "wrong-audience" + // } + {"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJhdWQiOiJ3cm9uZy1hdWRpZW5jZSJ9.rF4JqVpawgHcg_H2hAAsEI2GUxzxCote4pUlruK9hLF-Dv-YSeEmMcFBhfxgsFuDCJotUCG6v8EhwI4u2wxGQHzLz70a-0AEZLQBccCfF_V4qAk8B7M5z2fO7xtEy8RkB2pZKCHbJ1f_6MSM_EyV6r4oiwedveBSsLKcjDhWE3_wExmtmtZaujJy53gR8Wh7BnUt6pl95_d7OMFjGEp1C_N0f3xd9SizIZ-qlIwHiX4xLHtvTZIjdmfyzXxPm_MK_aMOXmX0F6DQn5tgMzAggEdKSD6YdU8HM256zLQeddczrrDI5P3SASiBJ6MCUM4AzbvoFuFAilQi0WzpLpmlJw", http.StatusOK, false}, + + // { + // "exp": "2099-04-23T18:25:43.511Z", + // "iat": "2000-04-23T18:25:43.511Z", + // "nbf": "2000-04-23T18:25:43.511Z", + // "aud": "tests" + // } + {"Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIyMDk5LTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJpYXQiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJuYmYiOiIyMDAwLTA0LTIzVDE4OjI1OjQzLjUxMVoiLCJhdWQiOiJ0ZXN0cyJ9.NQZCoKU2qoC-_VFi-_8fQDzObeQrnld9wyaqF0jYHL_wqROn5VumCDVl1oxMN7g-L9wqo5U-xUXf1HS_Ae6CLDFlkbd6dI-h1_l7_ALn_L_GoxQsEo2lQUDQ-Q4eqlLabc764cTYFXd5EwcsZMHWs5ZFCeMOv3exfeTmg8E9e1FiyuTuKVjvMxL-ZCh113nzXEGFr6GRzqjL6VSnJPDX0Pv78R9tnL6CqWbCuDBlIPOccbpWLuWF0yKjV-OyvcWpjkLIVtAbrimi3A7cNUI_V3EJm9Y4tr8e6hv9zViPNbhycmqvOp-vur2k64PrzeMcbuj7TFRCJg2V3moPJF3NtQ", http.StatusOK, true}, + } { - req.Header.Add("Authorization", "Bearer "+verify.AccessToken) - res, err := client.Do(req) - require.Nil(t, err) + client := &http.Client{} + form := url.Values{} + form.Add("token", access.AccessToken) - body, err := ioutil.ReadAll(res.Body) - require.Nil(t, err) - require.Equal(t, http.StatusOK, res.StatusCode, "%v", body) + req, _ := http.NewRequest("POST", ts.URL+"/oauth2/introspect", strings.NewReader(form.Encode())) + if c.accessToken != "" { + req.Header.Add("Authorization", c.accessToken) + } + res, _ := client.Do(req) + body, _ := ioutil.ReadAll(res.Body) + require.Equal(t, c.code, res.StatusCode, "Case %d: %s", k, body) + if res.StatusCode != http.StatusOK { + continue + } - var result map[string]interface{} - require.Nil(t, json.Unmarshal(body, &result)) - assert.True(t, result["active"].(bool)) - t.Logf("Got token: %s", body) + var result map[string]interface{} + require.Nil(t, json.Unmarshal(body, &result)) + assert.Equal(t, c.pass, result["active"].(bool), "Case %d", k) + } } diff --git a/oauth/provider/provider_test.go b/oauth/provider/provider_test.go new file mode 100644 index 00000000000..069a6c9c8fa --- /dev/null +++ b/oauth/provider/provider_test.go @@ -0,0 +1,30 @@ +package provider + +import ( + "github.com/RangelReale/osin" + "github.com/stretchr/testify/assert" + "golang.org/x/oauth2" + "testing" +) + +func TestGetAuthCodeURL(t *testing.T) { + conf := oauth2.Config{ + ClientID: "remote-client", + ClientSecret: "secret", + Endpoint: oauth2.Endpoint{ + AuthURL: "http://third-party/oauth2/auth", + TokenURL: "http://third-party/oauth2/token", + }, + RedirectURL: "http://hydra/oauth2/auth", + Scopes: []string{"scope"}, + } + ar := &osin.AuthorizeRequest{ + Type: osin.CODE, + Client: &osin.DefaultClient{Id: "client", Secret: "secret"}, + Scope: "scope", + RedirectUri: "http://remote/callback", + State: "state", + } + url := GetAuthCodeURL(conf, ar, "foo") + assert.Equal(t, "http://third-party/oauth2/auth?client_id=remote-client&redirect_uri=http%3A%2F%2Fhydra%2Foauth2%2Fauth%3Focl%3Dclient%26opr%3Dfoo%26ord%3Dhttp%253A%252F%252Fremote%252Fcallback%26osc%3Dscope%26ost%3Dstate%26otp%3Dcode&response_type=code&scope=scope&state=state", url) +} diff --git a/pkg/writeJSON.go b/pkg/writeJSON.go index 05d0d95b43b..a8611a05cbf 100644 --- a/pkg/writeJSON.go +++ b/pkg/writeJSON.go @@ -15,4 +15,5 @@ func WriteJSON(rw http.ResponseWriter, connection interface{}) { rw.Header().Set("Content-Type", "application/json") rw.Write(js) + rw.WriteHeader(http.StatusOK) } diff --git a/pkg/writeJSON_test.go b/pkg/writeJSON_test.go new file mode 100644 index 00000000000..5893a242868 --- /dev/null +++ b/pkg/writeJSON_test.go @@ -0,0 +1,42 @@ +package pkg + +import ( + "github.com/stretchr/testify/assert" + "net/http" + "testing" +) + +type rw struct { + written []byte + code int + header http.Header +} + +func (r *rw) Header() http.Header { + return r.header +} + +func (r *rw) Write(w []byte) (int, error) { + r.written = w + return 0, nil +} + +func (r *rw) WriteHeader(c int) { + r.code = c +} + +func TestWriteJSON(t *testing.T) { + r := &rw{header: http.Header{}} + js := struct { + Foo string `json:"foo"` + }{ + Foo: "bar", + } + WriteJSON(r, js) + assert.Equal(t, http.StatusOK, r.code) + assert.Equal(t, `{"foo":"bar"}`, string(r.written)) + assert.Equal(t, r.Header().Get("Content-Type"), "application/json") + + WriteJSON(r, func() {}) + assert.Equal(t, http.StatusInternalServerError, r.code) +}