My Golang Redirect problem

Sachith Muhandiram
4 min readApr 9, 2020

--

I will list mistakes I have made during last few days and solutions .Following contents are my own troubles I encountered and solutions I got. Those may not be the best or optimal solutions. Also this will be the introductory part for my project.

Last few months I had been developing a web based application using golang. Its based on Microservice architecture. Which is still I am learning :-). I have divided my whole architecture into

  • API Gateway. ( Building my own one)
  • User service. ( Handle all User related tasks)
  • Notification service. ( Sends Notifications)

Last few days I was trying to implement User login feature. In my scenario, user sends email and password and system embedded token for each login form.

API Gateway                                        User Service
| |
/login |
| |
Validate password -----------------> /login
|
Check credentials
|
/home <------------------ set JWT

So everything was working perfectly except for redirecting authorized users to /home route and I couldnt see any cookie. I have put a logging in my gateway and checked for cookie. Its not there. Cookie created successfully in User service and then added to Header. After wasting some time, decided to go for StackOverflow. [Response redirect to other domain with a cookie in golang].

Learned Lesson : 1

You can not set cookies from one domain and send it to another domain. So if I set a cookie from my User service and send that same to Gateway server, it can not identify it. As its described here.

Note that according to the same origin policy, cookies can only be accessed by pages originating from the same site. For example, the domain, application layer protocol, and port number (for most browsers) must match.

My problem was, I set the cookie in User Service domain and then tried to send it to Gateway domain. Which are completely different domains. So cookie set from User service doesn't show in Gateway.

After that was fixed, I decided , User service just to do authentication and sends a form data to Gateway route and if that value is set to 1, then gateway will generate a JWT and sends to /home route.

Gateway server :

_, err := http.PostForm("http://userservice/login", url.Values{"userid": {userid}, "uid": {requestID.String()},                         "password": {password}})

Here gateway server posts a form to userservice/login route. In user service login route I compared password with database and then post a form to gateway :

_, err := http.PostForm("http://gateway/home", url.Values{"userid": {userid}, "uid": {requestID.String()},                     "authorize": {"1"}})

In gateway check whether form value is authorize, if so it generated JWT and sent to a test route. Boom, yet no cookie is set there. I have checked again again and again. But no cookie is set.

Thought about this for hours but I couldn't get a solution for this. Night just before I fall a sleep, I understood something.

HTTP is all about request and response. Response is sent for a particular request. Didnt I miss it in my project? That was priority task I did next morning. Yes, its right. Everywhere I have used PostFormand if you have noticed. I have ignored all the responses I got from PostForm()

_, err := http.PostForm(" ")

Learned Lesson : 2

I should use same request,response cycle through out this whole process. Where ever request goes, it should go with original request received. Then only http response is set properly.

Then I got yet another problem. How can I add additional values to original request. In gateway server, I create an UUID for each request and its sent with that request everywhere it goes. I have tried some online solutions to add values to request before sending it to another place. But all failed and finally again went to StackOverFlow.

As you may have seen, when discussion goes on mkopriva suggested me the best possible solution. Which worked for me. But still I feel its ugly :)

I had to add those additional details to route url like this :

uuid := generateUUID() // generate uuid
// uuid would be like : 911cf95b-6b3f-43fd-b695-c80c79145c51
http.Redirect(w, r, "http://userservice/login?userid="+uuid, http.StatusSeeOther)

in other service had to use req.ParseForm() and then retrieve values using

userID := req.FormValue("userid") or you can use userID := req.URL.Query().Get("userid") .

After this I could solve my problem. User can login into system with valid user name and password. And routed to /home with a valid JWT.

So in every service I have used ,

http.Redirect(w, r, "http://service/route, http.StatusSeeOther)

In this way, same request goes to all the services and response is received to initial request received to gateway.

Learned Lesson : 3

Visit codes often, it will show you messes you have made.

Initial login code, here you can see I did password comparison and everything in same function. Function should do only one thing and it right, I knew it when I initially developing the code. But Ego didnt allow me to do it. And after I understood my mistakes, I changed it to be like this. Here each specific task is done by a separate function. Password and login token checked by separate functions and return a boolean value.

This may look like gibberish, I wrote things as they come into my mind. You may have noticed much more bigger mistake I have made. If so, please correct me.

--

--

Sachith Muhandiram
Sachith Muhandiram

Written by Sachith Muhandiram

DevOps who is willing to learn and try new things.

No responses yet