Skip to content

Commit b18b2ea

Browse files
paskalumputun
authored andcommitted
fix Content-Type header not being set for error responses
Replace WriteHeader + RenderJSON pattern with EncodeJSON to ensure Content-Type: application/json header is set before WriteHeader is called. This fixes go-pkgz/rest#38 where responses had text/plain content-type.
1 parent fe069ee commit b18b2ea

20 files changed

Lines changed: 97 additions & 103 deletions

File tree

_example/main.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ func main() {
4949
AvatarStore: avatar.NewLocalFS("/tmp/demo-auth-service"), // stores avatars locally
5050
AvatarResizeLimit: 200, // resizes avatars to 200x200
5151
ClaimsUpd: token.ClaimsUpdFunc(func(claims token.Claims) token.Claims { // modify issued token
52-
if claims.User != nil && claims.User.Name == "dev_admin" { // set attributes for dev_admin
52+
if claims.User != nil && claims.User.Name == "dev_admin" { // set attributes for dev_admin
5353
claims.User.SetAdmin(true)
5454
claims.User.SetStrAttr("custom-key", "some value")
5555
}
@@ -144,7 +144,7 @@ func main() {
144144
devAuthServer.Run(context.Background())
145145
}()
146146

147-
// Example: start custom oauth2 server, add to handlers
147+
// example: start custom oauth2 server, add to handlers
148148
srv := initGoauth2Srv()
149149
sopts := provider.CustomServerOpt{
150150
URL: "http://127.0.0.1:9096",
@@ -154,11 +154,11 @@ func main() {
154154
// create custom provider and prepare params for handler
155155
prov := provider.NewCustomServer(srv, sopts)
156156

157-
// Start server
157+
// start server
158158
go prov.Run(context.Background())
159159
service.AddCustomProvider("custom123", auth.Client{Cid: "cid", Csecret: "csecret"}, prov.HandlerOpt)
160160

161-
// Example: add different oauth2 provider
161+
// example: add different oauth2 provider
162162
c := auth.Client{
163163
Cid: os.Getenv("AEXMPL_BITBUCKET_CID"),
164164
Csecret: os.Getenv("AEXMPL_BITBUCKET_CSEC"),

auth.go

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ func (s *Service) Handlers() (authHandler, avatarHandler http.Handler) {
172172
// allow logout without specifying provider
173173
if elems[len(elems)-1] == "logout" {
174174
if len(s.providers) == 0 {
175-
w.WriteHeader(http.StatusBadRequest)
176-
rest.RenderJSON(w, rest.JSON{"error": "providers not defined"})
175+
_ = rest.EncodeJSON(w, http.StatusBadRequest, rest.JSON{"error": "providers not defined"})
177176
return
178177
}
179178
s.providers[0].Handler(w, r)
@@ -184,12 +183,11 @@ func (s *Service) Handlers() (authHandler, avatarHandler http.Handler) {
184183
if elems[len(elems)-1] == "user" {
185184
claims, _, err := s.jwtService.Get(r)
186185
if err != nil || claims.User == nil {
187-
w.WriteHeader(http.StatusUnauthorized)
188186
msg := "user is nil"
189187
if err != nil {
190188
msg = err.Error()
191189
}
192-
rest.RenderJSON(w, rest.JSON{"error": msg})
190+
_ = rest.EncodeJSON(w, http.StatusUnauthorized, rest.JSON{"error": msg})
193191
return
194192
}
195193
rest.RenderJSON(w, claims.User)
@@ -211,8 +209,7 @@ func (s *Service) Handlers() (authHandler, avatarHandler http.Handler) {
211209
provName := elems[len(elems)-2]
212210
p, err := s.Provider(provName)
213211
if err != nil {
214-
w.WriteHeader(http.StatusBadRequest)
215-
rest.RenderJSON(w, rest.JSON{"error": fmt.Sprintf("provider %s not supported", provName)})
212+
_ = rest.EncodeJSON(w, http.StatusBadRequest, rest.JSON{"error": fmt.Sprintf("provider %s not supported", provName)})
216213
return
217214
}
218215
p.Handler(w, r)
@@ -347,7 +344,7 @@ func (s *Service) AddAppleProvider(appleConfig provider.AppleConfig, privKeyLoad
347344
L: s.logger,
348345
}
349346

350-
// Error checking at create need for catch one when apple private key init
347+
// error checking at create need for catch one when apple private key init
351348
appleProvider, err := provider.NewApple(p, appleConfig, privKeyLoader)
352349
if err != nil {
353350
return fmt.Errorf("an AppleProvider creating failed: %w", err)

avatar/avatar.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func (p *Proxy) resize(reader io.Reader, limit int) io.Reader {
194194
newW, newH = limit, h*limit/w
195195
}
196196
m := image.NewRGBA(image.Rect(0, 0, newW, newH))
197-
// Slower than `draw.ApproxBiLinear.Scale()` but better quality.
197+
// slower than `draw.ApproxBiLinear.Scale()` but better quality.
198198
draw.BiLinear.Scale(m, m.Bounds(), src, src.Bounds(), draw.Src, nil)
199199

200200
var out bytes.Buffer

avatar/avatar_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,16 +252,16 @@ func TestAvatar_resize(t *testing.T) {
252252
}
253253

254254
p := Proxy{L: logger.Std}
255-
// Reader is nil.
255+
// reader is nil.
256256
resizedR := p.resize(nil, 100)
257257
assert.Nil(t, resizedR)
258258

259-
// Negative limit error.
259+
// negative limit error.
260260
resizedR = p.resize(strings.NewReader("some picture bin data"), -1)
261261
require.NotNil(t, resizedR)
262262
checkC(t, resizedR, []byte("some picture bin data"))
263263

264-
// Decode error.
264+
// decode error.
265265
resizedR = p.resize(strings.NewReader("invalid image content"), 100)
266266
assert.NotNil(t, resizedR)
267267
checkC(t, resizedR, []byte("invalid image content"))
@@ -278,12 +278,12 @@ func TestAvatar_resize(t *testing.T) {
278278
img, err := os.ReadFile(c.file)
279279
require.Nil(t, err, "can't open test file %s", c.file)
280280

281-
// No need for resize, avatar dimensions are smaller than resize limit.
281+
// no need for resize, avatar dimensions are smaller than resize limit.
282282
resizedR = p.resize(bytes.NewReader(img), 800)
283283
assert.NotNil(t, resizedR, "file %s", c.file)
284284
checkC(t, resizedR, img)
285285

286-
// Resizing to half of width. Check resizedR avatar format PNG.
286+
// resizing to half of width. Check resizedR avatar format PNG.
287287
resizedR = p.resize(bytes.NewReader(img), 400)
288288
assert.NotNil(t, resizedR, "file %s", c.file)
289289

middleware/auth.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ func (a *Authenticator) refreshExpiredToken(w http.ResponseWriter, claims token.
189189
}
190190

191191
claims.ExpiresAt = 0 // this will cause now+duration for refreshed token
192-
c, err := a.JWTService.Set(w, claims) // Set changes token
192+
c, err := a.JWTService.Set(w, claims) // set changes token
193193
if err != nil {
194194
return token.Claims{}, err
195195
}

provider/apple.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,22 +49,22 @@ const (
4949

5050
// appleVerificationResponse is based on https://developer.apple.com/documentation/signinwithapplerestapi/tokenresponse
5151
type appleVerificationResponse struct {
52-
// A token used to access allowed user data, but now not implemented public interface for it.
52+
// a token used to access allowed user data, but now not implemented public interface for it.
5353
AccessToken string `json:"access_token"`
5454

55-
// Access token type, always equal the "bearer".
55+
// access token type, always equal the "bearer".
5656
TokenType string `json:"token_type"`
5757

58-
// Access token expires time in seconds. Always equal 3600 seconds (1 hour)
58+
// access token expires time in seconds. Always equal 3600 seconds (1 hour)
5959
ExpiresIn int `json:"expires_in"`
6060

61-
// The refresh token used to regenerate new access tokens.
61+
// the refresh token used to regenerate new access tokens.
6262
RefreshToken string `json:"refresh_token"`
6363

64-
// Main JSON Web Token that contains the user’s identity information.
64+
// main JSON Web Token that contains the user’s identity information.
6565
IDToken string `json:"id_token"`
6666

67-
// Used to capture any error returned in response. Always check error for empty
67+
// used to capture any error returned in response. Always check error for empty
6868
Error string `json:"error"`
6969
}
7070

@@ -443,7 +443,7 @@ func (ah *AppleHandler) exchange(ctx context.Context, code, redirectURI string,
443443
return err
444444
}
445445

446-
// Trying to decode (unmarshal json) data of response
446+
// trying to decode (unmarshal json) data of response
447447
err = json.NewDecoder(res.Body).Decode(result)
448448
if err != nil {
449449
return fmt.Errorf("unmarshalling data from apple service response failed: %w", err)
@@ -455,8 +455,8 @@ func (ah *AppleHandler) exchange(ctx context.Context, code, redirectURI string,
455455
}
456456
}()
457457

458-
// If above operation done successfully checking a response code and error descriptions, if one exist.
459-
// Apple service will response either 200 (OK) or 400 (any error).
458+
// if above operation done successfully checking a response code and error descriptions, if one exist.
459+
// apple service will response either 200 (OK) or 400 (any error).
460460
if res.StatusCode != http.StatusOK || result.Error != "" {
461461
return fmt.Errorf("apple token service error: %s", result.Error)
462462
}
@@ -471,7 +471,7 @@ func (ah *AppleHandler) createClientSecret() (string, error) {
471471
if ah.conf.privateKey == nil {
472472
return "", fmt.Errorf("private key can't be empty")
473473
}
474-
// Create a claims
474+
// create a claims
475475
now := time.Now()
476476
exp := now.Add(time.Minute * 30).Unix() // default value
477477

@@ -502,7 +502,7 @@ func (ah *AppleHandler) parseUserData(user *token.User, jUser string) {
502502

503503
var userData UserData
504504

505-
// Catch error for log only. No need break flow if user name doesn't exist
505+
// catch error for log only. No need break flow if user name doesn't exist
506506
if err := json.Unmarshal([]byte(jUser), &userData); err != nil {
507507
ah.Logf("[DEBUG] failed to parse user data %s: %v", user, err)
508508
user.Name = "noname_" + user.ID[6:12] // paste noname if user name failed to parse

provider/custom_server_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func TestCustomProvider(t *testing.T) {
5151
L: logger.Std,
5252
WithLoginPage: true,
5353
LoginPageHandler: func(w http.ResponseWriter, r *http.Request) {
54-
// // Simulate POST from login page
54+
// // simulate POST from login page
5555
u, err := url.Parse("http://127.0.0.1:9096/authorize?" + r.URL.RawQuery)
5656
if err != nil {
5757
assert.Fail(t, "failed to parse url")

provider/sender/email.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,14 @@ type Email struct {
2020
type EmailParams struct {
2121
Host string // SMTP host
2222
Port int // SMTP port
23-
From string // From email field
24-
Subject string // Email subject
25-
ContentType string // Content type
23+
From string // from email field
24+
Subject string // email subject
25+
ContentType string // content type
2626

2727
TLS bool // TLS auth
28-
StartTLS bool // StartTLS auth
29-
InsecureSkipVerify bool // Skip certificate verification
30-
Charset string // Character set
28+
StartTLS bool // startTLS auth
29+
InsecureSkipVerify bool // skip certificate verification
30+
Charset string // character set
3131
LoginAuth bool // LOGIN auth method instead of default PLAIN, needed for Office 365 and outlook.com
3232
SMTPUserName string // username
3333
SMTPPassword string // password

provider/telegram.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ var expiredCleanupInterval = time.Minute * 5 // interval to check and clean up e
6363
// Run starts processing login requests sent in Telegram
6464
// Blocks caller
6565
func (th *TelegramHandler) Run(ctx context.Context) error {
66-
// Initialization
66+
// initialization
6767
atomic.AddInt32(&th.run, 1)
6868
info, err := th.Telegram.BotInfo(ctx)
6969
if err != nil {
@@ -168,7 +168,7 @@ func (th *TelegramHandler) processUpdates(ctx context.Context, updates *telegram
168168

169169
th.requests.RLock()
170170
authRequest, ok := th.requests.data[token]
171-
if !ok { // No such token
171+
if !ok { // no such token
172172
th.requests.RUnlock()
173173
err := th.Telegram.Send(ctx, update.Message.Chat.ID, th.ErrorMsg)
174174
if err != nil {
@@ -256,7 +256,7 @@ func (th *TelegramHandler) LoginHandler(w http.ResponseWriter, r *http.Request)
256256
queryToken := r.URL.Query().Get("token")
257257
if queryToken == "" {
258258
// GET /login (No token supplied)
259-
// Generate and send token
259+
// generate and send token
260260
token, err := randToken()
261261
if err != nil {
262262
rest.SendErrorJSON(w, r, th.L, http.StatusInternalServerError, err, "failed to generate code")
@@ -322,7 +322,7 @@ func (th *TelegramHandler) LoginHandler(w http.ResponseWriter, r *http.Request)
322322

323323
rest.RenderJSON(w, claims.User)
324324

325-
// Delete request
325+
// delete request
326326
th.requests.Lock()
327327
defer th.requests.Unlock()
328328
delete(th.requests.data, queryToken)
@@ -342,8 +342,8 @@ type tgAPI struct {
342342
token string
343343
client *http.Client
344344

345-
// Identifier of the first update to be requested.
346-
// Should be equal to LastSeenUpdateID + 1
345+
// identifier of the first update to be requested.
346+
// should be equal to LastSeenUpdateID + 1
347347
// See https://core.telegram.org/bots/api#getupdates
348348
updateOffset int
349349
}
@@ -387,7 +387,7 @@ func (tg *tgAPI) Send(ctx context.Context, id int, msg string) error {
387387

388388
// Avatar returns URL to user avatar
389389
func (tg *tgAPI) Avatar(ctx context.Context, id int) (string, error) {
390-
// Get profile pictures
390+
// get profile pictures
391391
url := fmt.Sprintf(`getUserProfilePhotos?user_id=%d`, id)
392392

393393
var profilePhotos = struct {
@@ -402,12 +402,12 @@ func (tg *tgAPI) Avatar(ctx context.Context, id int) (string, error) {
402402
return "", err
403403
}
404404

405-
// User does not have profile picture set or it is hidden in privacy settings
405+
// user does not have profile picture set or it is hidden in privacy settings
406406
if len(profilePhotos.Result.Photos) == 0 || len(profilePhotos.Result.Photos[0]) == 0 {
407407
return "", nil
408408
}
409409

410-
// Get max possible picture size
410+
// get max possible picture size
411411
last := len(profilePhotos.Result.Photos[0]) - 1
412412
fileID := profilePhotos.Result.Photos[0][last].ID
413413
url = fmt.Sprintf(`getFile?file_id=%s`, fileID)

provider/telegram_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestTelegramUnconfirmedRequest(t *testing.T) {
5050
tg, cleanup := setupHandler(t, m)
5151
defer cleanup()
5252

53-
// Get token
53+
// get token
5454
r := httptest.NewRequest("GET", "/", nil)
5555
w := httptest.NewRecorder()
5656
tg.LoginHandler(w, r)
@@ -68,7 +68,7 @@ func TestTelegramUnconfirmedRequest(t *testing.T) {
6868
assert.Equal(t, "my_auth_bot", resp.Bot)
6969
token := resp.Token
7070

71-
// Make sure we get error without first confirming auth request
71+
// make sure we get error without first confirming auth request
7272
r = httptest.NewRequest("GET", fmt.Sprintf("/?token=%s", token), nil)
7373
w = httptest.NewRecorder()
7474
tg.LoginHandler(w, r)
@@ -78,7 +78,7 @@ func TestTelegramUnconfirmedRequest(t *testing.T) {
7878

7979
time.Sleep(tgAuthRequestLifetime)
8080

81-
// Confirm auth request expired
81+
// confirm auth request expired
8282
r = httptest.NewRequest("GET", fmt.Sprintf("/?token=%s", token), nil)
8383
w = httptest.NewRecorder()
8484
tg.LoginHandler(w, r)
@@ -146,7 +146,7 @@ func TestTelegramConfirmedRequest(t *testing.T) {
146146
tg, cleanup := setupHandler(t, m)
147147
defer cleanup()
148148

149-
// Get token
149+
// get token
150150
r := httptest.NewRequest("GET", "/", nil)
151151
w := httptest.NewRecorder()
152152
tg.LoginHandler(w, r)
@@ -166,7 +166,7 @@ func TestTelegramConfirmedRequest(t *testing.T) {
166166
servedToken = resp.Token
167167
wgToken.Done()
168168

169-
// Check the token confirmation
169+
// check the token confirmation
170170
assert.Eventually(t, func() bool {
171171
r = httptest.NewRequest("GET", fmt.Sprintf("/?token=%s", resp.Token), nil)
172172
w = httptest.NewRecorder()
@@ -186,7 +186,7 @@ func TestTelegramConfirmedRequest(t *testing.T) {
186186
assert.Contains(t, info.ID, "telegram_")
187187
assert.Equal(t, "http://example.com/ava12345.png", info.Picture)
188188

189-
// Test request has been invalidated
189+
// test request has been invalidated
190190
r = httptest.NewRequest("GET", fmt.Sprintf("/?token=%s", resp.Token), nil)
191191
w = httptest.NewRecorder()
192192
tg.LoginHandler(w, r)
@@ -206,7 +206,7 @@ func TestTelegramLogout(t *testing.T) {
206206
tg, cleanup := setupHandler(t, m)
207207
defer cleanup()
208208

209-
// Same TestVerifyHandler_Logout
209+
// same TestVerifyHandler_Logout
210210
handler := http.HandlerFunc(tg.LogoutHandler)
211211
rr := httptest.NewRecorder()
212212
req, err := http.NewRequest("GET", "/logout", http.NoBody)
@@ -292,7 +292,7 @@ func TestTelegram_ProcessUpdateFlow(t *testing.T) {
292292
assert.EqualError(t, tg.ProcessUpdate(context.Background(), ""), "failed to decode provided telegram update: unexpected end of JSON input")
293293
assert.Len(t, tg.requests.data, 1, "expired token should be cleaned up despite the error")
294294

295-
// Verify that get token will return bot name
295+
// verify that get token will return bot name
296296
r := httptest.NewRequest("GET", "/", nil)
297297
w := httptest.NewRecorder()
298298
tg.LoginHandler(w, r)

0 commit comments

Comments
 (0)