Golang HTTP redirect happening multiple times - http

I have an HAProxy in front of my application. I have TLS passthrough on the proxy and I'm doing termination at the app layer.
If the authorization is missing or invalid, I simply redirect with a 302 to my auth subdomain, which presents the user with a login page.
func unauthorizedPage(logger *zap.Logger, w http.ResponseWriter, req *http.Request) {
logger.Error("The user is not authorized to make this request - referring",
zap.String("refer", req.Host+req.URL.Path))
b64Url := b64.StdEncoding.EncodeToString([]byte(req.Host + req.URL.Path))
http.Redirect(w,
req,
fmt.Sprintf("https://auth.my-site.com:443?refer=%s", b64Url),
http.StatusFound)
}
My network is showing 23 requests and then the browser is throwing me a The page isn’t redirecting properly error.
The page it loads is just a simple JS login page (no redirects). How is it possible for this to happen?

Related

Running into CORS error with any HTTP call regardless of it being handled on the server

I have a simple server written in Go. I then have a simple Nextjs app that acts as the frontend of my project. The issue is that whenever I try to fetch the backend from the frontend, the normal CORS error pops out.
Now, I know that the frontend does a preflight call with HTTP OPTIONS and it checks headers and so on, but after looking at dozens of StackOverflow posts with what seemed to be a solution, nothing worked.
This is the source code
package main
import (
"encoding/json"
"log"
"net/http"
"github.com/gorilla/mux"
)
type ClassReturn struct {
Errcode int `json:"errcode"`
Message string `json:"message"`
Data interface{} `json:"data"`
}
func main() {
r := mux.NewRouter()
router(r)
log.Fatal(http.ListenAndServe(":8000", r))
}
func router(r *mux.Router) {
r.HandleFunc("/get", corsHandler(handleOutput)).Methods("GET", "OPTIONS")
r.HandleFunc("/post", corsHandler(handleOutput)).Methods("POST", "OPTIONS")
}
func corsHandler(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
if r.Method == "OPTIONS" {
log.Print("preflight detected: ", r.Header)
w.Header().Add("Connection", "keep-alive")
w.Header().Add("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS, GET, DELETE, PUT")
w.Header().Add("Access-Control-Allow-Headers", "content-type")
w.Header().Add("Access-Control-Max-Age", "86400")
return
} else {
handleOutput(w, r)
}
}
}
func handleOutput(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(ClassReturn{
Errcode: 0,
Message: "stuff endpoint",
Data: r.Method,
})
}
This is the console message I get when fetching the go endpoint (for the get endpoint, but I do get the same for the post endpoint)
localhost/:1 Access to XMLHttpRequest at 'http://localhost:8000/get' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
In the network tab I get the failed HTTP Request with GET + Preflight blocked by CORS
Neither a POST nor a GET call works, nor does it if I change the Access-Control-Allow-Origin from http://localhost:3000 to *
I tried different things, like those
rs/cors
github.com/gorilla/handlers
How to handle preflight CORS requests on a Go server
Making golang Gorilla CORS handler work
Enable CORS in Golang
https://www.stackhawk.com/blog/golang-cors-guide-what-it-is-and-how-to-enable-it/
CORS on golang server & javascript fetch frontend
Why does my JavaScript code receive a "No 'Access-Control-Allow-Origin' header is present on the requested resource" error, while Postman does not?
But none of those above have worked.
I have also asked on the Discord server of Go but they prompt me to use these very solutions I have above, which don't work
If anyone knows a possible solution to my issue I would be delighted
SOLUTION
func corsHandler(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
log.Print("preflight detected: ", r.Header)
w.Header().Add("Connection", "keep-alive")
w.Header().Add("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS, GET, DELETE, PUT")
w.Header().Add("Access-Control-Allow-Headers", "content-type")
w.Header().Add("Access-Control-Max-Age", "86400")
// continue with my method
handleOutput(w, r)
}
}
EXPLANATION
After some checks (and help from #mkopriva), I found out that my frontend was not entering the if in the code, so my HTTP call was not OPTIONS, but directly GET. However, due to the CORS policy, headers were never set for Access-Control-Allow-Origin, so my call was failing.
Removing the control (if r.Method == "OPTIONS") allowed the backend to always set headers so even on simple calls, my fetch would not fail
SIDE NOTE
There is a necessity to make a little side note here. Usually, it is strange to HTTP GET to make a Preflight request (for CORS). It is stated and well documented here that Some requests don't trigger a CORS preflight. XMLHttpRequest and fetch can submit simple requests to any origin. Now, I was using axios in my Next.js project, so it was using a XMLHttpRequest. the server still must opt-in using Access-Control-Allow-Origin to share the response
This was written in the article linked above and should be a key reference when building a frontend with a backend, both in a localhost/development environment.
ANOTHER SIDE NOTE
I lost several hours of my time figuring out why adding custom headers was breaking CORS. The answer was in front of my eyes, as the CORS Policy "Access-Control-Allow-Headers" did not have the correct headers I sent on request. Considering I use "Authorization: Bearer ***", if I do not do like this, CORS breaks;
w.Header().Add("Access-Control-Allow-Headers", "Authorization, content-type")
This article helped me a lot
I hope this clears any doubt, feel free to improve this answer

NGinx sends 307 instead of 401

I've used several revers proxies over time, but NGinx blew me away with its behavior. I recently had to use NGinx after years of using HAProxy (as K8s Ingress) and I'm stuck with no solution in sight.
Right from the beginning, the behaviour was different when the session was lost. An HTTP 401 would tell the client (single page JavaScript application) to inform the user that the session was lost and he/she has to login again. Instead of sending 401 to the browser, it sends a 307 to the login page, with the wrong verb (POST) since the request that fails with 401 was a POST request.
The best way to troubleshoot would be in isolation so I installed the version from the repository (Linux Mint 20) and registered a simple reverse proxy entry in /etc/nginx/conf.d
server {
listen 80;
location / {
proxy_pass http://localhost:8080;
}
}
Unfortunately (or fortunately) the issue manifested itself right away: as soon as I removed the cookies and the server sends 401 "Please log in", the browser shows a 307 on the request that failed with 401.
Expected behavior:
Browser sends "POST /ping"
Browser receives "401 on /ping"
Current behavior:
Browser sends "POST /ping"
Browser receives "307 /auth" and then executes /auth with POST (not sure how it knows /auth is the login page)
Any idea how to disable this behavior?

Vue-Request not sending Authorization Header

I'm using VueJS with vue-request for http requests. I'm trying to subscribe an user to a Mailchimp list but Mailchimp uses BasicAuth, so I'm doing as such:
scope.$http.post('https://us15.api.mailchimp.com/3.0/lists/listid/members',
{...mydata...}, {headers: {Authorization: 'Basic myencodedAPIkey'}})
But I get an error from the API: 401 Unauthorized - Your request did not include an API key.
So I check the Network log on Chrome and the Authorization is on my headers like this: **Access-Control-Request-Headers: authorization** but it should be like **Authorization: myencodedAPIkey**
On the Console the error appears as:
XMLHttpRequest cannot load
https://us15.api.mailchimp.com/3.0/lists/listid/members. Response
to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://127.0.0.1:8000' is therefore not allowed
access. The response had HTTP status code 401.
When I use Postman it works just fine as the header is correctly sent.
This problem seems to have been solved here with setting the header on every request
https://laracasts.com/discuss/channels/vue/how-to-solve-the-allow-control-allow-cross-in-the-vuejs-header-request-setting?page=2
and here through setting it once
Vue-Request not sending Authorization Header
You are getting CORS error, when you are trying to request from one host to another, and the 'another' part does not allow it to happen. To prevent this error you can use webpack proxy configuration, so this way you do not have cross origin request, but I don't know how you will deal with this in production environment if your api does not allow cross origin requests.
In a project I'm working on, our devServer configuration is as follow
proxy: {
'/api': {
target: 'http://localhost:8080/'
}
},
with this, any request happening on /api/any/url will be redirect to localhost:8080/api/any/url

Go http.Request Header on Redirect and Proxy

https://groups.google.com/forum/#!topic/golang-nuts/OwGvopYXpwE
As seen in this thread, when http.Client sends requests to redirects, the header gets reset.
There is a workaround like:
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
if len(via) >= 10 {
return fmt.Errorf("too many redirects")
}
if len(via) == 0 {
return nil
}
for attr, val := range via[0].Header {
if _, ok := req.Header[attr]; !ok {
req.Header[attr] = val
}
}
return nil
}
But my question is how do I do this if I want to http Request through my proxy server.
When http request goes through proxy server, does header get all reset? Do I have to set up another http.Client in proxy?
I set up my proxy server using https://github.com/elazarl/goproxy
Thanks,
I looked briefly at the code, at it looks like goproxy doesn't handle 3XX responses in any special way – they're simply routed back to your client, so it can react accordingly. In your case, you'll issue another request with all the headers set.
If it wasn't your concern and you were just wondering if simply existence of proxy requires any hack on the client side, then no, http.Client won't remove any headers when proxy is in use, it won't even know there's a proxy involved.
You can easily test these assumption by setting up you own a simple server (in Go for example) which is returning a redirect response (to itself or some nc listener) and printing all the headers. When routing the client to the server through your proxy, you can make sure everything looks good on the server side.

How to connect to and keep the session alive using python requests

I am trying to login to a site, and then view user details.
The API documentation from the site is:
LogOn : All Calls with JSON and return JSON type
post - https://www.bit2c.co.il/Account/LogOn {UserName:'',Password:'',SecondFactor:[optional]}
return true if OK , error list of strings if not
Balance - GET https://www.bit2c.co.il/Account/Balance
return UserBalance as JSON
I've tried connecting to the site using
import requests
session=requests.session()
session.auth = ("username", "pass")
session.post("https//www.bit2c.co.il/Account/Balance")
but i am getting response 200 and the response content is "you must login".
What am I doing wrong ?
What kind of session? What is on server-side? Can you post the raw request/response pair(s)?
If PHP or ASP runs on server-side, you must capture the Set-Cookie header of the response to the login request, find the session cookie and in consequent requests you must set the Cookie header with the session cookie name and value captured previously.

Resources