Creating a JWT
#
When to create a JWT?The first step is to create a JWT from the microservice that will be sending the request (let's call this microservice M1
). This JWT will be verified by other microservices when M1
sends them a request. Since this JWT remains statis per microservice, the best time to create this is on process starts - i.e. when M1
starts.
#
What to add in the JWT?The JWT can contain any information you like, but at a minimum, it needs to contain information proving that it is a microservice that is allowed to query other microservices in your infrastructure. This is needed since you may issue a JWT to an end user as well, and they should not be able to query any microservice directly.
We can add the following claim in the JWT to "mark" the JWT as one that is meant for microservice auth only:
{..., "source": "microservice", ...}
In the receiving microservice (M2
), we can then verify the JWT and check that this claim is present before serving the request.
#
Code for creating a JWT- Using SuperTokens SDK
- Without SDK
First, we need to initialise the JWT
recipe in the supertokens.init
function call:
- NodeJS
- GoLang
- Python
import supertokens from "supertokens-node"import jwt from "supertokens-node/recipe/jwt"
supertokens.init({ appInfo: { apiDomain: "...", appName: "...", websiteDomain: "..." }, supertokens: { connectionURI: "...", // location of the core apiKey: "..." // provide the core's API key if configured }, recipeList: [ jwt.init() ]})
import ( "github.com/supertokens/supertokens-golang/recipe/jwt" "github.com/supertokens/supertokens-golang/supertokens")
func main() { supertokens.Init(supertokens.TypeInput{ AppInfo: supertokens.AppInfo{ AppName: "...", WebsiteDomain: "...", APIDomain: "...", }, Supertokens: &supertokens.ConnectionInfo{ ConnectionURI: "...", // location of the core APIKey: "...", // provide the core's API key if configured }, RecipeList: []supertokens.Recipe{ jwt.Init(nil), }, })}
from supertokens_python import init, InputAppInfo, SupertokensConfigfrom supertokens_python.recipe import jwt
init( app_info=InputAppInfo( app_name="...", api_domain="...", website_domain="...", ), supertokens_config=SupertokensConfig( connection_uri="...", # location of the core api_key="..." # provide the core's API key if configured ), framework='django', recipe_list=[ jwt.init(), ],)
important
- The value of
apiDomain
should be the domain part of the JWKS URL that will be used to verify the JWT (fromM2
). This should ideally be the domain of the microservice that has all the other SuperTokens' recipes initlised in them. - If this microservice does not initialise any other reicpe, the values of
appName
andwebsiteDomain
don't matter.
After this, you can use the JWT recipe to create your own JWT whenever required:
- NodeJS
- GoLang
- Python
import jwt from "supertokens-node/recipe/jwt"
async function createJWT(payload: any) { let jwtResposne = await jwt.createJWT({ ...payload, source: "microservice" }); if (jwtResposne.status === "OK") { // Send JWT as Authorization header to M2 return jwtResposne.jwt; } throw new Error("Unable to create JWT. Should never come here.")}
import ( "fmt"
"github.com/supertokens/supertokens-golang/recipe/jwt")
func main() { jwtResponse, err := jwt.CreateJWT(map[string]interface{}{ "source": "microservice", // ...additional payload }, nil) if err != nil { // handle error } jwtString := jwtResponse.OK.Jwt fmt.Println(jwtString) // Send JWT as Authorization header to M2}
- Asyncio
- Syncio
from supertokens_python.recipe.jwt import asynciofrom supertokens_python.recipe.jwt.interfaces import CreateJwtOkResult
async def create_jwt(): jwtResponse = await asyncio.create_jwt({ "source": "microservice", # ... extra payload })
if isinstance(jwtResponse, CreateJwtOkResult): _ = jwtResponse.jwt # Send JWT as Authorization header to M2 else: raise Exception("Unable to create JWT. Should never come here.")
from supertokens_python.recipe.jwt.syncio import create_jwtfrom supertokens_python.recipe.jwt.interfaces import CreateJwtOkResult
jwtResponse = create_jwt({ "source": "microservice", # ... extra payload})
if isinstance(jwtResponse, CreateJwtOkResult): jwtStr = jwtResponse.jwt # Send JWT as Authorization header to M2else: raise Exception("Unable to create JWT. Should never come here.")
note
By default, the lifetime of the JWT will be a 100 years. You can pass a second argument to the createJWT
function indicating a custom lifetime (in seconds) for the JWT.
You can send a HTTP
request to the core as follows:
curl --location --request POST '${connectionURI}/recipe/jwt' \--header 'rid: jwt' \--header 'api-key: ${APIKey}' \--header 'Content-Type: application/json' \--data-raw '{ "payload": { "source": "microservice", ... }, "algorithm": "RS256", "jwksDomain": "${apiDomain}", "validity": ${validityInSeconds}}'
- The value of
${connectionURI}
is the core's location ${APIKey}
is the API key to query the core. This is only needed if an API key is configured on the core.- The value of
${apiDomain}
is the domain on which the JWKs URLs will be served from ${validityInSeconds}
is the lifetime of the JWT
An example response is as follows:
{ "status": "OK", "jwt": "eyJraWQiOiI0YTE...rCFPcIRgzu_bChIIpFdA"}
#
What to do with the JWT?Once the JWT is created, you can store it in a (globally accessible) variable and access it when you want to talk to a microservice. You can add the JWT as an Authorization: Bearer
token like so:
curl --location --request POST 'https://microservice_location/path' \--header 'Authorization: Bearer eyJraWQiOiI0YTE...rCFPcIRgzu_bChIIpFdA' \--header 'Content-Type: application/json' \--data-raw '{ "request": "payload"}'