mirror of
https://github.com/elyby/chrly.git
synced 2024-11-16 18:22:58 +05:30
Implemented jwt generation
This commit is contained in:
parent
d2485df64d
commit
ca4479252f
34
Gopkg.lock
generated
34
Gopkg.lock
generated
@ -1,6 +1,12 @@
|
|||||||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
# 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]]
|
[[projects]]
|
||||||
name = "github.com/assembla/cony"
|
name = "github.com/assembla/cony"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@ -55,6 +61,12 @@
|
|||||||
packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"]
|
packages = [".","hcl/ast","hcl/parser","hcl/scanner","hcl/strconv","hcl/token","json/parser","json/scanner","json/token"]
|
||||||
revision = "8f6b1344a92ff8877cf24a5de9177bf7d0a2a187"
|
revision = "8f6b1344a92ff8877cf24a5de9177bf7d0a2a187"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/howeyc/gopass"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "bf9dde6d0d2c004a008c27aaee91170c786f6db8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
name = "github.com/inconshreveable/mousetrap"
|
name = "github.com/inconshreveable/mousetrap"
|
||||||
packages = ["."]
|
packages = ["."]
|
||||||
@ -73,6 +85,12 @@
|
|||||||
packages = ["cluster","pool","redis","util"]
|
packages = ["cluster","pool","redis","util"]
|
||||||
revision = "d234cfb904a91daafa4e1f92599a893b349cc0c2"
|
revision = "d234cfb904a91daafa4e1f92599a893b349cc0c2"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/mitchellh/go-homedir"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "b8bc1bf767474819792c23f32d8286a45736f1c6"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/mitchellh/mapstructure"
|
name = "github.com/mitchellh/mapstructure"
|
||||||
@ -109,6 +127,12 @@
|
|||||||
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
revision = "792786c7400a136282c1664665ae0a8db921c6c2"
|
||||||
version = "v1.0.0"
|
version = "v1.0.0"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "github.com/segmentio/go-prompt"
|
||||||
|
packages = ["."]
|
||||||
|
revision = "f0d19b6901ade831d5a3204edc0d6a7d6457fbb2"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "github.com/spf13/afero"
|
name = "github.com/spf13/afero"
|
||||||
@ -157,10 +181,16 @@
|
|||||||
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
revision = "69483b4bd14f5845b5a1e55bca19e954e827f1d0"
|
||||||
version = "v1.1.4"
|
version = "v1.1.4"
|
||||||
|
|
||||||
|
[[projects]]
|
||||||
|
branch = "master"
|
||||||
|
name = "golang.org/x/crypto"
|
||||||
|
packages = ["ssh/terminal"]
|
||||||
|
revision = "0fcca4842a8d74bfddc2c96a073bd2a4d2a7a2e8"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = ["unix"]
|
packages = ["unix","windows"]
|
||||||
revision = "7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce"
|
revision = "7ddbeae9ae08c6a06a59597f0c9edbc5ff2444ce"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
@ -184,6 +214,6 @@
|
|||||||
[solve-meta]
|
[solve-meta]
|
||||||
analyzer-name = "dep"
|
analyzer-name = "dep"
|
||||||
analyzer-version = 1
|
analyzer-version = 1
|
||||||
inputs-digest = "11938f85225b2839e4ed7cd4345bed8f44510b6eb50c003b89c8e14e0fd6b6e7"
|
inputs-digest = "a12e681ec671ce8a93256cd754d4e70797476b2d2ce4379c3860df09c4b6a552"
|
||||||
solver-name = "gps-cdcl"
|
solver-name = "gps-cdcl"
|
||||||
solver-version = 1
|
solver-version = 1
|
||||||
|
11
Gopkg.toml
11
Gopkg.toml
@ -23,6 +23,17 @@ ignored = ["elyby/minecraft-skinsystem"]
|
|||||||
name = "github.com/assembla/cony"
|
name = "github.com/assembla/cony"
|
||||||
version = "^0.3.2"
|
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
|
# Testing dependencies
|
||||||
|
|
||||||
[[constraint]]
|
[[constraint]]
|
||||||
|
124
auth/jwt.go
Normal file
124
auth/jwt.go
Normal 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"
|
||||||
|
}
|
@ -41,6 +41,7 @@ func initConfig() {
|
|||||||
viper.AutomaticEnv()
|
viper.AutomaticEnv()
|
||||||
|
|
||||||
if err := viper.ReadInConfig(); err == nil {
|
if err := viper.ReadInConfig(); err == nil {
|
||||||
|
// TODO: show only on verbose mode
|
||||||
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
fmt.Println("Using config file:", viper.ConfigFileUsed())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
65
cmd/token.go
Normal file
65
cmd/token.go
Normal 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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user