Implemented jwt generation

This commit is contained in:
ErickSkrauch 2018-01-15 23:52:22 +03:00
parent d2485df64d
commit ca4479252f
No known key found for this signature in database
GPG Key ID: 669339FCBB30EE0E
5 changed files with 233 additions and 2 deletions

34
Gopkg.lock generated
View File

@ -1,6 +1,12 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/SermoDigital/jose"
packages = [".","crypto","jws","jwt"]
revision = "f6df55f235c24f236d11dbcf665249a59ac2021f"
version = "1.1"
[[projects]]
name = "github.com/assembla/cony"
packages = ["."]
@ -55,6 +61,12 @@
packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"]
revision = "8f6b1344a92ff8877cf24a5de9177bf7d0a2a187"
[[projects]]
branch = "master"
name = "github.com/howeyc/gopass"
packages = ["."]
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
[[projects]]
name = "github.com/inconshreveable/mousetrap"
packages = ["."]
@ -73,6 +85,12 @@
packages = ["cluster","pool","redis","util"]
revision = "d234cfb904a91daafa4e1f92599a893b349cc0c2"
[[projects]]
branch = "master"
name = "github.com/mitchellh/go-homedir"
packages = ["."]
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
[[projects]]
branch = "master"
name = "github.com/mitchellh/mapstructure"
@ -109,6 +127,12 @@
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/segmentio/go-prompt"
packages = ["."]
revision = "f0d19b6901ade831d5a3204edc0d6a7d6457fbb2"
[[projects]]
branch = "master"
name = "github.com/spf13/afero"
@ -157,10 +181,16 @@
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
version = "v1.1.4"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ssh/terminal"]
revision = "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
packages = ["unix","windows"]
revision = "7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce"
[[projects]]
@ -184,6 +214,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "11938f85225b2839e4ed7cd4345bed8f44510b6eb50c003b89c8e14e0fd6b6e7"
inputs-digest = "a12e681ec671ce8a93256cd754d4e70797476b2d2ce4379c3860df09c4b6a552"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -23,6 +23,17 @@ ignored = ["elyby/minecraft-skinsystem"]
name = "github.com/assembla/cony"
version = "^0.3.2"
[[constraint]]
name = "github.com/SermoDigital/jose"
version = "~1.1.0"
[[constraint]]
name = "github.com/mitchellh/go-homedir"
[[constraint]]
name = "github.com/segmentio/go-prompt"
branch = "master"
# Testing dependencies
[[constraint]]

124
auth/jwt.go Normal file
View File

@ -0,0 +1,124 @@
package auth
import (
"encoding/base64"
"io/ioutil"
"math"
"math/rand"
"os"
"time"
"github.com/SermoDigital/jose/crypto"
"github.com/SermoDigital/jose/jws"
"github.com/mitchellh/go-homedir"
)
var hashAlg = crypto.SigningMethodHS256
const appHomeDirName = ".minecraft-skinsystem"
const scopesClaim = "scopes"
type Scope string
var (
SkinScope = Scope("skin")
)
type JwtAuth struct {
signingKey []byte
}
func (t *JwtAuth) NewToken(scopes ...Scope) ([]byte, error) {
key, err := t.getSigningKey()
if err != nil {
return nil, err
}
claims := jws.Claims{}
claims.Set(scopesClaim, scopes)
claims.SetIssuedAt(time.Now())
encoder := jws.NewJWT(claims, hashAlg)
token, err := encoder.Serialize(key)
if err != nil {
return nil, err
}
return token, nil
}
func (t *JwtAuth) GenerateSigningKey() error {
if err := createAppHomeDir(); err != nil {
return err
}
key := generateRandomBytes(64)
if err := ioutil.WriteFile(getKeyPath(), key, 0600); err != nil {
return err
}
return nil
}
func (t *JwtAuth) getSigningKey() ([]byte, error) {
if t.signingKey != nil {
return t.signingKey, nil
}
path := getKeyPath()
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return nil, &SigningKeyNotAvailable{}
}
return nil, err
}
key, err := ioutil.ReadFile(path)
if err != nil {
return nil, err
}
return key, nil
}
func createAppHomeDir() error {
path := getAppHomeDirPath()
if _, err := os.Stat(path); os.IsNotExist(err) {
err := os.Mkdir(path, 0755) // rwx r-x r-x
if err != nil {
return err
}
}
return nil
}
func getAppHomeDirPath() string {
path, err := homedir.Expand("~/" + appHomeDirName)
if err != nil {
panic(err)
}
return path
}
func getKeyPath() string {
return getAppHomeDirPath() + "/jwt-key"
}
func generateRandomBytes(n int) []byte {
randLen := int(math.Ceil(float64(n) / 1.37)) // base64 will increase length in 1.37 times
randBytes := make([]byte, randLen)
rand.Read(randBytes)
resBytes := make([]byte, n)
base64.URLEncoding.Encode(resBytes, randBytes)
return resBytes
}
type SigningKeyNotAvailable struct {
}
func (*SigningKeyNotAvailable) Error() string {
return "Signing key not available"
}

View File

@ -41,6 +41,7 @@ func initConfig() {
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err == nil {
// TODO: show only on verbose mode
fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}

65
cmd/token.go Normal file
View File

@ -0,0 +1,65 @@
package cmd
import (
"fmt"
"log"
"elyby/minecraft-skinsystem/auth"
"github.com/segmentio/go-prompt"
"github.com/spf13/cobra"
)
var tokenCmd = &cobra.Command{
Use: "token",
Short: "API tokens operations",
}
var createCmd = &cobra.Command{
Use: "create",
Short: "Create the new token, that allows interacting with Ely.by Skinsystem API",
Run: func(cmd *cobra.Command, args []string) {
jwtAuth := &auth.JwtAuth{}
for {
token, err := jwtAuth.NewToken(auth.SkinScope)
if err != nil {
if _, ok := err.(*auth.SigningKeyNotAvailable); !ok {
log.Fatalf("Unable to create new token. The error is %v\n", err)
}
log.Println("Signing key not available. Creating...")
err := jwtAuth.GenerateSigningKey()
if err != nil {
log.Fatalf("Unable to generate new signing key. The error is %v\n", err)
}
continue
}
fmt.Printf("%s\n", token)
}
},
}
var resetCmd = &cobra.Command{
Use: "reset",
Short: "Regenerate the secret key, that invalidate all tokens",
Run: func(cmd *cobra.Command, args []string) {
if !prompt.Confirm("Do you really want to invalidate all exists tokens?") {
fmt.Println("Aboart.")
return
}
jwtAuth := &auth.JwtAuth{}
if err := jwtAuth.GenerateSigningKey(); err != nil {
log.Fatalf("Unable to generate new signing key. The error is %v\n", err)
}
fmt.Println("Token successfully regenerated.")
},
}
func init() {
tokenCmd.AddCommand(createCmd, resetCmd)
RootCmd.AddCommand(tokenCmd)
}