# README

JWT Security

This package contains functions and types for enabling JWT as a security mechanism in a microservice. It integrates with Goa generated JWT security and also as part of the SecurityChain.

Setting up a JWT security

There are a couple of things you need to do to enable the JWT security middleware. For details on JWT you can find many resources on the official site: http://jwt.io.

JWT has two main parts: issuing JWT tokens (with claims) and verifying the tokens. This package provides means of validating a JWT token with claims by using secret keys (RSA key pairs).

Setting up the secret keys

Create a directory in which you'll keep your key-pair:

mkdir rsa-keys
cd rsa-keys

The generate 2048-bit RSA key pair:

openssl genrsa -des3 -out private.pem 2048

The output the keys in PEM form:

openssl rsa -in private.pem -outform PEM -pubout -out public.pub

NOTE: Make sure that the public key ends in .pub. This is how the default key resolver of the library locates the public keys.

Set up JWT with Goagen

To use the JWT security you need to set up JWT security in your microservice Goa DSL.

In your design file, add the JWT setup:

var JWTAuth = JWTSecurity("JWT", func() {
	Header("Authorization")
	Scope("api:read", "Read access to the API")
	Scope("api:write", "Write access to the API")
})

var _ = Resource("user", func() {
	BasePath("/user")

	// Other specification here ...

  Security(JWTAuth) // Add the JWTAuth to this resource
})

By running goagen ... on the deisgn again, you'll notice that Goa will add a security file app/security.go.

More details on how to configure the JWT security are available on the official documentation:

The most important thing there is the security specification scheme - the following function that builds JWTSecurity:

// NewJWTSecurity creates a JWT security definition.
func NewJWTSecurity() *goa.JWTSecurity {
	def := goa.JWTSecurity{
		In:       goa.LocHeader,
		Name:     "Authorization",
		TokenURL: "",
		Scopes: map[string]string{
			"api:read":  "Read access to the API",
			"api:write": "Write access to the API",
		},
	}
	return &def
}

We'll use this in the next step.

Setting up a SecurityChain

Once the security specs are generated by Goa, you need to set up a security chain for the microservice.

In the main.go file of your microservice, set up the JWT Security Chain middleware and add it to the security chain.

import (
  // other imports
  "githib.com/Microkubes/microservice-security/auth"
  "githib.com/Microkubes/microservice-security/chain"
  "githib.com/Microkubes/microservice-security/jwt"
)
func main(){
  // "secret-keys" is the directory containing the RSA keys
  // app.NewJWTSecurity() creates the JWTSecurity scheme structure
  JWTMiddleware := jwt.NewJWTSecurity("secret-keys", app.NewJWTSecurity())


  sc := chain.NewSecurityChain(). // create new chain
    AddMiddleware(JWTMiddleware). // Add the JWT middleware
    AddMiddleware(chain.CheckAuthMiddleware) // add the check for authentication

  // other initializations here...

  service.Use(chain.ToGoaMiddleware(sc)) // attach the security chain as Goa middleware

}

Testing the setup

To test the setup, you'll need to generate and sign a JWT token, then use it in the request header.

To generate a JWT, you can use different tools. In this example we'll use jwtgen command line tool. To install it:

npm install -g jwtgen

Then, to generate the JWT token, type:

jwtgen -a RS256\
       -p secret-keys/private.pem\
       -c "userId=9479e3b6-a4c5-445c-aefe-c84b5a47ed3c" \
       -c "username=test-user"\
       -c "roles=user,admin"\
       -c "organizations=Org1,Org2"

which will output the JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE1MDIyOTA3NDYsInVzZXJJZCI6Ijk0Nzl
lM2I2LWE0YzUtNDQ1Yy1hZWZlLWM4NGI1YTQ3ZWQzYyIsInVzZXJuYW1lIjoidGVzdC11c2VyIiwicm9
sZXMiOiJ1c2VyLGFkbWluIiwib3JnYW5pemF0aW9ucyI6Ik9yZzEsT3JnMiJ9.J2dFSUChwAyy3g92qj
6ggT-EA_B-kv16cjyEQS0xBTHl6Sw3vhsaooRTnFDkXKVeGqnctyjRLGmRxpQCYZ5FVnJocSCKw99Q1k
mr-HbyoLjjpppYOSC_LOyPPqmUe1M9ze5GOP_QY9H5V6g6Otar_Ge4tXoR9P7_px7KtHJ2jINeX_amxW
MnujN5fqsrI80wNSGcGTKwU2FSJynQoS_7zziHmCK0h1m-XV_wjaMu4h-_nHsK5pNAF1R7uWx20aAsg4
38wVjwdMmgDhRJOlctLwhFlthAr_GXYKDH-ztKGz6KNXRfUK8--ad4TaNgCDzbjVkHrgMLS-RatUXXoW
4pfw

(Note that the JWT is actually one line. For readability purposes it is displayed here in multiple lines.)

Then you'll need to add the token in the Authorization HTTP header of the request.

curl --header "Authorization: Bearer eyJ0eXAiOiJKV1Q...<full token here>...4pfw" "http://localhost:8080/user/1"

# Functions

LoadJWTPublicKeys loads PEM encoded RSA public keys used to validate and decrypt the JWT.
NewJWTSecurity creates a JWT SecurityChainMiddleware using a simple key resolver that loads the public keys from the keysDir.
NewJWTSecurityMiddleware creates a new chain.SecurityChainMiddleware with a given KeyResolver and JWTSecurity configuration.
NewKeyResolver creates a simple key resolver for the JWT middleware.
SignToken singns a JWT token with the given claims using the provided private key with the signingMethod.

# Constants

JWTSecurityType is the name of the JWT security type.

# Variables

AvailableSigningMethods holds the supported signing methods.

# Type aliases

SigningMethods is a map mapping from a signing method name to an actual SigningMethod.