Merge pull request #19 from dhax/hyperyuri-update/jwt-package

Hyperyuri update/jwt package
This commit is contained in:
dhax 2021-11-15 15:20:26 +01:00 committed by GitHub
commit c2f958eaa2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 70 additions and 49 deletions

1
.gitignore vendored
View file

@ -1,6 +1,7 @@
.DS_STORE
.vscode
.realize.yaml
.idea
debug
go-base

View file

@ -4,6 +4,8 @@ import (
"context"
"net/http"
"github.com/lestrrat-go/jwx/jwt"
"github.com/go-chi/jwtauth/v5"
"github.com/go-chi/render"
@ -32,7 +34,7 @@ func RefreshTokenFromCtx(ctx context.Context) string {
// response for any unverified tokens and passes the good ones through.
func Authenticator(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, claims, err := jwtauth.FromContext(r.Context())
token, claims, err := jwtauth.FromContext(r.Context())
if err != nil {
logging.GetLogEntry(r).Warn(err)
@ -40,6 +42,11 @@ func Authenticator(next http.Handler) http.Handler {
return
}
if err := jwt.Validate(token); err != nil {
render.Render(w, r, ErrUnauthorized(ErrTokenExpired))
return
}
// Token is authenticated, parse claims
var c AppClaims
err = c.ParseClaims(claims)
@ -58,13 +65,18 @@ func Authenticator(next http.Handler) http.Handler {
// AuthenticateRefreshJWT checks validity of refresh tokens and is only used for access token refresh and logout requests. It responds with 401 Unauthorized for invalid or expired refresh tokens.
func AuthenticateRefreshJWT(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, claims, err := jwtauth.FromContext(r.Context())
token, claims, err := jwtauth.FromContext(r.Context())
if err != nil {
logging.GetLogEntry(r).Warn(err)
render.Render(w, r, ErrUnauthorized(ErrTokenUnauthorized))
return
}
if err := jwt.Validate(token); err != nil {
render.Render(w, r, ErrUnauthorized(ErrTokenExpired))
return
}
// Token is authenticated, parse refresh token string
var c RefreshClaims
err = c.ParseClaims(claims)

View file

@ -6,11 +6,17 @@ import (
"github.com/lestrrat-go/jwx/jwt"
)
type CommonClaims struct {
ExpiresAt int64 `json:"exp,omitempty"`
IssuedAt int64 `json:"iat,omitempty"`
}
// AppClaims represent the claims parsed from JWT access token.
type AppClaims struct {
ID int `json:"id,omitempty"`
Sub string `json:"sub,omitempty"`
Roles []string `json:"roles,omitempty"`
CommonClaims
}
// ParseClaims parses JWT claims into AppClaims.
@ -47,6 +53,7 @@ func (c *AppClaims) ParseClaims(claims map[string]interface{}) error {
type RefreshClaims struct {
ID int `json:"id,omitempty"`
Token string `json:"token,omitempty"`
CommonClaims
}
// ParseClaims parses the JWT claims into RefreshClaims.

View file

@ -1,13 +1,12 @@
package jwt
import (
"context"
"crypto/rand"
"encoding/json"
"net/http"
"time"
"github.com/go-chi/jwtauth/v5"
"github.com/lestrrat-go/jwx/jwt"
"github.com/spf13/viper"
)
@ -54,35 +53,40 @@ func (a *TokenAuth) GenTokenPair(accessClaims AppClaims, refreshClaims RefreshCl
// CreateJWT returns an access token for provided account claims.
func (a *TokenAuth) CreateJWT(c AppClaims) (string, error) {
token := jwt.New()
token.Set(jwt.IssuedAtKey, time.Now().Unix())
token.Set(jwt.ExpirationKey, time.Now().Add(a.JwtExpiry).Unix())
c.IssuedAt = time.Now().Unix()
c.ExpiresAt = time.Now().Add(a.JwtExpiry).Unix()
token.Set(jwt.SubjectKey, c.Sub)
token.Set(`id`, c.ID)
token.Set(`roles`, c.Roles)
tokenMap, err := token.AsMap(context.Background())
claims, err := ParseStructToMap(c)
if err != nil {
return "", err
}
_, tokenString, err := a.JwtAuth.Encode(tokenMap)
_, tokenString, err := a.JwtAuth.Encode(claims)
return tokenString, err
}
func ParseStructToMap(c interface{}) (map[string]interface{}, error) {
var claims map[string]interface{}
inrec, _ := json.Marshal(c)
err := json.Unmarshal(inrec, &claims)
if err != nil {
return nil, err
}
return claims, err
}
// CreateRefreshJWT returns a refresh token for provided token Claims.
func (a *TokenAuth) CreateRefreshJWT(c RefreshClaims) (string, error) {
token := jwt.New()
token.Set(jwt.IssuedAtKey, time.Now().Unix())
token.Set(jwt.ExpirationKey, time.Now().Add(a.JwtRefreshExpiry).Unix())
c.IssuedAt = time.Now().Unix()
c.ExpiresAt = time.Now().Add(a.JwtRefreshExpiry).Unix()
token.Set(`token`, c.Token)
tokenMap, err := token.AsMap(context.Background())
claims, err := ParseStructToMap(c)
if err != nil {
return "", err
}
_, tokenString, err := a.JwtAuth.Encode(tokenMap)
_, tokenString, err := a.JwtAuth.Encode(claims)
return tokenString, err
}

View file

@ -2,7 +2,6 @@ package pwdless
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
@ -16,7 +15,6 @@ import (
"time"
"github.com/go-chi/chi/v5"
jwx_jwt "github.com/lestrrat-go/jwx/jwt"
"github.com/spf13/viper"
"github.com/dhax/go-base/auth/jwt"
@ -247,6 +245,9 @@ func TestAuthResource_refresh(t *testing.T) {
// }
refreshJWT := genRefreshJWT(jwt.RefreshClaims{
Token: tc.token,
CommonClaims: jwt.CommonClaims{
ExpiresAt: time.Now().Add(time.Minute * tc.exp).UnixNano(),
},
})
res, body := testRequest(t, ts, "POST", "/refresh", nil, refreshJWT)
@ -310,6 +311,9 @@ func TestAuthResource_logout(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
refreshJWT := genRefreshJWT(jwt.RefreshClaims{
Token: tc.token,
CommonClaims: jwt.CommonClaims{
ExpiresAt: time.Now().Add(time.Minute * tc.exp).UnixNano(),
},
})
res, body := testRequest(t, ts, "POST", "/logout", nil, refreshJWT)
@ -355,29 +359,16 @@ func testRequest(t *testing.T, ts *httptest.Server, method, path string, body io
}
// func genJWT(c jwt.AppClaims) string {
// token := jwx_jwt.New()
// token.Set(jwx_jwt.IssuedAtKey, time.Now().Unix())
// token.Set(jwx_jwt.ExpirationKey, time.Now().Add(time.Duration(time.Minute)).Unix())
// tokenMap, err := token.AsMap(context.Background())
// if err != nil {
// return ""
// }
// _, tokenString, _ := auth.TokenAuth.JwtAuth.Encode(tokenMap)
// claims, _ := jwt.ParseStructToMap(c)
// _, tokenString, _ := auth.TokenAuth.JwtAuth.Encode(claims)
// return tokenString
// }
func genRefreshJWT(c jwt.RefreshClaims) string {
token := jwx_jwt.New()
token.Set(jwx_jwt.IssuedAtKey, time.Now())
token.Set(jwx_jwt.ExpirationKey, time.Now().Add(time.Duration(time.Minute)))
claims, _ := jwt.ParseStructToMap(c)
token.Set(`token`, c.Token)
tokenMap, err := token.AsMap(context.Background())
if err != nil {
return ""
}
_, tokenString, _ := auth.TokenAuth.JwtAuth.Encode(tokenMap)
_, tokenString, _ := auth.TokenAuth.JwtAuth.Encode(claims)
return tokenString
}

9
go.mod
View file

@ -17,7 +17,7 @@ require (
github.com/gorilla/css v1.0.0 // indirect
github.com/jaytaylor/html2text v0.0.0-20200220170450-61d9dc4d7195
github.com/jinzhu/inflection v0.0.0-20180308033659-04140366298a // indirect
github.com/lestrrat-go/jwx v1.1.6
github.com/lestrrat-go/jwx v1.2.10
github.com/mattn/go-runewidth v0.0.8 // indirect
github.com/mitchellh/go-homedir v1.1.0
github.com/mssola/user_agent v0.5.1
@ -45,12 +45,13 @@ require (
)
require (
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/goccy/go-json v0.4.8 // indirect
github.com/goccy/go-json v0.7.10 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.7 // indirect
github.com/lestrrat-go/backoff/v2 v2.0.8 // indirect
github.com/lestrrat-go/blackmagic v1.0.0 // indirect
github.com/lestrrat-go/httpcc v1.0.0 // indirect
github.com/lestrrat-go/iter v1.0.1 // indirect
github.com/lestrrat-go/option v1.0.0 // indirect

15
go.sum
View file

@ -79,8 +79,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/chaincfg/chainhash v1.0.2/go.mod h1:BpbrGgrPTr3YJYRN3Bm+D9NuaFd+zGyNeIKgrhCXK60=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0 h1:sgNeV1VRMDzs6rzyPpxyM0jp317hnwiq58Filgag2xw=
github.com/decred/dcrd/dcrec/secp256k1/v3 v3.0.0/go.mod h1:J70FGZSbzsjecRTiTzER+3f1KZLNaXkuv+yeFTKoxM8=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d/go.mod h1:tmAIfUFEirG/Y8jhZ9M+h36obRZAk/1fcSpXwAVlfqE=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@ -122,8 +123,9 @@ github.com/go-pg/migrations v6.2.0+incompatible/go.mod h1:DtFiob3rFxsj0He8fye6Ta
github.com/go-pg/pg v7.1.7+incompatible h1:MXeUtzJtt9hie8LSANh0FvaS7ANQ535qD3Q5dtXy8q8=
github.com/go-pg/pg v7.1.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/goccy/go-json v0.4.8 h1:TfwOxfSp8hXH+ivoOk36RyDNmXATUETRdaNWDaZglf8=
github.com/goccy/go-json v0.4.8/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/goccy/go-json v0.7.10 h1:ulhbuNe1JqE68nMRXXTJRrUu0uhouf0VevLINxQq4Ec=
github.com/goccy/go-json v0.7.10/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/uuid v3.2.0+incompatible h1:y12jRkkFxsd7GpqdSZ+/KCs/fJbqpEXSGd4+jfEaewE=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@ -250,19 +252,22 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lestrrat-go/backoff/v2 v2.0.7 h1:i2SeK33aOFJlUNJZzf2IpXRBvqBBnaGXfY5Xaop/GsE=
github.com/lestrrat-go/backoff/v2 v2.0.7/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A=
github.com/lestrrat-go/backoff/v2 v2.0.8/go.mod h1:rHP/q/r9aT27n24JQLa7JhSQZCKBBOiM/uP402WwN8Y=
github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4=
github.com/lestrrat-go/blackmagic v1.0.0/go.mod h1:TNgH//0vYSs8VXDCfkZLgIrVTTXQELZffUV0tz3MtdQ=
github.com/lestrrat-go/codegen v1.0.0/go.mod h1:JhJw6OQAuPEfVKUCLItpaVLumDGWQznd1VaXrBk9TdM=
github.com/lestrrat-go/httpcc v1.0.0 h1:FszVC6cKfDvBKcJv646+lkh4GydQg2Z29scgUfkOpYc=
github.com/lestrrat-go/httpcc v1.0.0/go.mod h1:tGS/u00Vh5N6FHNkExqGGNId8e0Big+++0Gf8MBnAvE=
github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A=
github.com/lestrrat-go/iter v1.0.1/go.mod h1:zIdgO1mRKhn8l9vrZJZz9TUMMFbQbLeTsbqPDrJ/OJc=
github.com/lestrrat-go/jwx v1.1.6 h1:VfyUo2PAU4lO/liwhdwiSZ55/QZDLTT3EYY5z9KfwZs=
github.com/lestrrat-go/jwx v1.1.6/go.mod h1:c+R8G7qsaFNmTzYjU98A+sMh8Bo/MJqO9GnpqR+X024=
github.com/lestrrat-go/jwx v1.2.10 h1:rz6Ywm3wCRWsy2lyRZ7uHzE4E09m7X9eINaoAEVXCKw=
github.com/lestrrat-go/jwx v1.2.10/go.mod h1:25DcLbNWArPA/Ew5CcBmewl32cJKxOk5cbepBsIJFzw=
github.com/lestrrat-go/option v0.0.0-20210103042652-6f1ecfceda35/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4=
github.com/lestrrat-go/option v1.0.0/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
github.com/lestrrat-go/pdebug/v3 v3.0.1 h1:3G5sX/aw/TbMTtVc9U7IHBWRZtMvwvBziF1e4HoQtv8=
github.com/lestrrat-go/pdebug/v3 v3.0.1/go.mod h1:za+m+Ve24yCxTEhR59N7UlnJomWwCiIqbJRmKeiADU4=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls=