I'm trying to plug JWT authentication within a very simple go service written with go-restful .
The code is very similar to:
package main
import (
"github.com/emicklei/go-restful"
"log"
"net/http"
)
type User struct {
Id, Name string
}
type UserList struct {
Users []User
}
func getAllUsers(request *restful.Request, response *restful.Response) {
log.Printf("getAllUsers")
response.WriteEntity(UserList{[]User{{"42", "Gandalf"}, {"3.14", "Pi"}}})
}
func NewUserService() *restful.WebService {
ws := new(restful.WebService)
ws.
Path("/users").
Consumes(restful.MIME_XML, restful.MIME_JSON).
Produces(restful.MIME_JSON, restful.MIME_XML)
ws.Route(ws.GET("").To(getAllUsers))
return ws
}
func main() {
restful.Add(NewUserService())
log.Printf("start listening on localhost:8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
where restful.Request
is a wrapper around http.Request
.
That being said, it might be possible to use the Auth0 jwt middleware .
But as a golang newbie, I'm a bit lost in the plumbing process. I see that I must use a Filter
function like
ws.Filter(jwtAuthentication)
where
func jwtAuthentication(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
// Jwt Magic goes here \o
chain.ProcessFilter(req, resp)
}
But I don't figure how and where should I instanciate the JWT middleware.
Here is the example of filter implementation using auth0/go-jwt-middleware . In real life you probably want to avoid creating new instance of jwtMiddleware
every time.
func jwtAuthentication(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
jwtMiddleware := jwtmiddleware.New(jwtmiddleware.Options{
ValidationKeyGetter: func(token *jwt.Token) (interface{}, error) {
return []byte("My Secret"), nil
},
SigningMethod: jwt.SigningMethodHS256,
})
if err := jwtMiddleware.CheckJWT(resp.ResponseWriter, req.Request); err != nil {
logger.Errorf("Authentication error: %v", err)
}
chain.ProcessFilter(req, resp)
}
After the filter the parsed token will be in the context ( auth0/go-jwt-middleware uses gorilla/context ). Default context key is user
.
Note: when JWTMiddleware.SigningMethod
is set, the middleware verifies that tokens are signed with the specific signing algorithm.
If the signing method is not constant, the ValidationKeyGetter
callback can be used to implement additional checks.
Important to avoid security issues described here .
Here is example of Login API to generate Token, and JWT Authentication filter to check authentication
import (
"os"
"strings"
"github.com/dgrijalva/jwt-go"
"golang.org/x/crypto/bcrypt"
)
type Token struct {
UserId uint
Username string
jwt.StandardClaims
}
type Account struct {
ID uint
Email string
Password string
Token string
}
func Login(request *restful.Request, response *restful.Response) {
account := &Account{ID: 1, Email: "test@email.com" }
// TODO - account should be pulled from database
tk := &Token{ UserId: account.ID }
token := jwt.NewWithClaims(jwt.GetSigningMethod("HS256"), tk)
tokenString, _ := token.SignedString([]byte("JWT-SECRET-GOES-RIGHT-HERE"))
account.Token = tokenString
account.Password = ''
response.WriteEntity(account)
}
func JwtAuthentication(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) {
tokenHeader := req.Request.HeaderParameter("Authorization")
if tokenHeader == "" {
resp.WriteErrorString(http.StatusForbidden, "Not Authorized")
return
}
splitted := strings.Split(tokenHeader, " ")
if len(splitted) != 2 {
resp.WriteErrorString(http.StatusForbidden, "Not Authorized")
return
}
tokenPart := splitted[1]
tk := &Token{}
token, err := jwt.ParseWithClaims(tokenPart, tk, func(token *jwt.Token) (interface{}, error) {
return []byte("JWT-SECRET-GOES-RIGHT-HERE"), nil
})
if err != nil { //Malformed token, returns with http code 403 as usual
resp.WriteErrorString(http.StatusForbidden, "Not Authorized")
return
}
if !token.Valid { //Token is invalid, maybe not signed on this server
resp.WriteErrorString(http.StatusForbidden, "Not Authorized")
return
}
chain.ProcessFilter(req, resp)
}
And then apply filter
ws.Filter(JwtAuthentication)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.