jwtis

package module
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2020 License: MIT Imports: 12 Imported by: 0

README

JWTIS - JWT issuer server

GoDoc

Stand alone JWT issuer server. Just creates and renews JSON Web Tokens.

Used gopkg.in/square/go-jose.v2 library.
NOT github.com/dgrijalva/jwt-go.
But produced JSON Web Tokens are standard and may be verified and decrypted with any other library.
Client server (app) may use github.com/dgrijalva/jwt-go to verify tokens.

Installation

To install JWTIS, you need to install Go and set your Go workspace first.

  1. Download and install it:
$ go get -u -d github.com/karantin2020/jwtis/cmd
$ go install -o jwtis github.com/karantin2020/jwtis/cmd
  1. Import as library:
import "github.com/karantin2020/jwtis"
Server configuration
./jwtis -h

Usage: jwtis [OPTIONS]

JWT issuer server. Provides trusted JWT tokens

Source https://github.com/karantin2020/jwtis

Options:
  -V, --version      Show the version and exit
  -l, --listen       ip:port to listen to (env $JWTIS_ADDRESS) (default "127.0.0.1:4343")
      --tls          Use tls connection [not implemented yet] (env $JWTIS_TLS)
      --sigAlg       Default algorithn to be used for sign. Possible values are: ES256 ES384 ES512 EdDSA RS256 RS384 RS512 PS256 PS384 PS512 (env $JWTIS_SIG_ALG) (default "ES256")
      --sigBits      Default key size in bits for sign key. Supported elliptic bit lengths are 256, 384, 521 (env $JWTIS_SIG_BITS) (default 256)
      --encAlg       Default algorithn to be used for encrypt. Possible values are RSA1_5 RSA-OAEP RSA-OAEP-256 ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW (env $JWTIS_ENC_ALG) (default "ECDH-ES+A256KW")
      --encBits      Default key size in bits for encrypt. Supported elliptic bit lengths are 256, 384, 521 (env $JWTIS_ENC_BITS) (default 256)
      --contEnc      Default content encryption. Possible values are A128GCM, A192GCM, A256GCM (env $JWTIS_CONT_ENC) (default "A256GCM")
  -e, --expiry       Default keys time to live, expiration time [Duration string] (env $JWTIS_EXPIRY) (default "4320h")
  -a, --authTTL      Default auth JWT token time to live, expiration time [Duration string] (env $JWTIS_AUTH_TTL) (default "72h")
  -r, --refreshTTL   Default refresh JWT token time to live, expiration time [Duration string] (env $JWTIS_REFRESH_TTL) (default "720h")
  -n, --name         Name of this service (env $JWTIS_NAME) (default "JWTIS")
  -p, --pswd         Storage password. App generates password with db creation. Later user must provide a password to access the database (env $JWTIS_PSWD)
  -d, --dbPath       Path to store keys db (env $JWTIS_DB_PATH) (default "./data/keys.db")
  -v, --verbose      Verbose. Show detailed logs (env $JWTIS_VERBOSE)
Start server
./jwtis
Welcome. Started jwtis version v0.1.1
Created new bbolt database to store app's data
Generated new password: '?@P$mllo2H}W&Y[EAFZiv4.LoCi3-8L?'
Please save the password safely, it's not recoverable
Current configuration:
  internalRepo.configs.listen:          127.0.0.1:4343
  internalRepo.configs.tls:             false
  internalRepo.configs.sigAlg:          ES256
  internalRepo.configs.sigBits:         256
  internalRepo.configs.encAlg:          ECDH-ES+A256KW
  internalRepo.configs.encBits:         256
  internalRepo.configs.contEnc:         A256GCM
  internalRepo.configs.selfName:        JWTIS
  internalRepo.configs.expiry:          4320h0m0s
  internalRepo.configs.authTTL:         72h0m0s
  internalRepo.configs.refreshTTL:      720h0m0s
  confRepo.options.dbPath:              ./data/keys.db
jwtis works well
jwtis finished work

./jwtis -p '?@P$mllo2H}W&Y[EAFZiv4.LoCi3-8L?'
Welcome. Started jwtis version v0.1.1
Found existing bbolt database storing app's data
Use user inserted password to bboltDB
Current configuration:
  internalRepo.configs.listen:          127.0.0.1:4343
  internalRepo.configs.tls:             false
  internalRepo.configs.sigAlg:          ES256
  internalRepo.configs.sigBits:         256
  internalRepo.configs.encAlg:          ECDH-ES+A256KW
  internalRepo.configs.encBits:         256
  internalRepo.configs.contEnc:         A256GCM
  internalRepo.configs.selfName:        JWTIS
  internalRepo.configs.expiry:          4320h0m0s
  internalRepo.configs.authTTL:         72h0m0s
  internalRepo.configs.refreshTTL:      720h0m0s
  confRepo.options.dbPath:              ./data/keys.db
jwtis works well
When started first time JWTIS will:
  • generate new boltdb password
    • You must save this password, it's not recoverable
  • create boltdb database file
  • start server with default or user provided configuration
Terms
  • jwtis - is this server
  • client server (or app, business logic layer, domain logic layer) - the server which requests tokens from jwtis
  • web client (user) - the end consumer, client, user which requests client server
  • auth token and refresh token are described later in README
What is it for:
  • automate sign and encrypt key creation, storing and revision
  • automate producing JWTs (no need to implement it yourself)
  • providing two tokens: short lived auth token and long lived refresh token
  • signed and encrypted refresh token provides more security
  • signed auth token may be verified with it's signature and with matching refresh token
  • client server fetches tokens' set for certain web client (user) and authenticates user with that tokens
  • client server (app) never encrypts anything
  • many client servers may use one JWTIS instance
  • all info on JWTIS is stored encrypted in embedded database
Short-lived (minutes) JWT Auth Token

The short-lived jwt auth token allows the user to make stateless requests to protected api endpoints. It has an expiration time of 15 minutes by default and will be refreshed by the longer-lived refresh token.

Longer-lived (hours/days) JWT Refresh Token

This longer-lived token will be used to update the auth tokens. These tokens have a 72 hour expiration time by default which will be updated each time an auth token is refreshed.
Refresh tokens are signed and encrypted
These refresh tokens can be revoked by an authorized client

JWTIS flow implementation
  • client server (business logic layer, domain logic layer) requests JWT
  • JWTIS generates tokens and sends them to client server
  • client server authenticates web client with this tokens as ussually
Sign algorithms
  ES256 ES384 ES512 EdDSA RS256 RS384 RS512 PS256 PS384 PS512
Encrypt algorithms
  RSA1_5 RSA-OAEP RSA-OAEP-256 ECDH-ES ECDH-ES+A128KW ECDH-ES+A192KW ECDH-ES+A256KW
JWTIS http endpoints
  • /register/:kid - register new client server (app)

    • method
        POST
    
    • payload
      // RegisterClientRequest sent to jwtis to register new client
      type RegisterClientRequest struct {
        Expiry Duration `json:"expiry,omitempty"` // keys ttl, optional
    
        SigAlg  string `json:"sig_alg,omitempty"`  // default algorithn to be used for sign, optional
        SigBits int    `json:"sig_bits,omitempty"` // default key size in bits for sign, optional
        EncAlg  string `json:"enc_alg,omitempty"`  // default algorithn to be used for encrypt, optional
        EncBits int    `json:"enc_bits,omitempty"` // default key size in bits for encrypt, optional
    
        AuthTTL    Duration `json:"auth_ttl,omitempty"`    // default auth jwt ttl, optional
        RefreshTTL Duration `json:"refresh_ttl,omitempty"` // default refresh jwt ttl, optional
    
        // RefreshStrategy is used in RenewJWT to decide wheather to issue new refresh token
        // with access token or not
        // this option applies to all renewJWT requests
        RefreshStrategy string `json:"refresh_strategy,omitempty"` // optional, values are: 'refreshBoth', 'refreshOnExpire', 'noRefresh' (default)
      }
    
    • response
      // RegisterClientResponse sent to client after it's registration
      type RegisterClientResponse struct {
        Kid         string          `json:"kid,omitempty"`          // Keys id to use
        ClientToken string          `json:"client_token,omitempty"` // Client token given after registration [reserved]
        PubSigKey   jose.JSONWebKey `json:"pub_sig_key,omitempty"`  // Public sign key to verify AccessTokens
        PubEncKey   jose.JSONWebKey `json:"pub_enc_key,omitempty"`  // Public enc key to decrypt RefreshTokens
        Expiry      jwt.NumericDate `json:"expiry,omitempty"`
        Valid       bool            `json:"valid,omitempty"`
      }
    
  • /keys/:kid - fetch client server (app) public keys [TODO]

    • request for previously generated public keys for JWT
    • request for new pub and priv JSONWebKey, not for jwt issuing
    • and other functions
    • requests send parameters in url queue or req body
  • /renew_keys - request new client server (app) keys [TODO]

    • method
  • /issue_token - request new token set for client server with kid

    • method
    POST
    
    • payload
    // NewTokenRequest sent to jwtis to fetch new jwt
    // ClientToken {string} - must be in header
    type NewTokenRequest struct {
      Kid                   string                 `json:"kid"` // Keys id to use
      AuthTokenValidTime    time.Duration          `json:"auth_token_valid_time,omitempty"`
      ResreshTokenValidTime time.Duration          `json:"resresh_token_valid_time,omitempty"`
      Claims                map[string]interface{} `json:"claims,omitempty"` // Custom claims
    }
    
    • response
    // TokenResponse sent to client that requested tokens
    type TokenResponse struct {
      ID           string          `json:"id"`
      AuthToken    string          `json:"auth_token"`    // Short lived auth token
      RefreshToken string          `json:"refresh_token"` // Long lived refresh token
      Expiry       jwt.NumericDate `json:"expiry"`
    }
    
  • /renew_token - renew auth token for client server based on refresh token. Only if refresh token is valid. Otherwise need to request new token

    • method
    POST
    
    • payload
      // RenewTokenRequest sent to jwtis to fetch new jwt
      // ClientToken {string} - must be in header
      type RenewTokenRequest struct {
        Kid string `json:"kid"` // Keys id to use
        // AccessTokenValidTime  Duration `json:"access_token_valid_time,omitempty"`
        // RefreshTokenValidTime Duration `json:"refresh_token_valid_time,omitempty"`
        RefreshToken string `json:"refresh_token"`
        // RefreshStrategy is used in RenewJWT to decide wheather to issue new refresh token
        // with access token or not
        // this option applies only to a specific request
        RefreshStrategy string `json:"refresh_strategy,omitempty"` // optional, values are: 'refreshBoth', 'refreshOnExpire', 'noRefresh' (default)
      }
    
    • response
      // TokenResponse sent to client that requested tokens
      type TokenResponse struct {
        ID           string          `json:"id"`
        AccessToken  string          `json:"access_token"`  // Short lived auth token
        RefreshToken string          `json:"refresh_token"` // Long lived refresh token
        Expiry       jwt.NumericDate `json:"expiry"`
      }
    

Contributions are welcome

For note: JWT Structure
  1. "iss" (Issuer) Claim

    The "iss" (issuer) claim identifies the principal that issued the JWT. The processing of this claim is generally application specific. The "iss" value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.

  2. "sub" (Subject) Claim

    The "sub" (subject) claim identifies the principal that is the subject of the JWT. The claims in a JWT are normally statements about the subject. The subject value MUST either be scoped to be locally unique in the context of the issuer or be globally unique. The processing of this claim is generally application specific. The "sub" value is a case-sensitive string containing a StringOrURI value. Use of this claim is OPTIONAL.

  3. "aud" (Audience) Claim

    The "aud" (audience) claim identifies the recipients that the JWT is intended for. Each principal intended to process the JWT MUST identify itself with a value in the audience claim. If the principal processing the claim does not identify itself with a value in the "aud" claim when this claim is present, then the JWT MUST be rejected. In the general case, the "aud" value is an array of case- sensitive strings, each containing a StringOrURI value. In the special case when the JWT has one audience, the "aud" value MAY be a single case-sensitive string containing a StringOrURI value. The interpretation of audience values is generally application specific. Use of this claim is OPTIONAL.

  4. "exp" (Expiration Time) Claim

    The "exp" (expiration time) claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing. The processing of the "exp" claim requires that the current date/time MUST be before the expiration date/time listed in the "exp" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

  5. "nbf" (Not Before) Claim

    The "nbf" (not before) claim identifies the time before which the JWT MUST NOT be accepted for processing. The processing of the "nbf" claim requires that the current date/time MUST be after or equal to the not-before date/time listed in the "nbf" claim. Implementers MAY provide for some small leeway, usually no more than a few minutes, to account for clock skew. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

  6. "iat" (Issued At) Claim

    The "iat" (issued at) claim identifies the time at which the JWT was issued. This claim can be used to determine the age of the JWT. Its value MUST be a number containing a NumericDate value. Use of this claim is OPTIONAL.

  7. "jti" (JWT ID) Claim

    The "jti" (JWT ID) claim provides a unique identifier for the JWT. The identifier value MUST be assigned in a manner that ensures that there is a negligible probability that the same value will be accidentally assigned to a different data object; if the application uses multiple issuers, collisions MUST be prevented among values produced by different issuers as well. The "jti" claim can be used to prevent the JWT from being replayed. The "jti" value is a case- sensitive string. Use of this claim is OPTIONAL.

Source is https://tools.ietf.org/html/rfc7519#section-4.1

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// AlphaNum contains runes [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789].
	AlphaNum = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	// Alpha contains runes [abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ].
	Alpha = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
	// AlphaLowerNum contains runes [abcdefghijklmnopqrstuvwxyz0123456789].
	AlphaLowerNum = []rune("abcdefghijklmnopqrstuvwxyz0123456789")
	// AlphaUpperNum contains runes [ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789].
	AlphaUpperNum = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	// AlphaLower contains runes [abcdefghijklmnopqrstuvwxyz].
	AlphaLower = []rune("abcdefghijklmnopqrstuvwxyz")
	// AlphaUpper contains runes [ABCDEFGHIJKLMNOPQRSTUVWXYZ].
	AlphaUpper = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
	// Numeric contains runes [0123456789].
	Numeric = []rune("0123456789")
)
View Source
var (
	// SigAlgs is array of possible sig algorithms
	SigAlgs = []string{
		string(jose.ES256), string(jose.ES384), string(jose.ES512),
		string(jose.EdDSA), string(jose.RS256), string(jose.RS384),
		string(jose.RS512), string(jose.PS256), string(jose.PS384),
		string(jose.PS512),
	}
	// EncAlgs is array of possible enc algorithms
	EncAlgs = []string{
		string(jose.RSA1_5), string(jose.RSA_OAEP),
		string(jose.RSA_OAEP_256), string(jose.ECDH_ES),
		string(jose.ECDH_ES_A128KW), string(jose.ECDH_ES_A192KW),
		string(jose.ECDH_ES_A256KW),
	}
	// Use is array of possible use values
	Use = []string{
		"enc", "sig",
	}
)
View Source
var (
	// ErrKeyNotFound describes error when looking key is not found in db
	ErrKeyNotFound = errors.New("requested key was not found in db")
)

Functions

func ClaimsSigned

func ClaimsSigned(sigkey *jose.JSONWebKey,
	raw string, dest ...interface{}) error

ClaimsSigned takes claims from signed string jwt

func ClaimsSignedAndEncrypted

func ClaimsSignedAndEncrypted(enckey *jose.JSONWebKey, sigkey *jose.JSONWebKey,
	raw string, dest ...interface{}) error

ClaimsSignedAndEncrypted takes claims from encrypted and signed string jwt

func GenerateKeys

func GenerateKeys(kid string, opt KeyOptions) (jose.JSONWebKey, jose.JSONWebKey, error)

GenerateKeys generates enc, sig key

func GenerateSecret

func GenerateSecret(length int) ([]byte, error)

GenerateSecret gererates a secret including chars from secret char set

func JWTSigned

func JWTSigned(sigkey *jose.JSONWebKey, claims ...interface{}) (string, error)

JWTSigned signes claims, returns jwt token string and error

func JWTSignedAndEncrypted

func JWTSignedAndEncrypted(contEnc jose.ContentEncryption, enckey *jose.JSONWebKey, sigkey *jose.JSONWebKey,
	claims ...interface{}) (string, error)

JWTSignedAndEncrypted ecryptes and signes claims, returns jwt token string and error

func KeygenEnc

func KeygenEnc(alg jose.KeyAlgorithm, bits int) (crypto.PublicKey, crypto.PrivateKey, error)

KeygenEnc generates keypair for corresponding KeyAlgorithm.

func KeygenSig

KeygenSig generates keypair for corresponding SignatureAlgorithm.

func RuneSequence

func RuneSequence(l int, allowedRunes []rune) (seq []rune, err error)

RuneSequence returns a random sequence using the defined allowed runes.

Types

type Error

type Error []error

Error implements multierror type

func (*Error) Append

func (mr *Error) Append(errs ...error) Error

Append appends errors to array if err != nil

func (Error) Error

func (mr Error) Error() string

Error implements error interface

type KeyOptions

type KeyOptions struct {
	Use, Alg string
	Bits     int
}

KeyOptions represent the set of option to create sig or enc keys

type PrivPubKeySet

type PrivPubKeySet struct {
	Priv jose.JSONWebKey
	Pub  jose.JSONWebKey
}

PrivPubKeySet holds private and public keys

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL