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 static 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
- Other Frameworks
Important
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, SupertokensConfig
from 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 initialised in them. - If this microservice does not initialise any other recipe, 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
- Other Frameworks
Important
import jwt from "supertokens-node/recipe/jwt"
async function createJWT(payload: any) {
let jwtResponse = await jwt.createJWT({
...payload,
source: "microservice"
});
if (jwtResponse.status === "OK") {
// Send JWT as Authorization header to M2
return jwtResponse.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, 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 asyncio
from 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_jwt
from 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 M2
else:
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.
note
By default, the JWT is signed by a static key, not subject to the key rotation normally applied to access tokens. You can pass false
as the third argument to the createJWT
function to use use the dynamic keys.
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; charset=utf-8' \
--data-raw '{
"payload": {
"source": "microservice",
...
},
"useStaticSigningKey": true,
"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; charset=utf-8' \
--data-raw '{
"request": "payload"
}'