Building A JSON API In Golang - JWT Authentication part 4

preview_player
Показать описание

In this Golang programming tutorial series, I will teach you how to build a complete industry-ready JSON API project in Golang with JWT authentication, Postgresql, and Docker. We are going to build a bank API and build everything from scratch with only the mandatory packages we actually need. We are also going to write tests for each of our HTTP handlers. If you want to learn Golang or Rust consider subscribing to my channel so you can become a high-value engineer.

#golang
Рекомендации по теме
Комментарии
Автор

This series has been really fun to watch. However from about 20:00 onward, there is a serious misunderstanding about JWTs and signing that caused a lot of misinformation in this video.

JWTs are digitially signed by the server using its secret. As long as this secret is secure:

1) If a client sends an altered (changed by the user) JWT back to the server then the server will detect this because the signature test will fail.

2) If the signature test succeeds, then the server knows that the JWT has not been altered since the server created the JWT, and hence the server knows that it can TRUST the payload/claim data within the JWT. This is a huge win and is one of the main design purposes of the JWT because it enable the sever to avoid an additional query to lookup the user - as long as it has placed the user ID into the JWT claims/payload it can TRUST that value is good and usable when the JWT has passed validation (its signature is valid). This often means the JWT enables the server to avoid an additional database lookup on every single API call. Contrast this with Session authentication where a session token is used rather than a JWT token, and session tokens DO need to be looked up in a DB to see if they are still valid. This was one of the primary motivations for designing the whole JWT auth method.

Note that any data that the server places into the JWT (such as claims) which it hasn't encrypted are accessible to the user, so don't place anything in the JWT such as passwords or sensitive data unless you encrypt it on the server side in such a way that only the server can decrypt it.

The best way to deal with fishing out the payload from the JWT and passing it along with the request is to add code to your withJWTAuth() method to add the claims to the request context. This way you don't need to change the call prototypes for anything, but this also enables any of the code that processes the requests to access this claims data without needing to revalidate the JWT.

Example:

func withJWTAuth(handlerFunc http.HandlerFunc) http.HandlerFunc {
//...

if claims, ok := token.Claims.(jwt.MapClaims); ok {
// Store claims in request context
ctx := context.WithValue(r.Context(), ClaimsKey, claims)
// Create new request with updated context
r = r.WithContext(ctx)
}
//...

}


// Then inside any of your handleXyz(w http.ResponseWriter, r *http.Request) functions you can get access to the claims you added to the context like so:

claims, err := getClaimsFromContext(r)
if err != nil {
return err
}
fmt.Printf("Request made by user ID: %v\n", claims["id"]) // Note I used "id" lowercase for my json/sql

otterhopper
Автор

its amazing to see you solve the problem in real time, thank you anthony

hakimchulan
Автор

Completed Part 4 - thanks. Looks like I'll need to do an independent deep-dive into JWT but the essentials are there.

knight
Автор

This tutorial is more like an exercise in pair programming. Thank you for this. I love it!

wiqarc
Автор

I've been a mostly decent JS dev for some time now, but I felt drwan to learn Go after seeing a few tutorials here and there and the stuff you can do with it.

(Also, I'm tired of the never ending changes in the JS community and to be honest the only reason I went that way was because I just love making UI's with CSS).

So I've been following the series while reading in the docs (be aware I'm not really an expert in anything that's not front-end related), but I'm using UUID from Google to create an ID for the user instead of using the auto-generated number.
(not really sure if this is a bad practice)

The thing is, I couldn't find a way to convert the string to UUID (yes, my claims["UserID"] is a typeOf string), and I resorted to this:

user.ID.String() !=

(yes, instead of account, I named it user because I'm handling more than one table, also I like to try doing things my way to see if I understand, and boy am I learning stuff in this series).

My question is, is this approach okay, or will this be a problem later?

PS: keep up the good work, you are awesome!

luismelo
Автор

33:06 the moment when you entered the flow mode and got carried away a bit with security

wMwPlay
Автор

Thanks so much for all your time, I am almost done. :) I appreciate it

freivincampbell
Автор

Nice one!! Usually what I do I fetch the user when he logged in and generate the JWT, then I add the JWT to cookies or session storage. Then when I am going to fetch I first get the user ID from the claims in the JWT in the cookies.

victorguidi
Автор

Hi, Since you are already using "mux", you could also use the middleware mechanism directly from it (.Use() like weavebox ;-) ). What do you think about injecting the UserID into the context of the request in the middleware function and authorising it in the handle function? So you don't need the store in the middleware.
type ContextUserIDKey string
var myUserIDKey ContextUserIDKey = "userID"
ctx := context.WithValue(r.Context(), myUserIDKey, userID)
next.ServeHTTP(w, r.WithContext(ctx))

AStranger
Автор

Hi, Thanks so much for the awesome content!! I have learnt a lot from your videos. do you plan creating Golang + GRPC or Golang + Kafka projects?

melvinkimathi
Автор

Thanks! Will you be continuing with this series I could not find the next video on your channel?

EUU
Автор

Hi Anthony, I really like your videos, thank you!
What do you think about adding the account ID to the claims in the JWT to avoid a database query and just compare the account ID with the one in the path parameter?

AndresOscarRaulAtencio
Автор

@8:28, Windows users use "set" instead of "export".

TattedFaceJoey
Автор

Are you going to continue this series...? Theres plenty of mentions of the next episode but it has been a few months 😕

nodidog
Автор

Im a big fan of your content, this is the first series im coding along and its been great. I just didnt understand why did you need to get the user from the database in the JWT authenticator. Why dont simply compare the id in the claims with the id in the route?. Thanks a lot

andresdominguez
Автор

All accounts have the same account number? Did I see that correctly?

CallousCoder
Автор

15:05 very dangerous to do this when implementing authentication. If you do not know what you're doing, try to read up and get informed before you try to code by just guessing what's happening...

squky
Автор

90% of Go development is just writing if err != nil {}

jakcasey
Автор

what was the terminal command at 8:11 when you typed export JWT_SECRET-hunter9999

hakimchulan