Skip to content

Instantly share code, notes, and snippets.

@mohashari
Created March 14, 2026 15:12
Show Gist options
  • Select an option

  • Save mohashari/b46d87053c985fbc2d46cb6769eaf03d to your computer and use it in GitHub Desktop.

Select an option

Save mohashari/b46d87053c985fbc2d46cb6769eaf03d to your computer and use it in GitHub Desktop.
Code snippets — Jwt Authentication Deep Dive
func RefreshTokens(refreshToken string) (accessToken, newRefreshToken string, err error) {
// Validate refresh token against database
stored, err := db.GetRefreshToken(ctx, refreshToken)
if err != nil || stored.ExpiresAt.Before(time.Now()) {
return "", "", errors.New("invalid or expired refresh token")
}
// Generate new tokens
accessToken, _ = GenerateAccessToken(stored.UserID)
newRefreshToken = generateSecureRandom(32)
// Rotate: delete old, store new (refresh token rotation)
db.DeleteRefreshToken(ctx, refreshToken)
db.StoreRefreshToken(ctx, stored.UserID, newRefreshToken, 7*24*time.Hour)
return accessToken, newRefreshToken, nil
}
// BAD - anyone with the token can read this
{ "sub": "42", "credit_card": "4111-1111-1111-1111" }
// GOOD - minimal claims only
{ "sub": "42", "role": "user" }
1. Login → server returns:
- access_token (15 min TTL, stored in memory)
- refresh_token (7 days TTL, stored in httpOnly cookie)
2. API requests use access_token in Authorization header
3. When access_token expires:
- Client sends refresh_token
- Server validates and issues new access_token + rotated refresh_token
4. Logout → invalidate refresh_token in database
// WRONG - vulnerable to alg:none attack
jwt.Parse(tokenString, func(t *jwt.Token) (interface{}, error) {
return signingKey, nil
})
// RIGHT - verify algorithm first
jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}
return signingKey, nil
})
jwt.ParseWithClaims(tokenString, &Claims{},
keyFunc,
jwt.WithExpirationRequired(),
jwt.WithIssuer("myapp"),
jwt.WithAudience("myapp-api"),
)
package auth
import (
"time"
"github.com/golang-jwt/jwt/v5"
)
type Claims struct {
UserID string `json:"sub"`
Email string `json:"email"`
Role string `json:"role"`
jwt.RegisteredClaims
}
var signingKey = []byte(os.Getenv("JWT_SECRET"))
func GenerateToken(userID, email, role string) (string, error) {
claims := Claims{
UserID: userID,
Email: email,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(15 * time.Minute)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "myapp",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(signingKey)
}
func ValidateToken(tokenString string) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
// CRITICAL: verify the signing method
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}
return signingKey, nil
})
if err != nil {
return nil, err
}
claims, ok := token.Claims.(*Claims)
if !ok || !token.Valid {
return nil, fmt.Errorf("invalid token")
}
return claims, nil
}
// WRONG
localStorage.setItem('access_token', token);
// RIGHT - store in memory (React state, etc.)
setAccessToken(token);
// Refresh token in httpOnly cookie (set by server)
// document.cookie cannot access httpOnly cookies
// Header
{ "alg": "HS256", "typ": "JWT" }
// Payload
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 1516242622
}
// Signature = HMAC_SHA256(base64(header) + "." + base64(payload), secret)
# Generate a strong secret
openssl rand -hex 32
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment