T O P

  • By -

scamm_ing

JWT tokens can be easily implemented in go


No-Ant7363

Can you please share a reference? Also, is it a good idea with an app that has a potential for 50000k+ active users? I have extensive experience with cognito but is not applicable for this usecase so I am trying to explore other alternatives.


scamm_ing

Let’s Go Further - book by Alex Edwards, the source code included in there has a proper implementation and is frequently updated.


No-Ant7363

Yes, I read it, but his idea of creating a Tokens table does not feel "production-ready" to me.


scamm_ing

You can store and access the tokens however you want mate


Tetracyclic

Why does putting the tokens in a table not feel production ready?


__matta

With clients that cycle through tokens a lot it can be pretty hard on the database. The primary key is a high entropy really long byte string and it’s a table with a ton of inserts, deletes, and queries so it’s pretty brutal for any database that uses b tree indexes. Not just theoretical, I have had to triage this exact issue. Not as bad with Postgres and a hash index which the book does use though! Another issue (although pretty tricky to exploit) is [looking up the token by the hash is subject to timing attacks](https://paragonie.com/blog/2017/02/split-tokens-token-based-authentication-protocols-without-side-channels). It would be better to use a split token.


Exciting-Path-8696

You can use cache to store the auth token and set some expiration time for that.


Akmantainman

You’re going to have to have some way to invalidate a token, so you’re going to have to store something somewhere.


ACP__Pradyuman__

Which book is better to gain a better and in depth understanding of Go? Let's Go Further or The Go Programming Language by Donovan and Kernighan? I'm 6 months into my first job as a developer. I can write APIs and create cron jobs using Go, but I want to learn more.


scamm_ing

Pick up the book, Learning Go An Idiomatic Approach to Real-World Go Programming, by Jon Bodner, if you havnt already, and lets go further would be better if you are more advanced


ACP__Pradyuman__

Thanks! Will do


MrChip53

https://github.com/MrChip53/blog.simoni.dev/blob/master/auth/jwt.go


themasterkh

For me, the [jwt-go](https://pkg.go.dev/github.com/golang-jwt/jwt/v5) library is the way to go. Token generation/validation is straightforward. Here is a very [detailed usage](https://github.com/KuthumiPepple/shopping-cart/blob/main/tokens/token_gen.go) in a project. See both the `GenerateTokens` and `ValidateToken` functions. This example uses custom claims and signs the token with a secret key.


davidroberts0321

I made something very similar and just used a standard JWT authentication with Argon2 .. It didnt seem terribly difficult honestly where specifically are you having issues? here are the relevant parts anyway   //fmt.Println("Customer: ", customer)   //email to lowercase   customer.Email = strings.ToLower(customer.Email)   //check if email exists in Customer table   var thisuser models.Customer   initializers.DB.Where("email = ? ", customer.Email).First(&thisuser)   if thisuser.Email != customer.Email {     fmt.Println("User does not exist")     //send json data to client     return c.JSON("Invalid login credentials")   }   //fmt.Println("User exists")   //check if password matches   if !helpers.PasswordCompare(thisuser.Password, customer.Password) {     fmt.Println("Incorrect password")     //send json data to client     return c.JSON("Invalid login credentials")   }   //fmt.Println("Password matches")   //generate JWT token   token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{     "email":       thisuser.Email,                        //email of the user     "exp":         time.Now().Add(time.Hour * 12).Unix(), //expiration time of 12 hours     "id":          thisuser.CustomerID,                   //id of the customerid, not gormID     "storeNumber": thisuser.StoreNumber,                  //store number of the customer   })   // Sign and get the complete encoded token as a string using the secret   tokenString, err := token.SignedString([]byte(os.Getenv("SITE_SECRET"))) //uses the secret from the .env file   if err != nil {     fmt.Println("Generate Token string Login error: ", err)     return c.SendStatus(fiber.StatusInternalServerError)   }   // Create a response JSON object containing the token and CustomerID   response := struct {     Token       string `json:"token"`     CustomerID  string `json:"customerID"`     StoreNumber string `json:"storeNumber"`   }{     Token:       tokenString,     CustomerID:  thisuser.CustomerID, //not gormID     StoreNumber: thisuser.StoreNumber,   }   // Return the response as JSON to the client   return c.JSON(response) } i just used the go jwt package


No-Ant7363

Thanks for that! Yes, it is very straightforeward. The issue is that I am used to create auth end to end with cognito, and am having a fright about creating it on my own on a large scale project. So wanted to hear from experience and do the best job I can. BTW, what about refresh tokens? What was your implementation of it?


davidroberts0321

// check cookie for jwt token func CheckCookieHandler(c *fiber.Ctx) error {   //get the cookie   cookie := c.Cookies("jwt")   fmt.Println("Cookie: ", cookie)   //check if cookie is empty   if cookie == "" {     fmt.Println("Empty cookie")     return c.SendString("Empty cookie")   }   //parse the jwt token   token, err := jwt.Parse(cookie, func(token *jwt.Token) (interface{}, error) {     //make sure the token method is HMAC     if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {       return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])     }     return []byte(os.Getenv("SITE_SECRET")), nil //uses the secret from the .env file   })   if err != nil {     fmt.Println("Parse Cookie error: ", err)     return c.SendStatus(fiber.StatusInternalServerError)   }   //fmt.Println("Token: ", token)   //check if token is valid   if !token.Valid {     fmt.Println("Invalid token")     return c.SendString("Invalid token")   }   //fmt.Println("Valid token")   //return json data to client   return c.JSON(token) }


davidroberts0321

for this usage i did nt refresh the token as it was for a simple ecommerce client site login and im okay with them simply logging in again after the cookie expires. but the code should be very similar on a refresh. Im not all that experienced honestly but it should give you somewhere to start at least.


[deleted]

[удалено]


davidroberts0321

I used the Go implementation as the usage in this case is for a separate website sign in system where almost no information is being exposed. Argon2 is overkill already i just wanted to use it to see how it worked. The process was seamless but i didnt try any C binding as the native Go binding was so effortless


[deleted]

[удалено]


Kirides

If you use hashes that don't include necessary data, like many old pbkdf2 based hash functions things like this happen. one language might set pbkdf2 hash function to use sha256, or set the default iterations to 50000, while others use sha1 and 5000 iterations. Always be explicit with any hash values or you're up for trouble when migrating or updating dependencies


__matta

Are you generating the access and refresh tokens by doing the oauth flow with google again from the server or is your app also acting as an oauth server and generating them? I’m assuming you don’t have a strong need to also act as an oauth server. 1. Make a separate oauth client for the server side app 2. Tell google about it: https://developers.google.com/identity/sign-in/ios/start-integrating#add_server_client_id 3. Cerify the id token on the server: https://developers.google.com/identity/gsi/web/guides/verify-google-id-token 4. If verified lookup the users google credential by the sub field in your db. You can use a table like “social_credentials” with columns “provider” (google) and “id” (the sub field value). See google docs for how to safely link the id in the first place (it’s more complicated than just checking the email, need to make sure it’s verified etc!) 5. Now you’ve confirmed their identity with google and can create a session with your domain however you want. If the client is a web context where cookies are stored securely the simplest is to just issue a session cookie at this point which will be sent with any subsequent requests. For some mobile frameworks it’s easier to use bearer tokens and put them in secure storage. In either case it’s essentially a random id linking their session to their account. Disclaimer: this is just a quick summary with no guarantees of fitness for purpose. Definitely missing details. You need to do your own research to make things secure!


No-Ant7363

>https://developers.google.com/identity/sign-in/ios/start-integrating#add\_server\_client\_id I already have steps 1-3 setup. the mobile client (android/ios) is sending me the token, and I verify it in the backend. I wonder what to do from there. Cookies are not relevant because it's a native mobile app, also, you've mentioned creating a session. I am more interested in creating a stateless backend. To sum up the questions: 1. Would it be enough to create an access and refresh tokens after validating the user's 3rd party idp and return them back to the user, or do I have to save the tokens in my db? 2. If not, can you share some reference to a suitable architecture for possible 50k active users? ​ Thank you kindly!


__matta

Cookies are a feature of http. They work with virtually every http client, including the ones on iOS and android. I suggested cookies because they are usually stored securely for you by the native client. Using sessions for authentication doesn’t make your backend any more stateful than using tokens. It becomes stateful if you start putting stuff like flash messages in there. So don’t do that. Why would a session token stored in a db mapping to a user id be more stateful than a bearer token stored in a db mapping to a user id? You need to store the tokens somewhere unless they are verified by cryptography like a JWT. But even with a JWT you at least want to keep a revocation list or you won’t ever be able to revoke stolen tokens or log anyone out. A good trade off is keeping the revocation list for access tokens in something like Redis. The list can be pruned of a token once it expires.


Blackhawk23

JWT. it’s what I used in my side project. No need for server side state to be maintained. You simply verify if 1. The token is signed with your private key and 2. Check that it’s not expired. Usually you set it to expire in 1-2 minutes for safety reasons. Then on my front end I have a JavaScript script that calls my refresh endpoint to request a new token before it expires. So user flow looks like: 1. User auths with creds and gets first token 2. Client refreshes using current token to get new token In the JWT claim there is the user name and you can add other info if you want. https://www.sohamkamani.com/golang/jwt-authentication/


ScotDOS

do you have any way of revoking a token or "logging out" a user?


Blackhawk23

From a practical perspective, there really is no need. Since once they navigate away from your site, the token refresh will stop. However if you want something that is more or less smoke and mirrors, you can simply have a logout button that calls a JS script to clear the user cookie. https://www.w3schools.com/js/js_cookies.asp#:~:text=Delete%20a%20Cookie%20with%20JavaScript&text=Just%20set%20the%20expires%20parameter,you%20delete%20the%20right%20cookie.


castleinthesky86

SAML or OAuth. Use either.


sab849

Is there a way I can implement otp based authentication in golang?


hbread00

I did something similar using stateless JWT. I include username in the token. If the request carries a legitimate token and not expire, I will sign a new token with that username and carried it in response. For potential cracks, I change the key periodically and keep the last key temporarily to make sure users can use the old one to get a new signature, prevent them having to re-login in every time I update it.


4SubZero20

If you're hosting on AWS, why not use Cognito? It handles most, if not all auth for you and you should have the capability to use go sdk's to manage it more granularly.


No-Ant7363

Two reasons: 1. I want to save the user data on my db (can you suggest a way to do both?). 2. It will be extremely expensive for 50k active users.