I am trying to implement JWT for REST
I'm just curious on this code below
var mySigningKey = []byte("mysecret")
token, err := jwt.Parse(r.Header["Token"][0], func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("There was an error")
}
return mySigningKey, nil
})
fmt.Println(token)
I expect token will return mySigningKey value but its return Token value
that shoud return value inside mySigningKey right?
Or did I misunderstand some concept?
By calling Parse method on your header Token value you are creating the token struct. If you want to get your SigningString you need to call SigningString() method on your token variable.
// Generate the signing string. This is the
// most expensive part of the whole deal. Unless you
// need this for something special, just go straight for
// the SignedString.
func (t *Token) SigningString() (string, error) {
var err error
parts := make([]string, 2)
for i, _ := range parts {
var source map[string]interface{}
if i == 0 {
source = t.Header
} else {
source = t.Claims
}
var jsonValue []byte
if jsonValue, err = json.Marshal(source); err != nil {
return "", err
}
parts[i] = EncodeSegment(jsonValue)
}
return strings.Join(parts, "."), nil
}
The function which is the second argument to Parse is responsible for providing the signing key which will be used to verify the JWT signature is correct.
In the case illustrated in the question, the signing key is a constant because HMAC is used to sign it.
However, if you had a RSA keypair, the token header would be useful to pick the right public key to check the signature, perhaps pulling it down from a well known JWKS endpoint.
Related
I'm trying to write simple JWT implementation with these functionalities:
Generating token using HMAC
Validating token (if signature is correct or exp is not timed out)
Decode token and getting claims
from scratch for better understanding how does it work in depth.
So far I found this article how to build an authentication microservice in golang from scratch. One chapter is dedicated to implementation JWT from scratch. I used it go generate token, however when I paste token in https://jwt.io I've got invalid signature and following warnings:
Warning: Looks like your JWT signature is not encoded correctly using base64url (https://tools.ietforg/html/rfc4648#section-5). Note that padding ("=") must be omitted as per https://tools.ietf.org/html/rfc7515#section-2
Warning: Looks like your JWT header is not encoded correctly using base64url (https://tools.ietforg/html/rfc4648#section-5). Note that padding ("=") must be omitted as per https://tools.ietf.org/html/rfc7515#section-2
Warning: Looks like your JWT payload is not encoded correctly using base64url (https://tools.ietforg/html/rfc4648#section-5). Note that padding ("=") must be omitted as per https://tools.ietf.org/html/rfc7515#section-2
Token I paste look like below:
eyAiYWxnIjogIkhTMjU2IiwgInR5cCI6ICJKV1QiIH0=.eyJhdWQiOiJmcm9udGVuZC5rbm93c2VhcmNoLm1sIiwiZXhwIjoiMTY1MTIyMjcyMyIsImlzcyI6Imtub3dzZWFyY2gubWwifQ==.SqCW8Hxakzck9Puzl0BEOkREPDyl38g2Fd4KFaDazV4=
My JWT code implementation:
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
func GenerateToken(header string, payload map[string]string, secret string) (string, error) {
h := hmac.New(sha256.New, []byte(secret))
header64 := base64.StdEncoding.EncodeToString([]byte(header))
payloadstr, err := json.Marshal(payload)
if err != nil {
return "", err
}
payload64 := base64.StdEncoding.EncodeToString(payloadstr)
message := header64 + "." + payload64
unsignedStr := header + string(payloadstr)
h.Write([]byte(unsignedStr))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
tokenStr := message + "." + signature
return tokenStr, nil
}
func ValidateToken(token string, secret string) (bool, error) {
splitToken := strings.Split(token, ".")
if len(splitToken) != 3 {
return false, nil
}
header, err := base64.StdEncoding.DecodeString(splitToken[0])
if err != nil {
return false, err
}
payload, err := base64.StdEncoding.DecodeString(splitToken[1])
if err != nil {
return false, err
}
unsignedStr := string(header) + string(payload)
h := hmac.New(sha256.New, []byte(secret))
h.Write([]byte(unsignedStr))
signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
fmt.Println(signature)
if signature != splitToken[2] {
return false, nil
}
return true, nil
}
func main() {
claimsMap := map[string]string{
"aud": "frontend.knowsearch.ml",
"iss": "knowsearch.ml",
"exp": fmt.Sprint(time.Now().Add(time.Second * 2).Unix()),
}
secret := "Secure_Random_String"
header := `{ "alg": "HS256", "typ": "JWT" }`
tokenString, err := GenerateToken(header, claimsMap, secret)
if err != nil {
fmt.Println(err)
return
}
fmt.Println("token: ", tokenString)
isValid, _ := ValidateToken(tokenString, secret)
fmt.Println("is token valid: ", isValid)
duration := time.Second * 4
time.Sleep(duration)
isValid, _ = ValidateToken(tokenString, secret)
fmt.Println("is token valid: ", isValid)
}
What's wrong with implementation above and how to fix it and get rid of warnings?
I decided to use Golang for implementation, however examples in any other languages very appreciated.
JWT specification requires that all padding = characters are removed:
Base64 encoding using the URL- and filename-safe character set
defined in Section 5 of RFC 4648 [RFC4648], with all trailing '='
characters omitted (as permitted by Section 3.2) and without the
inclusion of any line breaks, whitespace, or other additional
characters.
You can use base64.RawURLEncoding , which creates Base64Url encoding without padding, instead of base64.StdEncoding.
You can see the differences between the StdEncoding, RawStdEncodingand RawURLEncoding in this short Go Playground example.
Also, I strongly recommend to use a JWT library if it's not for learning exercise.
According to Coinbase pro API docs:
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp +
method + requestPath + body (where + represents string concatenation)
and base64-encode the output. The timestamp value is the same as the
CB-ACCESS-TIMESTAMP header.
The body is the request body string or omitted if there is no request
body (typically for GET requests).
The method should be UPPER CASE.
I borrowed a signing function from a better programmer and feed it something like this:
1619383731POST/reports{{"end_date":"2021-01-02T11:59:59Z","start_date":"2020-01-01T00:00:00Z","type":"account"}}
But keep getting invalid signature from Coinbase.
Signing function for reference:
// sign
func (e *exchange) sign(msg string) string {
key, err := base64.StdEncoding.DecodeString(e.http.secret)
if e.checkErr(err) {
return "bad_sig"
}
signature := hmac.New(sha256.New, key)
_, err = signature.Write([]byte(msg))
if e.checkErr(err) {
return "bad_sig"
}
return base64.StdEncoding.EncodeToString(signature.Sum(nil))
}
Where am I screwing up?
Have you checked out the go-coinbase github repo implementing this as:
https://github.com/preichenberger/go-coinbasepro/blob/master/client.go
h := make(map[string]string)
h["CB-ACCESS-KEY"] = c.Key
h["CB-ACCESS-PASSPHRASE"] = c.Passphrase
h["CB-ACCESS-TIMESTAMP"] = timestamp
message := fmt.Sprintf(
"%s%s%s%s",
timestamp,
method,
url,
data,
)
sig, err := generateSig(message, c.Secret)
if err != nil {
return nil, err
}
h["CB-ACCESS-SIGN"] = sig
return h, nil
It turns out using req.Body directly is the culprit. Why I don't know and something I need to find out, but reading into a []byte and then casting to a string solves part of it.
I'm reading the example folder for JWT i'm a bit unsure how things work for validating the token.
func ExampleNewWithClaims_customClaimsType() {
mySigningKey := []byte("AllYourBase")
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.StandardClaims
}
// Create the Claims
claims := MyCustomClaims{
"bar",
jwt.StandardClaims{
ExpiresAt: 15000,
Issuer: "test",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
ss, err := token.SignedString(mySigningKey)
fmt.Printf("%v %v", ss, err)
//Output: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c <nil>
}
Here it's straight forward and the token is being signed here token.SignedString(mySigningKey) using "mySigningKey"
Now there unparsing is much less clear for me :
func ExampleParseWithClaims_customClaimsType() {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
type MyCustomClaims struct {
Foo string `json:"foo"`
jwt.StandardClaims
}
// sample token is expired. override time so it parses as valid
at(time.Unix(0, 0), func() {
token, err := jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
if claims, ok := token.Claims.(*MyCustomClaims); ok && token.Valid {
fmt.Printf("%v %v", claims.Foo, claims.StandardClaims.ExpiresAt)
} else {
fmt.Println(err)
}
})
// Output: bar 15000
}
Is order to validate that the signature of the token string given back by the client is valid would you need to
decode the claim ( done in &MyCustomClaims{} )
validate that the signature part of the decoded claim is valid against the " pub key included in the token" using token.Valid.
But the example is just decoding the key and by "magic" the return is the secret/signing key?
It doesn't make sense to me at all. Also returning a valid claim for a public key is useless as it could have been done by any private key.
What am i missing ?
Validation is not done by the public key included in the token.
HS256 is symmetric, so whatever key you used to sign the token, you have to use the same key to validate the signature, and that's what's happening. The function passed to ParseWithClaims is returning the same key used to sign the token.
If an asymmetric signing algorithm was used, you'd use the private key to sign the token, and then the public key to validate it, and that nested function would have to return the public key so that ParseWithClaims can validate it.
It sounds like the part that confused you is:
jwt.ParseWithClaims(tokenString, &MyCustomClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("AllYourBase"), nil
})
The nested function passed to ParseWithClaims is supposed to inspect the token passed, and return the correct key that can be used to verify the signature. For HS256, it is the same as the key used to sign it. For RSxxx, it'd be the public key, and which public key it should return can be retrieved from the token passed in. It usually contains a public key id so you can select the correct key.
func Login(c echo.Context) error {
user := &users.User{}
if err := c.Bind(&user); err != nil {
return err
}
return token.SigIn(c, user.Email, user.Password)
}
This is my Login function that retrieve the token when the user send the requests.
the Signin func that handle the token
func SigIn(c echo.Context, email, password string) error {
user := users.User{}
db := database.SetUp()
if err := db.Where("email = ?", email).First(&user).Error; gorm.IsRecordNotFoundError(err) {
restErr := errors.NewBadRequestError("Invalid credentials")
return c.JSON(http.StatusBadRequest, restErr)
}
if user.VerifyPassword(password) != nil {
restErr := errors.NewUnauthorizedError("Couldn't log you in with these credentials")
return c.JSON(http.StatusUnauthorized, restErr)
}
//user is successfull
return CreateToken(c)
}
the CreateToken func is as follow
type TokenJWT struct {
Token string `json:"token"`
}
func CreateToken(c echo.Context) error {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["authorized"] = true
claims["name"] = "Pascal Gaetan"
claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("my_secret_key"))
if err != nil {
return err
}
return c.JSON(http.StatusOK, TokenJWT{
Token: t,
})
}
when everyhting is succesfull, i would like to get the authenticated user through an URL /api/me that calls a Me function
Let me split your question into two parts: the first one is how to easily encode and decode user in or from JWT token and the second part is how to write a generic code which can retrieve user from everywhere.
From your example I mentioned that you created a MapClaims but to reduce parsing complexity it will be better to create a token using a custom claims type. If you are using dgrijalva/jwt-go, then according to documentation you can do something like that
type UserClaims struct {
Name string `json:"name"`
jwt.StandardClaims
}
// encode it as before, but with your created type
t := jwt.New(signer)
userClaims := &UserClaims{Name: "Burmese"}
t.Claims = userClaims
tokenString, err = t.SignedString(]byte("my_secret_key"))
then you can parse your user in your router/framework middleware with
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("my_secret_key"), nil
})
if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
fmt.Printf("%v %v", claims.Name, claims.StandardClaims.ExpiresAt)
} else {
fmt.Println(err)
}
This example was adopted from an official documentation here
Now you know how to parse authenticated user struct with ease and the next logic move is to wrap it into your middleware. Whether there are a lot of implementation details like you can retrieve JWT from cookie, header or query, also defining some ordering on them, the gist the following: you should have wrapped abovementioned code into your middleware and after parsing the struct you can pass it via your request context. I don't use echo and other frameworks, but for pure net/http you can pass your parsed struct from middleware with
context.WithValue(ctx, UserCtxKey, claims)
Hope it helps!
This is a fairly common design pattern to create an authenticated client and then call various action methods on it. You could do something like the following:
type Client struct {
... // other members
token string // unexported unless there is a special reason to do otherwise
}
func NewClient(c echo.Context, email, password string) (*Client, error) {
user := users.User{}
cl := Client{}
... // your original method
cl.token = token
return &cl, nil
}
func (c *Client) DoSomething(...) ... { ... }
I have REST services:
each request has a header with JWT token
each controller get parameters from request (variables, body..) and pass them to data layer
I need to pass JWT token from header of each request into corresponding data layer method like this:
func (a *App) UpdateOrder(_ http.ResponseWriter, r *http.Request) (interface{}, error) {
bodyData := new(models.Order)
err = json.NewDecoder(r.Body).Decode(&bodyData)
if err != nil {
return nil, err
}
user, err := a.Saga.GetUserByToken(r.Header.Get("Authorization")) // here
// error handling ...
a.DbLayer.UpdateOrder(id, bodyData, user) // and there
}
In this case I must write the same code for each controller to get the user by token, and pass this user to database layer explicitly.
Is there a way to pass this user for each request without writing this code in each controller ?
I know about middleware and I can get user by token in my middleware. But how can I pass this user from middleware to corresponding database level method ?
May be I am looking for something like "global variables" for goroutine ? I can get user in my middleware and set it to something like "global variable". I can get the value of this "global variable" in the database layer. But it must be "global variable" for the current web request and concurrent web requests mustn't affect to each other.
Is there a some mechanism in Go, http module or gorilla\mux to implement what I have called "global variables" ?
You are describing contexts.
Originally there was the gorilla context package, which provides a pseudoglobal context object - essentially a map[interface{}]interface{} with a reference intrinsicly available to all players in the middleware/controller/datalayer stack.
See this except from an excellent guide to the package (all credit to the author, Matt Silverlock).
type contextKey int
// Define keys that support equality.
const csrfKey contextKey = 0
const userKey contextKey = 1
var ErrCSRFTokenNotPresent = errors.New("CSRF token not present in the request context.")
// We'll need a helper function like this for every key:type
// combination we store in our context map else we repeat this
// in every middleware/handler that needs to access the value.
func GetCSRFToken(r *http.Request) (string, error) {
val, ok := context.GetOk(r, csrfKey)
if !ok {
return "", ErrCSRFTokenNotPresent
}
token, ok := val.(string)
if !ok {
return "", ErrCSRFTokenNotPresent
}
return token, nil
}
// A bare-bones example
func CSRFMiddleware(h http.Handler) http.Handler {
return func(w http.ResponseWriter, r *http.Request) {
token, err := GetCSRFToken(r)
if err != nil {
http.Error(w, "No good!", http.StatusInternalServerError)
return
}
// The map is global, so we just call the Set function
context.Set(r, csrfKey, token)
h.ServeHTTP(w, r)
}
}
After the gorilla package's inception, a context package was added to the standard library. It's slightly different, in that contexts are no longer pseudoglobal, but instead passed from method to method. Under this, the context comes attached to the initial request - available via request.Context. Layers below the handler can accept a context value as a part of their signature, and read values from it.
Here's a simplified example:
type contextKey string
var (
aPreSharedKey = contextKey("a-preshared-key")
)
func someHandler(w http.ResponseWriter, req *http.Request) {
ctx := context.WithValue(req.Context, aPreSharedKey, req.Header.Get("required-header"))
data, err := someDataLayerFunction(ctx)
if err != nil {
fmt.Fprintf(w, "uhoh", http.StatusBadRequest)
return
}
fmt.Fprintf(w, data, http.StatusOK)
}
func someDataLayerFunction(ctx context.Context) (string, error) {
val, ok := ctx.Value(aPreSharedKey).(string)
if !ok {
return nil, errors.New("required context value missing")
}
return val
}
For more details and a less contrived example, check out google's excellent blog on the context package's use.