├── .gitignore ├── go.mod ├── .travis.yml ├── signing.go ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── echo.go ├── gin.go ├── web_framework.go ├── LICENSE ├── authorization.go ├── jwt.go ├── custom.go ├── CODE_OF_CONDUCT.md ├── go.sum └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/supanadit/jwt-go 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/dgrijalva/jwt-go v3.2.0+incompatible 7 | github.com/gin-gonic/gin v1.6.2 8 | github.com/labstack/echo/v4 v4.1.16 9 | github.com/mitchellh/mapstructure v1.2.2 10 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 11 | ) 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | matrix: 4 | fast_finish: true 5 | include: 6 | - go: 1.14.x 7 | - go: master 8 | 9 | git: 10 | depth: 10 11 | 12 | before_install: 13 | - if [[ "${GO111MODULE}" = "on" ]]; then mkdir "${HOME}/go"; export GOPATH="${HOME}/go"; fi 14 | 15 | install: 16 | - if [[ "${GO111MODULE}" = "on" ]]; then go mod download; fi 17 | - if [[ "${GO111MODULE}" = "on" ]]; then export PATH="${GOPATH}/bin:${GOROOT}/bin:${PATH}"; fi 18 | - if [[ "${GO111MODULE}" = "on" ]]; then make tools; fi 19 | 20 | go_import_path: github.com/supanadit/jwt-go 21 | 22 | after_success: 23 | - bash <(curl -s https://codecov.io/bash) -------------------------------------------------------------------------------- /signing.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "github.com/dgrijalva/jwt-go" 5 | ) 6 | 7 | // SigningHMAC is type of signing that can be used for signing and validation JWT 8 | type SigningHMAC struct { 9 | Method *jwt.SigningMethodHMAC 10 | } 11 | 12 | // HS256 is type of SigningHMAC method 13 | func HS256() SigningHMAC { 14 | return SigningHMAC{Method: jwt.SigningMethodHS256} 15 | } 16 | 17 | // HS384 is type of SigningHMAC method 18 | func HS384() SigningHMAC { 19 | return SigningHMAC{Method: jwt.SigningMethodHS384} 20 | } 21 | 22 | // HS512 is type of SigningHMAC method 23 | func HS512() SigningHMAC { 24 | return SigningHMAC{Method: jwt.SigningMethodHS512} 25 | } 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /echo.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "github.com/labstack/echo/v4" 5 | ) 6 | 7 | // GetJWTFromEchoHeader get JWT from header which provided by echo web framework 8 | func GetJWTFromEchoHeader(c echo.Context) (token string, err error) { 9 | return GetJWTFromHeader(c.Request().Header.Get("Authorization")) 10 | } 11 | 12 | // VerifyEchoHeader only verify echo header 13 | func VerifyEchoHeader(c echo.Context) (string, bool, error) { 14 | return VerifyAndBindingEchoHeader(nil, c) 15 | } 16 | 17 | // VerifyAndBindingEchoHeader verify and binding the jwt model 18 | func VerifyAndBindingEchoHeader(model interface{}, c echo.Context) (token string, isValid bool, err error) { 19 | token, err = GetJWTFromEchoHeader(c) 20 | isValid, err = VerifyAndBinding(&model, token) 21 | return token, isValid, err 22 | } 23 | -------------------------------------------------------------------------------- /gin.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | ) 6 | 7 | // GetJWTFromGinHeader is to get JWT from header which provided by gin web framework 8 | func GetJWTFromGinHeader(c *gin.Context) (token string, err error) { 9 | return GetJWTFromHeader(c.GetHeader("Authorization")) 10 | } 11 | 12 | // VerifyGinHeader is the function that only verify gin header 13 | func VerifyGinHeader(c *gin.Context) (string, bool, error) { 14 | return VerifyAndBindingGinHeader(nil, c) 15 | } 16 | 17 | // VerifyAndBindingGinHeader is to verify and binding the jwt model 18 | func VerifyAndBindingGinHeader(model interface{}, c *gin.Context) (token string, isValid bool, err error) { 19 | token, err = GetJWTFromGinHeader(c) 20 | isValid, err = VerifyAndBinding(&model, token) 21 | return token, isValid, err 22 | } 23 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Browser [e.g. chrome, safari] 29 | - Version [e.g. 22] 30 | 31 | **Smartphone (please complete the following information):** 32 | - Device: [e.g. iPhone6] 33 | - OS: [e.g. iOS8.1] 34 | - Browser [e.g. stock browser, safari] 35 | - Version [e.g. 22] 36 | 37 | **Additional context** 38 | Add any other context about the problem here. 39 | -------------------------------------------------------------------------------- /web_framework.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | // GetJWTFromHeader get JWT from header which provided by any of web framework, but with rules JWT "token" 9 | func GetJWTFromHeader(header string) (token string, err error) { 10 | if header != "" { 11 | splitAuthorization := strings.Split(header, " ") 12 | if len(splitAuthorization) != 0 && len(splitAuthorization) == 2 { 13 | if splitAuthorization[0] != "JWT" { 14 | err = errors.New("unknown authorization type") 15 | } else { 16 | token = splitAuthorization[1] 17 | } 18 | } else { 19 | err = errors.New("invalid authorization header") 20 | } 21 | } else { 22 | err = errors.New("no authorization provided") 23 | } 24 | return token, err 25 | } 26 | 27 | // VerifyAndBinding is to verify and binding any given token 28 | func VerifyAndBinding(model interface{}, t string) (bool, error) { 29 | isValid, err := VerifyAndBindingJWT(&model, t) 30 | if err != nil { 31 | if !IsUseAuthorization() { 32 | err = nil 33 | isValid = !IsUseAuthorization() 34 | } 35 | } 36 | return isValid, err 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Supan Adit Pratama 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /authorization.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | // Authorization is the default authorization model 4 | type Authorization struct { 5 | Username string `form:"username" json:"username" xml:"username" binding:"required"` 6 | Password string `form:"password" json:"password" xml:"password" binding:"required"` 7 | } 8 | 9 | // GenerateJWT is to generate JWT token by authorization model 10 | func (auth Authorization) GenerateJWT() (string, error) { 11 | return GenerateJWT(auth) 12 | } 13 | 14 | // GenerateJWTAndSetExpiredTime generate JWT token by authorization model also set the expired time manually 15 | func (auth Authorization) GenerateJWTAndSetExpiredTime(hour int64, minute int64, seconds int64) (string, error) { 16 | return GenerateJWTAndSetExpiredTime(auth, hour, minute, seconds) 17 | } 18 | 19 | // GetPasswordEncryption is to create password encryption using bcrypt 20 | func (auth Authorization) GetPasswordEncryption() ([]byte, error) { 21 | return EncryptPassword(auth.Password) 22 | } 23 | 24 | // GetStringPasswordEncryption is to create password encryption using bcrypt and string as the result 25 | func (auth Authorization) GetStringPasswordEncryption() (string, error) { 26 | encryption, err := auth.GetPasswordEncryption() 27 | return string(encryption), err 28 | } 29 | 30 | // VerifyJWT is the simply way to check authorization 31 | func (auth Authorization) VerifyJWT(token string) (bool, error) { 32 | return VerifyJWT(token) 33 | } 34 | 35 | // VerifyPassword is the fastest way to verify password 36 | func (auth Authorization) VerifyPassword(password string) (bool, error) { 37 | ep, err := auth.GetStringPasswordEncryption() 38 | if err != nil { 39 | return false, err 40 | } 41 | return VerifyPassword(ep, password) 42 | } 43 | 44 | // VerifyEncryptedPassword is the fastest way to verify encrypted password stored in database 45 | func (auth Authorization) VerifyEncryptedPassword(password string) (bool, error) { 46 | return VerifyPassword(auth.Password, password) 47 | } 48 | -------------------------------------------------------------------------------- /jwt.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | // secretCodeJWT is the global variable for default secret code 4 | var secretCodeJWT = "54A3E4F19C28CCA4A27E5648871A6" 5 | 6 | // useAuthorization is the variable for enable and disable authorization 7 | var useAuthorization = true 8 | 9 | // expiredHoursTime is the hours time for duration JWT token 10 | var expiredHoursTime int64 = 1 11 | 12 | // expiredMinutesTime is the minutes time for duration JWT token 13 | var expiredMinutesTime int64 = 59 14 | 15 | // expiredSecondsTime is the seconds time for duration JWT token 16 | var expiredSecondsTime int64 = 60 17 | 18 | // signingHMACMethod is a method for signing and validation JWT 19 | var signingHMACMethod SigningHMAC = HS256() 20 | 21 | // GetStringJWTSecretCode is to get JWT secret code as a string 22 | func GetStringJWTSecretCode() string { 23 | return secretCodeJWT 24 | } 25 | 26 | // GetJWTSecretCode get JWT secret code as a byte 27 | func GetJWTSecretCode() []byte { 28 | return []byte(secretCodeJWT) 29 | } 30 | 31 | // SetJWTSecretCode is to set JWT secret code globally 32 | func SetJWTSecretCode(secretCode string) { 33 | secretCodeJWT = secretCode 34 | } 35 | 36 | // IsUseAuthorization is to get status whether use authorization or not 37 | func IsUseAuthorization() bool { 38 | return useAuthorization 39 | } 40 | 41 | // EnableAuthorization is to enable authorization 42 | func EnableAuthorization() { 43 | useAuthorization = true 44 | } 45 | 46 | // DisableAuthorization is to Disable the authorization 47 | func DisableAuthorization() { 48 | useAuthorization = false 49 | } 50 | 51 | // SetExpiredTime is to set expired time for JWT session 52 | func SetExpiredTime(hour int64, minute int64, second int64) { 53 | expiredHoursTime = hour 54 | expiredMinutesTime = minute 55 | expiredSecondsTime = second 56 | } 57 | 58 | // SetHMACSigningMethod is to set signing method globally 59 | func SetHMACSigningMethod(s SigningHMAC) { 60 | signingHMACMethod = s 61 | } 62 | 63 | // GetHMACSigningMethod is to get signing method 64 | func GetHMACSigningMethod() SigningHMAC { 65 | return signingHMACMethod 66 | } 67 | -------------------------------------------------------------------------------- /custom.go: -------------------------------------------------------------------------------- 1 | package jwt 2 | 3 | import ( 4 | "errors" 5 | "github.com/dgrijalva/jwt-go" 6 | "github.com/mitchellh/mapstructure" 7 | "golang.org/x/crypto/bcrypt" 8 | "time" 9 | ) 10 | 11 | // CustomClaims is the model provided by this library 12 | type CustomClaims struct { 13 | Object interface{} 14 | jwt.StandardClaims 15 | } 16 | 17 | // GenerateJWT is the function for generate JWT token 18 | func GenerateJWT(model interface{}) (s string, e error) { 19 | return GenerateJWTAndSetExpiredTime(model, expiredHoursTime, expiredMinutesTime, expiredSecondsTime) 20 | } 21 | 22 | // GenerateJWTAndSetExpiredTime will generate JWT token alongside with custom expired time 23 | func GenerateJWTAndSetExpiredTime(model interface{}, hours int64, minutes int64, seconds int64) (s string, e error) { 24 | if hours != 0 || minutes != 0 || seconds != 0 { 25 | r := CustomClaims{ 26 | Object: model, 27 | StandardClaims: jwt.StandardClaims{ 28 | ExpiresAt: time.Now().Local().Add(time.Hour*time.Duration(hours) + 29 | time.Minute*time.Duration(minutes) + 30 | time.Second*time.Duration(seconds)).Unix(), 31 | }, 32 | } 33 | t := jwt.NewWithClaims(GetHMACSigningMethod().Method, r) 34 | s, e = t.SignedString(GetJWTSecretCode()) 35 | } else { 36 | e = errors.New("expired time must be at least 1 second") 37 | } 38 | return s, e 39 | } 40 | 41 | // VerifyJWT is the function for verify any generated JWT token 42 | func VerifyJWT(token string) (bool, error) { 43 | return VerifyAndBindingJWT(nil, token) 44 | } 45 | 46 | // VerifyAndBindingJWT is the easy way to verify and binding JWT token at the same time 47 | func VerifyAndBindingJWT(model interface{}, token string) (bool, error) { 48 | isValid := !IsUseAuthorization() 49 | t, err := jwt.ParseWithClaims(token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) { 50 | return GetJWTSecretCode(), nil 51 | }) 52 | if t != nil { 53 | if claims, ok := t.Claims.(*CustomClaims); ok && t.Valid { 54 | if model != nil { 55 | err = mapstructure.Decode(claims.Object, &model) 56 | } 57 | isValid = true 58 | } 59 | } 60 | return isValid, err 61 | } 62 | 63 | // EncryptPassword is the function provided to encrypt any password safely 64 | func EncryptPassword(password string) ([]byte, error) { 65 | return bcrypt.GenerateFromPassword([]byte(password), 12) 66 | } 67 | 68 | // VerifyPassword is the function for verify between encryption password and requested password 69 | func VerifyPassword(encryptedPassword string, password string) (bool, error) { 70 | isValid := false 71 | err := bcrypt.CompareHashAndPassword([]byte(encryptedPassword), []byte(password)) 72 | if err == nil { 73 | isValid = true 74 | } 75 | return isValid, err 76 | } 77 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at supanadit@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= 5 | github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= 6 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 7 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 8 | github.com/gin-gonic/gin v1.6.2 h1:88crIK23zO6TqlQBt+f9FrPJNKm9ZEr7qjp9vl/d5TM= 9 | github.com/gin-gonic/gin v1.6.2/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= 10 | github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= 11 | github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 12 | github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= 13 | github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= 14 | github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no= 15 | github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= 16 | github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= 17 | github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= 18 | github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I= 19 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 20 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 21 | github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns= 22 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 23 | github.com/labstack/echo/v4 v4.1.16 h1:8swiwjE5Jkai3RPfZoahp8kjVCRNq+y7Q0hPji2Kz0o= 24 | github.com/labstack/echo/v4 v4.1.16/go.mod h1:awO+5TzAjvL8XpibdsfXxPgHr+orhtXZJZIQCVjogKI= 25 | github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= 26 | github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= 27 | github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y= 28 | github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= 29 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 30 | github.com/mattn/go-colorable v0.1.6 h1:6Su7aK7lXmJ/U79bYtBjLNaha4Fs1Rg9plHpcH+vvnE= 31 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 32 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 33 | github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= 34 | github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= 35 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 36 | github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4= 37 | github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 38 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= 39 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 40 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= 41 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 42 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 43 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 44 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 45 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 46 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 47 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 48 | github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= 49 | github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= 50 | github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs= 51 | github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= 52 | github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= 53 | github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= 54 | github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 55 | github.com/valyala/fasttemplate v1.1.0 h1:RZqt0yGBsps8NGvLSGW804QQqCUYYLsaOjTVHy1Ocw4= 56 | github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8= 57 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 58 | golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 59 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71 h1:DOmugCavvUtnUD114C1Wh+UgTgQZ4pMLzXxi1pSt+/Y= 60 | golang.org/x/crypto v0.0.0-20200406173513-056763e48d71/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 61 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 62 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b h1:0mm1VjtFUOIlE1SbDlwjYaDxZVDP2S5ou6y0gSgXHu8= 63 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 64 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 65 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 66 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 67 | golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 68 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= 69 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 70 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8= 71 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 72 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 73 | golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= 74 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 75 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 76 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 77 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 78 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 79 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 80 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JWT Go 2 | 3 | [![Build Status](https://travis-ci.org/supanadit/jwt-go.svg?branch=master)](https://travis-ci.org/supanadit/jwt-go) 4 | [![Go Report Card](https://goreportcard.com/badge/github.com/supanadit/jwt-go)](https://goreportcard.com/report/github.com/supanadit/jwt-go) 5 | [![GoDoc](https://godoc.org/github.com/supanadit/jwt-go?status.svg)](https://godoc.org/github.com/supanadit/jwt-go) 6 | 7 | The easiest JWT Library that could be a starting point for your project. 8 | 9 | ## Installation 10 | 11 | `go get github.com/supanadit/jwt-go` 12 | 13 | ## Quick Start 14 | 15 | ```go 16 | package main 17 | 18 | import ( 19 | "fmt" 20 | "github.com/supanadit/jwt-go" 21 | "log" 22 | ) 23 | 24 | func main() { 25 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 26 | jwt.SetJWTSecretCode("Your Secret Code") 27 | 28 | // Create default authorization 29 | auth := jwt.Authorization{ 30 | Username: "admin", 31 | Password: "admin", 32 | } 33 | 34 | // Generate JWT Token from default authorization model 35 | token, err := auth.GenerateJWT() 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | fmt.Println("JWT Token : " + token) 41 | 42 | // Verify the token 43 | valid, err := auth.VerifyJWT(token) 44 | if err != nil { 45 | fmt.Println(err) 46 | } 47 | 48 | fmt.Print("Status : ") 49 | 50 | if valid { 51 | fmt.Println("Valid") 52 | } else { 53 | fmt.Println("Invalid") 54 | } 55 | } 56 | ``` 57 | 58 |
Custom Authorization 59 | 60 |

61 | 62 | ```go 63 | package main 64 | 65 | import ( 66 | "fmt" 67 | "github.com/supanadit/jwt-go" 68 | "log" 69 | ) 70 | 71 | type Login struct { 72 | Email string 73 | Password string 74 | Name string 75 | } 76 | 77 | func main() { 78 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 79 | jwt.SetJWTSecretCode("Your Secret Code") 80 | 81 | // Create default authorization 82 | auth := Login{ 83 | Email: "asd@asd.com", 84 | Password: "asd", 85 | Name: "asd", 86 | } 87 | 88 | // Generate JWT Token from default authorization model 89 | token, err := jwt.GenerateJWT(auth) 90 | if err != nil { 91 | log.Fatal(err) 92 | } 93 | 94 | fmt.Println("JWT Token : " + token) 95 | 96 | // Variable for decoded JWT token 97 | var dataAuth Login 98 | // Verify the token 99 | valid, err := jwt.VerifyAndBindingJWT(&dataAuth, token) 100 | if err != nil { 101 | fmt.Println(err) 102 | } 103 | 104 | // or simply you can do this, if you don't need to decode the JWT 105 | // valid, err := jwt.VerifyJWT(token) 106 | // if err != nil { 107 | // fmt.Println(err) 108 | // } 109 | 110 | fmt.Print("Status : ") 111 | 112 | if valid { 113 | fmt.Println("Valid") 114 | } else { 115 | fmt.Println("Invalid") 116 | } 117 | } 118 | ``` 119 | 120 |

121 |
122 | 123 |
Encrypt & Verify Password 124 | 125 |

126 | 127 | ```go 128 | package main 129 | 130 | import ( 131 | "fmt" 132 | "github.com/supanadit/jwt-go" 133 | "log" 134 | ) 135 | 136 | type Login struct { 137 | Email string 138 | Password string 139 | } 140 | 141 | func main() { 142 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 143 | jwt.SetJWTSecretCode("Your Secret Code") 144 | 145 | // Create authorization from your own struct 146 | auth := Login{ 147 | Email: "example@email.com", 148 | Password: "123", 149 | } 150 | 151 | // Encrypt password, which you can save to database 152 | ep, err := jwt.EncryptPassword(auth.Password) 153 | if err != nil { 154 | log.Fatal(err) 155 | } 156 | 157 | fmt.Println("Encrypted Password " + string(ep)) 158 | 159 | // Verify Encrypted Password 160 | valid, err := jwt.VerifyPassword(string(ep), auth.Password) 161 | if err != nil { 162 | fmt.Println(err) 163 | } 164 | 165 | fmt.Print("Status : ") 166 | 167 | if valid { 168 | fmt.Println("Valid") 169 | } else { 170 | fmt.Println("Invalid") 171 | } 172 | } 173 | ``` 174 | 175 |

176 |
177 | 178 |
Decrypt Password 179 |

180 | 181 | No you can't, as the thread at [Stack Exchange](https://security.stackexchange.com/questions/193943/is-it-possible-to-decrypt-bcrypt-encryption) 182 | 183 | > bcrypt is not an encryption function, it's a password hashing function, relying on Blowfish's key scheduling, not its encryption. Hashing are mathematical one-way functions, meaning there is no way to reverse the output string to get the input string. 184 |
of course only Siths deal in absolutes and there are a few attacks against hashes. But none of them are "reversing" the hashing, AFAIK. 185 | 186 | so that enough to secure the password 187 | 188 |

189 |
190 | 191 |
Set Expired Time 192 |

193 | 194 | ```go 195 | package main 196 | 197 | import ( 198 | "fmt" 199 | "github.com/supanadit/jwt-go" 200 | "log" 201 | ) 202 | 203 | func main() { 204 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 205 | jwt.SetJWTSecretCode("Your Secret Code") 206 | 207 | // You can simply do this, jwt.setExpiredTime(Hour,Minute,Second) 208 | jwt.SetExpiredTime(0, 0, 1) 209 | } 210 | ``` 211 | 212 |

213 |
214 | 215 |
Support Gin Web Framework 216 |

217 | 218 | ```go 219 | package main 220 | 221 | import ( 222 | "github.com/gin-gonic/gin" 223 | "github.com/supanadit/jwt-go" 224 | "net/http" 225 | ) 226 | 227 | func main() { 228 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 229 | jwt.SetJWTSecretCode("Your Secret Code") 230 | 231 | // Create authorization 232 | auth := jwt.Authorization{ 233 | Username: "admin", 234 | Password: "123", 235 | } 236 | 237 | router := gin.Default() 238 | 239 | // Login / Authorization for create JWT Token 240 | router.POST("/auth", func(c *gin.Context) { 241 | var a jwt.Authorization 242 | err := c.Bind(&a) 243 | if err != nil { 244 | c.JSON(http.StatusBadRequest, gin.H{ 245 | "status": "Invalid body request", 246 | "token": nil, 247 | }) 248 | } else { 249 | valid, err := auth.VerifyPassword(a.Password) 250 | if err != nil { 251 | c.JSON(http.StatusBadRequest, gin.H{ 252 | "status": "Wrong username or password", 253 | "token": nil, 254 | }) 255 | } else { 256 | if valid { 257 | token, err := a.GenerateJWT() 258 | if err != nil { 259 | c.JSON(http.StatusInternalServerError, gin.H{ 260 | "status": "Can't generate JWT token", 261 | "token": nil, 262 | }) 263 | } else { 264 | c.JSON(http.StatusOK, gin.H{ 265 | "status": "Success", 266 | "token": token, 267 | }) 268 | } 269 | } else { 270 | c.JSON(http.StatusBadRequest, gin.H{ 271 | "status": "Wrong username or password", 272 | "token": nil, 273 | }) 274 | } 275 | } 276 | } 277 | }) 278 | 279 | // Test Authorization 280 | router.GET("/test", func(c *gin.Context) { 281 | // Variable for binding if you need decoded JWT 282 | var dataAuth jwt.Authorization 283 | // Verify and binding JWT 284 | token, valid, err := jwt.VerifyAndBindingGinHeader(&dataAuth, c) 285 | 286 | // in case if you don't want to decode the JWT, simply use this code 287 | // token, valid, err := jwt.VerifyGinHeader(c) 288 | 289 | if err != nil { 290 | c.JSON(http.StatusOK, gin.H{ 291 | "status": err.Error(), 292 | }) 293 | } else { 294 | if valid { 295 | c.JSON(http.StatusOK, gin.H{ 296 | "status": token + " is valid", 297 | }) 298 | } else { 299 | c.JSON(http.StatusBadRequest, gin.H{ 300 | "status": "Invalid", 301 | }) 302 | } 303 | } 304 | }) 305 | 306 | _ = router.Run(":8080") 307 | } 308 | ``` 309 | 310 |

311 |
312 | 313 |
Support Echo Web Framework 314 |

315 | 316 | ```go 317 | package main 318 | 319 | import ( 320 | "github.com/labstack/echo/v4" 321 | "github.com/supanadit/jwt-go" 322 | "net/http" 323 | ) 324 | 325 | func main() { 326 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 327 | jwt.SetJWTSecretCode("Your Secret Code") 328 | 329 | // Create authorization 330 | auth := jwt.Authorization{ 331 | Username: "admin", 332 | Password: "123", 333 | } 334 | 335 | e := echo.New() 336 | 337 | // Login / Authorization for create JWT Token 338 | e.POST("/auth", func(c echo.Context) error { 339 | a := new(jwt.Authorization) 340 | // Create struct for response, or you can create globally by your self 341 | var r struct { 342 | Status string 343 | Token string 344 | } 345 | err := c.Bind(a) 346 | if err != nil { 347 | r.Status = "Invalid body request" 348 | return c.JSON(http.StatusBadRequest, &r) 349 | } else { 350 | valid, err := auth.VerifyPassword(a.Password) 351 | if err != nil { 352 | r.Status = "Wrong username or password" 353 | return c.JSON(http.StatusBadRequest, &r) 354 | } else { 355 | if valid { 356 | token, err := a.GenerateJWT() 357 | if err != nil { 358 | r.Status = "Can't generate JWT Token" 359 | return c.JSON(http.StatusInternalServerError, &r) 360 | } else { 361 | r.Status = "Success" 362 | r.Token = token 363 | return c.JSON(http.StatusOK, &r) 364 | } 365 | } else { 366 | r.Status = "Wrong username or password" 367 | return c.JSON(http.StatusBadRequest, &r) 368 | } 369 | } 370 | } 371 | }) 372 | 373 | // Test Authorization 374 | e.GET("/test", func(c echo.Context) error { 375 | // Create struct for response 376 | var r struct { 377 | Status string 378 | } 379 | // Variable for binding if you need decoded JWT 380 | dataAuth := new(jwt.Authorization) 381 | // Verify and binding JWT 382 | token, valid, err := jwt.VerifyAndBindingEchoHeader(&dataAuth, c) 383 | 384 | // in case if you don't want to decode the JWT, simply use this code 385 | // Token, valid, err := jwt.VerifyEchoHeader(c) 386 | 387 | if err != nil { 388 | r.Status = err.Error() 389 | return c.JSON(http.StatusBadRequest, &r) 390 | } else { 391 | if valid { 392 | r.Status = token + " is valid" 393 | return c.JSON(http.StatusOK, &r) 394 | } else { 395 | r.Status = "Invalid" 396 | return c.JSON(http.StatusBadRequest, &r) 397 | } 398 | } 399 | }) 400 | 401 | // Start server 402 | e.Logger.Fatal(e.Start(":1323")) 403 | } 404 | ``` 405 | 406 |

407 |
408 | 409 |
Disable & Enable Authorization 410 |

411 | 412 | ```go 413 | package main 414 | 415 | import ( 416 | "github.com/supanadit/jwt-go" 417 | ) 418 | 419 | func main() { 420 | // Set Your JWT Secret Code, its optional but important, because default secret code is not secure 421 | jwt.SetJWTSecretCode("Your Secret Code") 422 | 423 | // Disable authorization, meaning when verify jwt token it will return true even if the token was expired or invalid 424 | jwt.DisableAuthorization() 425 | 426 | // or 427 | 428 | // Enable authorization 429 | jwt.EnableAuthorization() 430 | } 431 | ``` 432 | 433 |

434 |
435 | 436 |
Set HMAC Signing Method 437 |

438 | 439 | ```go 440 | package main 441 | 442 | import "github.com/supanadit/jwt-go" 443 | 444 | func main() { 445 | // Set HMAC signing method 446 | jwt.SetHMACSigningMethod(jwt.HS256()) // or jwt.HS384(), jwt.HS512() 447 | } 448 | ``` 449 | 450 |

451 |
452 | 453 | ## Thanks to 454 | - [jwt-go](https://github.com/dgrijalva/jwt-go) 455 | - [bcrypt](golang.org/x/crypto/bcrypt) 456 | - [mapstructure](github.com/mitchellh/mapstructure) 457 | --------------------------------------------------------------------------------